Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/cpp2util.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,15 @@
#endif


#if defined(_WIN32)
#define CPPFRONTAPI __declspec(dllexport)
#else
#define CPPFRONTAPI __attribute__ ((visibility ("default")))
#endif

#define CPP2_C_API extern "C" CPPFRONTAPI


namespace cpp2 {


Expand Down
6 changes: 1 addition & 5 deletions source/reflect.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@ class compiler_services_data;
// Reflection and meta
//===========================================================================

#ifndef CPPFRONT_LOAD_METAFUNCTION_IMPL_HEADER
#define CPPFRONT_LOAD_METAFUNCTION_IMPL_HEADER "reflect_load_metafunction_never.h"
#endif

#include CPPFRONT_LOAD_METAFUNCTION_IMPL_HEADER
#include "reflect_load_metafunction.h"

#include "parse.h"

Expand Down
6 changes: 1 addition & 5 deletions source/reflect.h2
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@
// Reflection and meta
//===========================================================================

#ifndef CPPFRONT_LOAD_METAFUNCTION_IMPL_HEADER
#define CPPFRONT_LOAD_METAFUNCTION_IMPL_HEADER "reflect_load_metafunction_never.h"
#endif

#include CPPFRONT_LOAD_METAFUNCTION_IMPL_HEADER
#include "reflect_load_metafunction.h"

#include "parse.h"

Expand Down
153 changes: 153 additions & 0 deletions source/reflect_load_metafunction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#include <cstdlib>
#include <functional>
#include <string>
#include <string_view>
#include <utility>
#include "cpp2util.h"

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#else
#include <dlfcn.h>
#endif // _WIN32

namespace cpp2::meta {

class dll
{
public:
dll(std::string const& path)
{
#ifdef _WIN32
handle_ = static_cast<void*>(LoadLibraryA(path.c_str()));
#else
handle_ = static_cast<void*>(dlopen(path.c_str(), RTLD_NOW|RTLD_LOCAL));
#endif // _WIN32
// TODO(Herb/Johel): Decide a user-friendlier way of reporting this.
if(handle_ == nullptr)
std::cerr << "Error while loading DLL '" << path << "': " << get_last_error_() << '\n';
}

~dll() noexcept
{
if(handle_ != nullptr)
{
#ifdef _WIN32
FreeLibrary(static_cast<HMODULE>(handle_));
#else
dlclose(handle_);
#endif // _WIN32
}
}

// Uncopyable
dll(dll&) = delete;
dll(dll const&) = delete;
auto operator=(dll const&) -> dll& = delete;
// Unmovable
dll(dll&& from) = delete;
auto operator=(dll&& from) -> dll& = delete;

auto is_open() noexcept -> bool { return handle_ != nullptr; }

template<typename T>
auto get_alias(std::string const& name) noexcept -> T*
{
#ifdef _WIN32
auto symbol = GetProcAddress(static_cast<HMODULE>(handle_), name.c_str());
#else
auto symbol = dlsym(handle_, name.c_str());
if(symbol == nullptr)
{
// Some platforms export with additional underscore, so try that.
auto const us_name = "_" + name;
symbol = dlsym(handle_, us_name.c_str());
}
#endif // _WIN32
// TODO(Herb/Johel): Decide a user-friendlier way of reporting this.
if(symbol == nullptr)
std::cerr << "Error while looking up DLL symbol '" << name << "': " << get_last_error_() << '\n';
return function_cast_<T*>(symbol);
}
private:
void* handle_{nullptr};

template<typename T>
static auto function_cast_(auto ptr) noexcept -> T {
using generic_function_ptr = void (*)(void);
return reinterpret_cast<T>(reinterpret_cast<generic_function_ptr>(ptr));
}

static auto get_last_error_() noexcept -> std::string {
#ifdef _WIN32
DWORD errorMessageID = GetLastError();
if(errorMessageID == 0)
return {}; // No error message has been recorded
LPSTR messageBuffer = nullptr;
auto size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
errorMessageID,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
&messageBuffer,
0,
nullptr
);
std::string message(messageBuffer, static_cast<size_t>(size));
LocalFree(messageBuffer);
return message;
#else
return std::string{dlerror()};
#endif // _WIN32
}

};


// Load metafunction by opening DLL with OS APIs
//
// The ':'-separated library paths
// are read from the environment variable
// 'CPPFRONT_METAFUNCTION_LIBRARIES'
auto load_metafunction(std::string const& name) -> std::function<void(type_declaration&)>
{
// FIXME: On Windows, using this approach with the system apis not set to utf8, will
// break if a metafunction library contains unicode codepoints in its name, a proper
// way to handle this would be to use _wgetenv and use wchar_t strings for the dll opening
// function
auto cpp1_libraries_cstr = std::getenv("CPPFRONT_METAFUNCTION_LIBRARIES");
if (!cpp1_libraries_cstr) {
return {};
}

auto cpp1_libraries = std::string_view{cpp1_libraries_cstr};
auto cpp1_name = "cpp2_metafunction_" + name;

while (!cpp1_libraries.empty())
{
auto colon = cpp1_libraries.find(':');
auto lib_path = cpp1_libraries.substr(0, colon);
cpp1_libraries.remove_prefix(lib_path.size() + unsigned(colon != lib_path.npos));

auto lib = std::make_shared<dll>(std::string(lib_path));
if(!lib->is_open())
continue;

if (auto* fun = lib->get_alias<void(void*)>(cpp1_name); fun != nullptr)
{
return [
fun = fun,
lib = lib
](type_declaration& t)
{
fun(static_cast<void*>(&t));
};
}
}

return {};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This brings another point up: How to deal with the error logging? (I put some TODOs for now), I immediately identified the error because I had some debug statements to print whatever dlerror gave me, I think there is value in providing that to end-users but I wouldn't know how to integrate that with the rest of cppfront.

This is an important QoI issue that we should discuss with Herb.
I already have 3 different modules authoring metafunctions.
Just imagine how much worse this error message could get
if it listed all metafunctions authored in which libraries (https://cpp2.godbolt.org/z/vqMYYKv5e):

t: @ugh type = { }
main: () = { }
main.cpp2...
main.cpp2(2,1): error: unrecognized metafunction name: ugh
main.cpp2(2,1): error: (temporary alpha limitation) currently the supported names are: interface, polymorphic_base, ordered, weakly_ordered, partially_ordered, copyable, basic_value, value, weakly_ordered_value, partially_ordered_value, struct, enum, flag_enum, union, print

}

}
48 changes: 0 additions & 48 deletions source/reflect_load_metafunction_boost_dll.h

This file was deleted.

26 changes: 0 additions & 26 deletions source/reflect_load_metafunction_never.h

This file was deleted.

7 changes: 4 additions & 3 deletions source/to_cpp1.h
Original file line number Diff line number Diff line change
Expand Up @@ -6448,11 +6448,12 @@ class cppfront
{
auto identifier = print_to_string(*n.identifier);
printer.print_extra(
"\nextern \"C\" constexpr auto cpp2_metafunction_"
"\nCPP2_C_API void cpp2_metafunction_"
+ identifier
+ " = &"
+ "(void* t) { "
+ identifier
+ ";"
+ "(*static_cast<cpp2::meta::type_declaration*>(t));"
+ " }"
);
}
}
Expand Down