Skip to content

Commit

Permalink
Merge pull request #1 from DyXel/program-defined-metafunctions-no-boo…
Browse files Browse the repository at this point in the history
…st-dll

refactor(reflect): remove Boost.DLL dependency by doing DLL handling ourselves
  • Loading branch information
JohelEGP authored Dec 27, 2023
2 parents b4a7727 + 066a051 commit c6217f9
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 87 deletions.
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 {};
}

}
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

0 comments on commit c6217f9

Please sign in to comment.