Skip to content
Draft
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
298 changes: 298 additions & 0 deletions include/CppInterOp/CppInterOpDispatch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
//--------------------------------------------------------------------*- C++ -*-
// CppInterOp Dispatch Mechanism
// author: Aaron Jomy <[email protected]>
//===----------------------------------------------------------------------===//
//
// This defines the mechanism which enables dispatching of the CppInterOp API
// without linking to it, preventing any LLVM or Clang symbols from being leaked
// into the client application.
//
//===----------------------------------------------------------------------===//
#ifndef CPPINTEROP_CPPINTEROPDISPATCH_H
#define CPPINTEROP_CPPINTEROPDISPATCH_H

#include <cstddef>
#include <cstdint>
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: included header cstddef is not used directly [misc-include-cleaner]

Suggested change
#include <cstdint>
#include <cstdint>

#include <set>
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: included header cstdint is not used directly [misc-include-cleaner]

Suggested change
#include <set>
#include <set>

#include <string>
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: included header set is not used directly [misc-include-cleaner]

Suggested change
#include <string>
#include <string>

#include <vector>
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: included header string is not used directly [misc-include-cleaner]

Suggested change
#include <vector>
#include <vector>


Copy link
Contributor

Choose a reason for hiding this comment

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

warning: included header vector is not used directly [misc-include-cleaner]

Suggested change

#include <cstdlib>
#include <dlfcn.h>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

needs something like:

#ifdef _WIN32
#include <io.h>
#include <Psapi.h>
#define RTLD_DEFAULT ((void *)::GetModuleHandle(NULL))
//#define dlsym(library, function_name) ::GetProcAddress((HMODULE)library, function_name)
#define dlopen(library_name, flags) ::LoadLibrary(library_name)
#define dlclose(library) ::FreeLibrary((HMODULE)library)
#endif

#include <iostream>
#include <mutex>

#include <CppInterOp/CppInterOp.h>

// Configured by CMake, can be overridden by defining before including this
// header
#ifndef CPPINTEROP_LIBRARY_PATH
#define CPPINTEROP_LIBRARY_PATH "@CPPINTEROP_LIBRARY_PATH@"
Copy link
Contributor

Choose a reason for hiding this comment

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

That does not seem correct. The library can be installed or relocated.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is not the build location, but a new flag. The path can be overridden to the installed/relocated location either with CMake or by setting an env var. The header has no way of knowing that...

Copy link
Contributor

Choose a reason for hiding this comment

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

That is still a problem, however, I do not understand why you need to define it if it is provided from the outside...

#endif

using __CPP_FUNC = void (*)();
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: declaration uses identifier '__CPP_FUNC', which is a reserved identifier [bugprone-reserved-identifier]

Suggested change
using __CPP_FUNC = void (*)();
using CPP_FUNC = void (*)();

include/CppInterOp/CppInterOpDispatch.h:24:

-   __CPP_FUNC address;
+   CPP_FUNC address;

Copy link
Contributor

Choose a reason for hiding this comment

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

warning: declaration uses identifier '__CPP_FUNC', which is a reserved identifier [bugprone-reserved-identifier]

Suggested change
using __CPP_FUNC = void (*)();
using CPP_FUNC = void (*)();


///\param[in] procname - the name of the FunctionEntry in the symbol lookup
/// table.
///
///\returns the function address of the requested API, or nullptr if not found
extern "C" CPPINTEROP_API void (
*CppGetProcAddress(const unsigned char* procname))(void);

#define EXTERN_CPP_FUNC_SIMPLE(func_name) \
extern CppAPIType::func_name func_name;

#define EXTERN_CPP_FUNC_OVERLOADED(func_name, signature) \
extern CppAPIType::func_name func_name;

#define LOAD_CPP_FUNCTION_SIMPLE(func_name) \
func_name = \
reinterpret_cast<CppAPIType::func_name>(dlGetProcAddress(#func_name));

#define LOAD_CPP_FUNCTION_OVERLOADED(func_name, signature) \
func_name = \
reinterpret_cast<CppAPIType::func_name>(dlGetProcAddress(#func_name));

#define DECLARE_CPP_NULL_SIMPLE(func_name) \
CppAPIType::func_name func_name = nullptr;

#define DECLARE_CPP_NULL_OVERLOADED(func_name, signature) \
CppAPIType::func_name func_name = nullptr;

// macro that allows declaration and loading of all CppInterOp API functions in
// a consistent way. This is used as our dispatched API list, along with the
// name-address pair table
#define FOR_EACH_CPP_FUNCTION_SIMPLE(DO) \
DO(CreateInterpreter) \
Copy link
Contributor

Choose a reason for hiding this comment

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

This can take a pair <CreateInterpreter, Type> and in this case you can write the using clauses automatically. We can also use clang-tablegen to generate these for every function in the namespace Cpp.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ping on this. We want to be writing the using clauses automatically.

Copy link
Collaborator Author

@aaronj0 aaronj0 Dec 15, 2025

Choose a reason for hiding this comment

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

Looks like this is indeed possible.

#define EXTERN_CPP_FUNC_SIMPLE(func_name) \
    extern CppAPIType::func_name func_name;

#define EXTERN_CPP_FUNC_OVERLOADED(func_name, signature) \
    extern CppAPIType::func_name func_name;

#define LOAD_CPP_FUNCTION_SIMPLE(func_name) \
    func_name = reinterpret_cast<CppAPIType::func_name>(dlGetProcAddress(#func_name));

#define LOAD_CPP_FUNCTION_OVERLOADED(func_name, signature) \
    func_name = reinterpret_cast<CppAPIType::func_name>(dlGetProcAddress(#func_name));

#define DECLARE_CPP_NULL_SIMPLE(func_name) \
    CppAPIType::func_name func_name = nullptr;

#define DECLARE_CPP_NULL_OVERLOADED(func_name, signature) \
    CppAPIType::func_name func_name = nullptr;

// macro that allows declaration and loading of all CppInterOp API functions in
// a consistent way. This is used as our dispatched API list, along with the
// name-address pair table
#define FOR_EACH_CPP_FUNCTION_SIMPLE(DO)                                       \
  DO(CreateInterpreter)                                                        \
  DO(GetInterpreter)                                                           \
// ...  rest of non overloaded API
  DO(EnableDebugOutput)

#define FOR_EACH_CPP_FUNCTION_OVERLOADED(DO)                                   \
  DO(MakeFunctionCallable, Cpp::JitCall (*)(Cpp::TCppConstFunction_t))        \
  DO(GetFunctionAddress, Cpp::TCppFuncAddr_t (*)(Cpp::TCppFunction_t))

#define EXTRACT_NAME_OVERLOADED(name, sig) name

#define FOR_EACH_CPP_FUNCTION(DO)                                              \
  FOR_EACH_CPP_FUNCTION_SIMPLE(DO)                                             \
  FOR_EACH_CPP_FUNCTION_OVERLOADED(EXTRACT_NAME_OVERLOADED)

#define DECLARE_TYPE_SIMPLE(func_name) \
    using func_name = decltype(&Cpp::func_name);

#define DECLARE_TYPE_OVERLOADED(func_name, signature) \
    using func_name = signature;
namespace CppDispatch {
// Forward all type aliases
using TCppIndex_t = ::Cpp::TCppIndex_t;
...

using JitCall = ::Cpp::JitCall;
} // end namespace CppDispatch

namespace CppAPIType {
    FOR_EACH_CPP_FUNCTION_SIMPLE(DECLARE_TYPE_SIMPLE)
    FOR_EACH_CPP_FUNCTION_OVERLOADED(DECLARE_TYPE_OVERLOADED)
} // end namespace CppAPIType

#undef DECLARE_TYPE_SIMPLE
#undef DECLARE_TYPE_OVERLOADED

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Here FOR_EACH_CPP_FUNCTION_SIMPLE and FOR_EACH_CPP_FUNCTION_OVERLOADED serve as the only ground truth for where we keep the API list. If you have an overloaded API, you put it in the overloaded version as <API name, function pointer type>

Copy link
Collaborator Author

@aaronj0 aaronj0 Dec 15, 2025

Choose a reason for hiding this comment

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

Of course all usages of the previous macro split up:

- FOR_EACH_CPP_FUNCTION(LOAD_CPP_FUNCTION)
+ FOR_EACH_CPP_FUNCTION_SIMPLE(LOAD_CPP_FUNCTION_SIMPLE)
+ FOR_EACH_CPP_FUNCTION_OVERLOADED(LOAD_CPP_FUNCTION_OVERLOADED)

- FOR_EACH_CPP_FUNCTION(EXTERN_CPP_FUNC)
+ FOR_EACH_CPP_FUNCTION_SIMPLE(EXTERN_CPP_FUNC_SIMPLE)
+ FOR_EACH_CPP_FUNCTION_OVERLOADED(EXTERN_CPP_FUNC_OVERLOADED)

But that keeps the whole thing automatic

Copy link
Contributor

@vgvassilev vgvassilev Dec 17, 2025

Choose a reason for hiding this comment

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

Probably you don't need such a complexity but a X-macro that expands. You can also take the type of the function using decltype and probably handle the overloads there, too. It seems that you already use decltype but I do not understand why this can be a simpler macro setup..

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Probably you don't need such a complexity but a X-macro that expands.

Can you give me a clearer idea on that?

You can also take the type of the function using decltype and probably handle the overloads there, too.

Well, yes I can make this simpler if I write the signatures for all API and take the decltype of the function obtained on static_cast. I've split up the macros to avoid static_cast on non-overloaded methods, as that seems like an unnecessary operation...

Copy link
Contributor

Choose a reason for hiding this comment

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

DO(GetInterpreter) \
DO(Process) \
DO(GetResourceDir) \
DO(AddIncludePath) \
DO(LoadLibrary) \
DO(Declare) \
DO(DeleteInterpreter) \
DO(IsNamespace) \
DO(ObjToString) \
DO(GetQualifiedCompleteName) \
DO(GetValueKind) \
DO(GetNonReferenceType) \
DO(IsEnumType) \
DO(GetIntegerTypeFromEnumType) \
DO(GetReferencedType) \
DO(IsPointerType) \
DO(GetPointeeType) \
DO(GetPointerType) \
DO(IsReferenceType) \
DO(GetTypeAsString) \
DO(GetCanonicalType) \
DO(HasTypeQualifier) \
DO(RemoveTypeQualifier) \
DO(GetUnderlyingType) \
DO(IsRecordType) \
DO(IsFunctionPointerType) \
DO(GetVariableType) \
DO(GetNamed) \
DO(GetScopeFromType) \
DO(GetClassTemplateInstantiationArgs) \
DO(IsClass) \
DO(GetType) \
DO(GetTypeFromScope) \
DO(GetComplexType) \
DO(GetIntegerTypeFromEnumScope) \
DO(GetUnderlyingScope) \
DO(GetScope) \
DO(GetGlobalScope) \
DO(GetScopeFromCompleteName) \
DO(InstantiateTemplate) \
DO(GetParentScope) \
DO(IsTemplate) \
DO(IsTemplateSpecialization) \
DO(IsTypedefed) \
DO(IsClassPolymorphic) \
DO(Demangle) \
DO(SizeOf) \
DO(GetSizeOfType) \
DO(IsBuiltin) \
DO(IsComplete) \
DO(Allocate) \
DO(Deallocate) \
DO(Construct) \
DO(Destruct) \
DO(IsAbstract) \
DO(IsEnumScope) \
DO(IsEnumConstant) \
DO(IsAggregate) \
DO(HasDefaultConstructor) \
DO(IsVariable) \
DO(GetAllCppNames) \
DO(GetUsingNamespaces) \
DO(GetCompleteName) \
DO(GetDestructor) \
DO(IsVirtualMethod) \
DO(GetNumBases) \
DO(GetName) \
DO(GetBaseClass) \
DO(IsSubclass) \
DO(GetOperator) \
DO(GetFunctionReturnType) \
DO(GetBaseClassOffset) \
DO(GetClassMethods) \
DO(GetFunctionsUsingName) \
DO(GetFunctionNumArgs) \
DO(GetFunctionRequiredArgs) \
DO(GetFunctionArgName) \
DO(GetFunctionArgType) \
DO(GetFunctionArgDefault) \
DO(IsConstMethod) \
DO(GetFunctionTemplatedDecls) \
DO(ExistsFunctionTemplate) \
DO(IsTemplatedFunction) \
DO(IsStaticMethod) \
DO(GetClassTemplatedMethods) \
DO(BestOverloadFunctionMatch) \
DO(GetOperatorFromSpelling) \
DO(IsFunctionDeleted) \
DO(IsPublicMethod) \
DO(IsProtectedMethod) \
DO(IsPrivateMethod) \
DO(IsConstructor) \
DO(IsDestructor) \
DO(GetDatamembers) \
DO(GetStaticDatamembers) \
DO(GetEnumConstantDatamembers) \
DO(LookupDatamember) \
DO(IsLambdaClass) \
DO(GetQualifiedName) \
DO(GetVariableOffset) \
DO(IsPublicVariable) \
DO(IsProtectedVariable) \
DO(IsPrivateVariable) \
DO(IsStaticVariable) \
DO(IsConstVariable) \
DO(GetDimensions) \
DO(GetEnumConstants) \
DO(GetEnumConstantType) \
DO(GetEnumConstantValue) \
DO(DumpScope) \
DO(AddSearchPath) \
DO(Evaluate) \
DO(IsDebugOutputEnabled) \
DO(EnableDebugOutput)

#define FOR_EACH_CPP_FUNCTION_OVERLOADED(DO) \
DO(MakeFunctionCallable, Cpp::JitCall (*)(Cpp::TCppConstFunction_t)) \
DO(GetFunctionAddress, Cpp::TCppFuncAddr_t (*)(Cpp::TCppFunction_t))

#define EXTRACT_NAME_OVERLOADED(name, sig) name

#define FOR_EACH_CPP_FUNCTION(DO) \
FOR_EACH_CPP_FUNCTION_SIMPLE(DO) \
FOR_EACH_CPP_FUNCTION_OVERLOADED(EXTRACT_NAME_OVERLOADED)

#define DECLARE_TYPE_SIMPLE(func_name) \
using func_name = decltype(&Cpp::func_name);
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: macro argument should be enclosed in parentheses [bugprone-macro-parentheses]

Suggested change
using func_name = decltype(&Cpp::func_name);
using (func_name) = decltype(&Cpp::func_name);

Copy link
Contributor

Choose a reason for hiding this comment

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

These are good suggestions..


#define DECLARE_TYPE_OVERLOADED(func_name, signature) \
using func_name = signature;
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: macro argument should be enclosed in parentheses [bugprone-macro-parentheses]

Suggested change
using func_name = signature;
using (func_name) = signature;

namespace CppDispatch {
// Forward all type aliases
using TCppIndex_t = ::Cpp::TCppIndex_t;
using TCppScope_t = ::Cpp::TCppScope_t;
using TCppConstScope_t = ::Cpp::TCppConstScope_t;
using TCppType_t = ::Cpp::TCppType_t;
using TCppFunction_t = ::Cpp::TCppFunction_t;
using TCppConstFunction_t = ::Cpp::TCppConstFunction_t;
using TCppFuncAddr_t = ::Cpp::TCppFuncAddr_t;
using TInterp_t = ::Cpp::TInterp_t;
using TCppObject_t = ::Cpp::TCppObject_t;

using Operator = ::Cpp::Operator;
using OperatorArity = ::Cpp::OperatorArity;
using QualKind = ::Cpp::QualKind;
using TemplateArgInfo = ::Cpp::TemplateArgInfo;
using ValueKind = ::Cpp::ValueKind;

using JitCall = ::Cpp::JitCall;
} // end namespace CppDispatch

namespace CppAPIType {
FOR_EACH_CPP_FUNCTION_SIMPLE(DECLARE_TYPE_SIMPLE)
FOR_EACH_CPP_FUNCTION_OVERLOADED(DECLARE_TYPE_OVERLOADED)
} // end namespace CppAPIType

#undef DECLARE_TYPE_SIMPLE
#undef DECLARE_TYPE_OVERLOADED

// TODO: implement overload that takes an existing opened DL handle
inline void* dlGetProcAddress(const char* name,
const char* customLibPath = nullptr) {
if (!name)
return nullptr;

Check warning on line 230 in include/CppInterOp/CppInterOpDispatch.h

View check run for this annotation

Codecov / codecov/patch

include/CppInterOp/CppInterOpDispatch.h#L230

Added line #L230 was not covered by tests

static std::once_flag loaded;
static void* handle = nullptr;
static void* (*getCppProcAddress)(const char*) = nullptr;

std::call_once(loaded, [customLibPath]() {
// priority order: 1) custom path argument, or CPPINTEROP_LIBRARY_PATH via
// 2) cmake configured path 3) env vars
const char* libPath = customLibPath;
if (!libPath) {
libPath = std::getenv("CPPINTEROP_LIBRARY_PATH");

Check warning on line 241 in include/CppInterOp/CppInterOpDispatch.h

View check run for this annotation

Codecov / codecov/patch

include/CppInterOp/CppInterOpDispatch.h#L241

Added line #L241 was not covered by tests
}
if (!libPath || libPath[0] == '\0') {
libPath = CPPINTEROP_LIBRARY_PATH;

Check warning on line 244 in include/CppInterOp/CppInterOpDispatch.h

View check run for this annotation

Codecov / codecov/patch

include/CppInterOp/CppInterOpDispatch.h#L244

Added line #L244 was not covered by tests
}

handle = dlopen(libPath, RTLD_LOCAL | RTLD_NOW);
if (!handle) {
std::cerr << "[CppInterOp] Failed to load library from " << libPath
<< ": " << dlerror() << '\n';
return;

Check warning on line 251 in include/CppInterOp/CppInterOpDispatch.h

View check run for this annotation

Codecov / codecov/patch

include/CppInterOp/CppInterOpDispatch.h#L250-L251

Added lines #L250 - L251 were not covered by tests
}

getCppProcAddress = reinterpret_cast<void* (*)(const char*)>(
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]

    getCppProcAddress = reinterpret_cast<void* (*)(const char*)>(
                        ^

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
getCppProcAddress = reinterpret_cast<void* (*)(const char*)>(
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
getCppProcAddress = reinterpret_cast<void* (*)(const char*)>(

dlsym(handle, "CppGetProcAddress"));
if (!getCppProcAddress) {
std::cerr << "[CppInterOp] Failed to find CppGetProcAddress: "
<< dlerror() << '\n';
dlclose(handle);
handle = nullptr;

Check warning on line 260 in include/CppInterOp/CppInterOpDispatch.h

View check run for this annotation

Codecov / codecov/patch

include/CppInterOp/CppInterOpDispatch.h#L258-L260

Added lines #L258 - L260 were not covered by tests
}
});

return getCppProcAddress ? getCppProcAddress(name) : nullptr;
}
namespace CppDispatch {
FOR_EACH_CPP_FUNCTION_SIMPLE(EXTERN_CPP_FUNC_SIMPLE)
Copy link
Contributor

Choose a reason for hiding this comment

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

warning: variable 'AddIncludePath' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables]

FOR_EACH_CPP_FUNCTION_SIMPLE(EXTERN_CPP_FUNC_SIMPLE)
^
Additional context

include/CppInterOp/CppInterOpDispatch.h:69: expanded from macro 'FOR_EACH_CPP_FUNCTION_SIMPLE'

  DO(AddIncludePath)                                                           \
     ^

Copy link
Contributor

Choose a reason for hiding this comment

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

warning: variable 'AddSearchPath' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables]

FOR_EACH_CPP_FUNCTION_SIMPLE(EXTERN_CPP_FUNC_SIMPLE)
^
Additional context

include/CppInterOp/CppInterOpDispatch.h:176: expanded from macro 'FOR_EACH_CPP_FUNCTION_SIMPLE'

  DO(AddSearchPath)                                                            \
     ^

Copy link
Contributor

Choose a reason for hiding this comment

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

warning: variable 'Allocate' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables]

FOR_EACH_CPP_FUNCTION_SIMPLE(EXTERN_CPP_FUNC_SIMPLE)
^
Additional context

include/CppInterOp/CppInterOpDispatch.h:116: expanded from macro 'FOR_EACH_CPP_FUNCTION_SIMPLE'

  DO(Allocate)                                                                 \
     ^

FOR_EACH_CPP_FUNCTION_OVERLOADED(EXTERN_CPP_FUNC_OVERLOADED)

/// Initialize all CppInterOp API from the dynamically loaded library
/// (RTLD_LOCAL) \param[in] customLibPath Optional custom path to
/// libclangCppInterOp.so \returns true if initialization succeeded, false
/// otherwise
inline bool init_functions(const char* customLibPath = nullptr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

That should be a test case, right?

// trigger library loading if custom path provided
if (customLibPath) {
void* test = dlGetProcAddress("GetInterpreter", customLibPath);
if (!test) {
std::cerr << "[CppInterOp] Failed to initialize with custom path: "
<< customLibPath << '\n';
return false;
}
}

FOR_EACH_CPP_FUNCTION_SIMPLE(LOAD_CPP_FUNCTION_SIMPLE)
FOR_EACH_CPP_FUNCTION_OVERLOADED(LOAD_CPP_FUNCTION_OVERLOADED)

// test to verify that critical (and consequently all) functions loaded
if (!GetInterpreter || !CreateInterpreter) {
std::cerr << "[CppInterOp] Failed to load critical functions" << std::endl;
return false;
}

return true;
}
} // namespace CppDispatch

#endif // CPPINTEROP_CPPINTEROPDISPATCH_H
1 change: 1 addition & 0 deletions lib/CppInterOp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ endif()
add_llvm_library(clangCppInterOp
DISABLE_LLVM_LINK_LLVM_DYLIB
CppInterOp.cpp
CppInterOpDispatch.cpp
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should name the infrastructure we built with something closer to what it does, eg. DyldDispatch.

Copy link
Contributor

Choose a reason for hiding this comment

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

ping.

Copy link
Collaborator Author

@aaronj0 aaronj0 Dec 17, 2025

Choose a reason for hiding this comment

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

#include <CppInterOp/DyldDispatch.h>?

CXCppInterOp.cpp
${DLM}
LINK_LIBS
Expand Down
33 changes: 33 additions & 0 deletions lib/CppInterOp/CppInterOpDispatch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//------------------------------------------------------------------------------
// CppInterOp Dispatch Implementation
// author: Aaron Jomy <[email protected]>
//------------------------------------------------------------------------------

#include <CppInterOp/CppInterOp.h>
#include <CppInterOp/CppInterOpDispatch.h>

#include <unordered_map>

// Macro for simple functions (direct cast)
#define MAP_ENTRY_SIMPLE(func_name) {#func_name, (__CPP_FUNC)Cpp::func_name},

// Macro for overloaded functions (needs static_cast with signature)
#define MAP_ENTRY_OVERLOADED(func_name, signature) \
{#func_name, (__CPP_FUNC) static_cast<signature>(&Cpp::func_name)},

static const std::unordered_map<std::string_view, __CPP_FUNC>
INTEROP_FUNCTIONS = {
FOR_EACH_CPP_FUNCTION_SIMPLE(MAP_ENTRY_SIMPLE)
FOR_EACH_CPP_FUNCTION_OVERLOADED(MAP_ENTRY_OVERLOADED)};

#undef MAP_ENTRY_SIMPLE
#undef MAP_ENTRY_OVERLOADED

static inline __CPP_FUNC _cppinterop_get_proc_address(const char* funcName) {
auto it = INTEROP_FUNCTIONS.find(funcName);
return (it != INTEROP_FUNCTIONS.end()) ? it->second : nullptr;
}

void (*CppGetProcAddress(const unsigned char* procName))(void) {
return _cppinterop_get_proc_address(reinterpret_cast<const char*>(procName));
}
8 changes: 8 additions & 0 deletions unittests/CppInterOp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ else()
set(EXTRA_PATH_TEST_BINARIES /CppInterOpTests/unittests/bin/$<CONFIG>/)
endif()

# Add the DispatchAPITest only when building shared libraries
if (BUILD_SHARED_LIBS)
set_source_files_properties(DispatchAPITest.cpp PROPERTIES COMPILE_DEFINITIONS
"CPPINTEROP_LIB_DIR=\"${CMAKE_BINARY_DIR}/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}\""
Copy link
Contributor

Choose a reason for hiding this comment

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

Please write a test and try to run that test against installed CppInterOp with the build folder deleted.

)
list(APPEND EXTRA_TEST_SOURCE_FILES DispatchAPITest.cpp)
endif()

add_cppinterop_unittest(CppInterOpTests
EnumReflectionTest.cpp
FunctionReflectionTest.cpp
Expand Down
Loading
Loading