CppInterOp
C++ Language Interoperability Layer
Loading...
Searching...
No Matches
Dispatch.h
Go to the documentation of this file.
1//===--- Dispatch.h - CppInterOp's API Dispatch Mechanism ---*- C++ -*-===//
2//
3// Part of the compiler-research project, under the Apache License v2.0 with
4// LLVM Exceptions.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Defines the mechanism which enables dispatching of the CppInterOp API
10// without linking, preventing any LLVM or Clang symbols from being leaked
11// into the client application. This is achieved using a symbol-address table
12// and an address lookup through a C symbol allowing clients to dlopen
13// CppInterOp with RTLD_LOCAL, and automatically assign the API during runtime.
14//
15// The function list is generated by cppinterop-tblgen from CppInterOp.td
16// into CppInterOpAPI.inc, which is included multiple times with different
17// macro definitions (X-macro pattern).
18//
19//===----------------------------------------------------------------------===//
20
21#ifndef CPPINTEROP_DISPATCH_H
22#define CPPINTEROP_DISPATCH_H
23
24#ifdef CPPINTEROP_CPPINTEROP_H
25#error "CppInterOp.h and Dispatch.h are mutually exclusive — include only one."
26#endif
27
28#ifdef _WIN32
29#include <windows.h>
30#undef LoadLibrary
31#else
32#include <dlfcn.h>
33#endif
34
35#include "CppInterOp/Box.h"
37
38#include <cstdlib>
39#include <iostream>
40#include <memory>
41#include <mutex>
42#include <set>
43
44using CppFnPtrTy = void (*)();
45///\param[in] procname - the name of the FunctionEntry in the symbol lookup
46/// table.
47///
48///\returns the function address of the requested API, or nullptr if not found
49extern "C" CPPINTEROP_API CppFnPtrTy CppGetProcAddress(const char* procname);
50
51// TODO: implement overload that takes an existing opened DL handle
52inline void* dlGetProcAddress(const char* name,
53 const char* customLibPath = nullptr) {
54 static auto init = std::make_unique<std::once_flag>();
55 static void* (*getProc)(const char*);
56
57 if (!name) {
58 init = std::make_unique<std::once_flag>();
59 getProc = nullptr;
60 return nullptr;
61 }
62
63 std::call_once(*init, [customLibPath]() {
64 const char* path =
65 customLibPath ? customLibPath : std::getenv("CPPINTEROP_LIBRARY_PATH");
66 if (!path)
67 return;
68
69#ifdef _WIN32
70 HMODULE h = LoadLibraryA(path);
71 if (h) {
72 getProc = reinterpret_cast<void* (*)(const char*)>(
73 GetProcAddress(h, "CppGetProcAddress"));
74 if (!getProc)
75 FreeLibrary(h);
76 } else {
77 std::cerr << "[CppInterOp Dispatch] error code=" << GetLastError()
78 << "\n";
79 }
80#else
81 void* handle = dlopen(path, RTLD_LOCAL | RTLD_NOW);
82 if (handle) {
83 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
84 getProc = reinterpret_cast<void* (*)(const char*)>(
85 dlsym(handle, "CppGetProcAddress"));
86 if (!getProc)
87 dlclose(handle);
88 } else {
89 std::cerr << "[CppInterOp Dispatch] " << dlerror() << "\n";
90 }
91#endif
92 });
93
94 if (!getProc) {
95 init = std::make_unique<std::once_flag>();
96 getProc = nullptr;
97 return nullptr;
98 }
99
100 return getProc(name);
101}
102
103// Raw function pointers populated by LoadDispatchAPI. No default arguments.
104// Kept in a separate namespace so the dispatch-table state doesn't pollute
105// the public Cpp:: surface.
107using namespace Cpp;
108// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
109#define CPPINTEROP_API_FUNC(DN, CN, Ret, DeclArgs, CallArgs, RawTypes) \
110 extern Ret(*DN) RawTypes;
111#include "CppInterOp/CppInterOpAPI.inc"
112// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
113} // namespace CppInternal::DispatchRaw
114
115// Inline wrappers go directly in namespace Cpp so that consumer source code
116// (Cpp::IsAbstract, Cpp::GetNamed, ...) compiles identically against either
117// CppInterOp.h (direct linking, declarations only) or Dispatch.h (inline
118// bodies calling through the dispatch table). The two headers are mutually
119// exclusive (#error guard at top) so only one body of Cpp::* exists per TU.
120// CN (CppName) may differ from DN (DispatchName) for overloaded functions,
121// e.g. CN=GetFunctionAddress, DN=GetFunctionAddress_fn.
122namespace Cpp {
123
124#define CPPINTEROP_API_FUNC(DN, CN, Ret, DeclArgs, CallArgs, RawTypes) \
125 inline Ret CN DeclArgs { return ::CppInternal::DispatchRaw::DN CallArgs; }
126#include "CppInterOp/CppInterOpAPI.inc"
127
128/// Initialize all CppInterOp API from the dynamically loaded library.
129inline bool LoadDispatchAPI(const char* customLibPath = nullptr) {
130#ifndef NDEBUG
131 std::cout << "[CppInterOp Dispatch] Loading CppInterOp API from "
132 << (customLibPath ? customLibPath : "default library path") << '\n';
133#endif
134 if (customLibPath) {
135 void* test = dlGetProcAddress("GetInterpreter", customLibPath);
136 if (!test) {
137 std::cerr << "[CppInterOp Dispatch] Failed to load API from: "
138 << customLibPath << '\n';
139 return false;
140 }
141 }
142
143 // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
144#define CPPINTEROP_API_FUNC(DN, CN, Ret, DeclArgs, CallArgs, RawTypes) \
145 ::CppInternal::DispatchRaw::DN = \
146 reinterpret_cast<Ret(*) RawTypes>(dlGetProcAddress(#DN));
147#include "CppInterOp/CppInterOpAPI.inc"
148 // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
149
152 std::cerr << "[CppInterOp Dispatch] Failed to load critical functions\n";
153 return false;
154 }
155 return true;
156}
157
158inline void UnloadDispatchAPI() {
159#define CPPINTEROP_API_FUNC(DN, CN, Ret, DeclArgs, CallArgs, RawTypes) \
160 ::CppInternal::DispatchRaw::DN = nullptr;
161#include "CppInterOp/CppInterOpAPI.inc"
162 dlGetProcAddress(nullptr, nullptr);
163}
164} // namespace Cpp
165
166#endif // CPPINTEROP_DISPATCH_H
#define CPPINTEROP_API
void(*)() CppFnPtrTy
Definition Dispatch.h:44
void * dlGetProcAddress(const char *name, const char *customLibPath=nullptr)
Definition Dispatch.h:52
CppFnPtrTy CppGetProcAddress(const char *procname)
Definition Box.h:70
InterpRef CreateInterpreter(const std::vector< const char * > &Args, const std::vector< const char * > &GpuArgs)
void UnloadDispatchAPI()
Definition Dispatch.h:158
bool LoadDispatchAPI(const char *customLibPath=nullptr)
Initialize all CppInterOp API from the dynamically loaded library.
Definition Dispatch.h:129
InterpRef GetInterpreter()