[c-api] Generate C wrappers from TableGen instead of hand-writing them. - #921
Conversation
fe01513 to
f2d0aa4
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #921 +/- ##
==========================================
+ Coverage 83.02% 85.89% +2.87%
==========================================
Files 15 15
Lines 5079 4971 -108
==========================================
+ Hits 4217 4270 +53
+ Misses 862 701 -161
... and 4 files with indirect coverage changes
🚀 New features to boost your workflow:
|
fb435c4 to
d7a9760
Compare
|
|
||
| // C-compatible headers — usable from both C and C++. | ||
| #include <stdbool.h> | ||
| #include <stddef.h> |
There was a problem hiding this comment.
warning: including 'stdbool.h' has no effect in C++; consider removing it [modernize-deprecated-headers]
| #include <stddef.h> | |
| . | |
| // C-compatible headers — usable from both C and C++. | ||
| #include <stdbool.h> | ||
| #include <stddef.h> | ||
| #include <stdint.h> |
There was a problem hiding this comment.
warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead [modernize-deprecated-headers]
| #include <stdint.h> | |
| > | |
| ><cstddef> |
| #include <stddef.h> | ||
| #include <stdint.h> | ||
|
|
||
| #ifdef __cplusplus |
There was a problem hiding this comment.
warning: inclusion of deprecated C++ header 'stdint.h'; consider using 'cstdint' instead [modernize-deprecated-headers]
| #ifdef __cplusplus | |
| > | |
| ><cstdint> |
| #include <cstddef> | ||
| #include <cstdint> | ||
| #include <set> | ||
| #include <string> |
There was a problem hiding this comment.
warning: included header set is not used directly [misc-include-cleaner]
| #include <string> | |
| > | |
| #include <cstdint> | ||
| #include <set> | ||
| #include <string> | ||
| #include <vector> |
There was a problem hiding this comment.
warning: included header string is not used directly [misc-include-cleaner]
| #include <vector> | |
| > | |
| #include <string> | ||
| #include <vector> | ||
|
|
||
| namespace CppImpl { |
There was a problem hiding this comment.
warning: included header vector is not used directly [misc-include-cleaner]
| namespace CppImpl { | |
| > | |
| void** data; | ||
| size_t size; | ||
| } CppInterOpArray; | ||
|
|
There was a problem hiding this comment.
warning: use 'using' instead of 'typedef' [modernize-use-using]
| . | |
| using CppInterOpArray = struct CppInterOpArray { | |
| void** data; | |
| size_t size; | |
| } |
| char** data; | ||
| size_t size; | ||
| } CppInterOpStringArray; | ||
|
|
There was a problem hiding this comment.
warning: use 'using' instead of 'typedef' [modernize-use-using]
| . | |
| using CppInterOpStringArray = struct CppInterOpStringArray { | |
| char** data; | |
| size_t size; | |
| } |
| : m_Type(type), m_IntegralValue(integral_value) {} | ||
| #endif | ||
| } TemplateArgInfo; | ||
|
|
There was a problem hiding this comment.
warning: use 'using' instead of 'typedef' [modernize-use-using]
| . | |
| using TemplateArgInfo = struct TemplateArgInfo { | |
| void* m_Type; | |
| const char* m_IntegralValue; | |
| #ifdef __cplusplus | |
| TemplateArgInfo(void* type, const char* integral_value = nullptr) | |
| : m_Type(type), m_IntegralValue(integral_value) {} | |
| #endif | |
| } |
| return clang::cxscope::MakeCXScope(CXXRD->getDestructor(), getNewTU(S)); | ||
| // GetClassTemplatedMethods returns bool AND fills a vector out-param. | ||
| // The C wrapper drops the bool (caller checks arr.size > 0 instead). | ||
| CPPINTEROP_API Cpp::CppInterOpArray |
There was a problem hiding this comment.
warning: no header providing "CPPINTEROP_API" is directly included [misc-include-cleaner]
CPPINTEROP_API Cpp::CppInterOpArray
^5215f6b to
8ec7eba
Compare
| return clang::cxscope::MakeCXScope(CXXRD->getDestructor(), getNewTU(S)); | ||
| // GetClassTemplatedMethods returns bool AND fills a vector out-param. | ||
| // The C wrapper drops the bool (caller checks arr.size > 0 instead). | ||
| CPPINTEROP_API Cpp::CppInterOpArray |
There was a problem hiding this comment.
warning: no header providing "CppImpl::CppInterOpArray" is directly included [misc-include-cleaner]
lib/CppInterOp/CXCppInterOp.cpp:6:
- #include <cstdlib>
+ #include <CppInterOp/CppInterOpTypes.h>
+ #include <cstdlib>| // The C wrapper drops the bool (caller checks arr.size > 0 instead). | ||
| CPPINTEROP_API Cpp::CppInterOpArray | ||
| cppinterop_GetClassTemplatedMethods(const char* name, void* parent) { | ||
| std::vector<Cpp::TCppFunction_t> out; |
There was a problem hiding this comment.
warning: no header providing "CppImpl::TCppFunction_t" is directly included [misc-include-cleaner]
std::vector<Cpp::TCppFunction_t> out;
^| CPPINTEROP_API Cpp::CppInterOpArray | ||
| cppinterop_GetClassTemplatedMethods(const char* name, void* parent) { | ||
| std::vector<Cpp::TCppFunction_t> out; | ||
| Cpp::GetClassTemplatedMethods(std::string(name), parent, out); |
There was a problem hiding this comment.
warning: no header providing "std::string" is directly included [misc-include-cleaner]
lib/CppInterOp/CXCppInterOp.cpp:8:
- #include <vector>
+ #include <string>
+ #include <vector>| Cpp::GetClassTemplatedMethods(std::string(name), parent, out); | ||
| Cpp::CppInterOpArray arr = {nullptr, out.size()}; | ||
| if (arr.size) { | ||
| arr.data = static_cast<void**>(malloc(arr.size * sizeof(void*))); |
There was a problem hiding this comment.
warning: assigning newly created 'gsl::owner<>' to non-owner 'void **' [cppcoreguidelines-owning-memory]
arr.data = static_cast<void**>(malloc(arr.size * sizeof(void*)));
^| Cpp::GetClassTemplatedMethods(std::string(name), parent, out); | ||
| Cpp::CppInterOpArray arr = {nullptr, out.size()}; | ||
| if (arr.size) { | ||
| arr.data = static_cast<void**>(malloc(arr.size * sizeof(void*))); |
There was a problem hiding this comment.
warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
arr.data = static_cast<void**>(malloc(arr.size * sizeof(void*)));
^| Cpp::CppInterOpArray arr = {nullptr, out.size()}; | ||
| if (arr.size) { | ||
| arr.data = static_cast<void**>(malloc(arr.size * sizeof(void*))); | ||
| memcpy(arr.data, out.data(), arr.size * sizeof(void*)); |
There was a problem hiding this comment.
warning: multilevel pointer conversion from 'void **' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
memcpy(arr.data, out.data(), arr.size * sizeof(void*));
^| Cpp::CppInterOpArray arr = {nullptr, out.size()}; | ||
| if (arr.size) { | ||
| arr.data = static_cast<void**>(malloc(arr.size * sizeof(void*))); | ||
| memcpy(arr.data, out.data(), arr.size * sizeof(void*)); |
There was a problem hiding this comment.
warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
memcpy(arr.data, out.data(), arr.size * sizeof(void*));
^| #include <cstdlib> | ||
| #include <cstring> | ||
|
|
||
| using Cpp::CppInterOpArray; |
There was a problem hiding this comment.
warning: no header providing "CppImpl::CppInterOpArray" is directly included [misc-include-cleaner]
lib/CppInterOp/CXCppInterOpGenerated.cpp:5:
- #include <cstdlib>
+ #include <CppInterOp/CppInterOpTypes.h>
+ #include <cstdlib>| #include <cstring> | ||
|
|
||
| using Cpp::CppInterOpArray; | ||
| using Cpp::CppInterOpStringArray; |
There was a problem hiding this comment.
warning: no header providing "CppImpl::CppInterOpStringArray" is directly included [misc-include-cleaner]
using Cpp::CppInterOpStringArray;
^|
|
||
| using Cpp::CppInterOpArray; | ||
| using Cpp::CppInterOpStringArray; | ||
| using Cpp::TemplateArgInfo; |
There was a problem hiding this comment.
warning: no header providing "CppImpl::TemplateArgInfo" is directly included [misc-include-cleaner]
using Cpp::TemplateArgInfo;
^8ec7eba to
fc336e4
Compare
| #include "CppInterOp/CXCppInterOpDecl.inc" | ||
|
|
||
| // Hand-written wrappers not in the .inc file. | ||
| CPPINTEROP_API Cpp::CppInterOpArray |
There was a problem hiding this comment.
warning: no header providing "CPPINTEROP_API" is directly included [misc-include-cleaner]
CPPINTEROP_API Cpp::CppInterOpArray
^| #include "CppInterOp/CXCppInterOpDecl.inc" | ||
|
|
||
| // Hand-written wrappers not in the .inc file. | ||
| CPPINTEROP_API Cpp::CppInterOpArray |
There was a problem hiding this comment.
warning: no header providing "CppImpl::CppInterOpArray" is directly included [misc-include-cleaner]
unittests/CppInterOp/CAPITest.cpp:7:
- #include <cstdlib>
+ #include <CppInterOp/CppInterOpTypes.h>
+ #include <cstdlib>| TYPED_TEST(CppInterOpTest, CAPI_Construct) { | ||
| if (TypeParam::isOutOfProcess) | ||
| GTEST_SKIP() << "Test fails for OOP JIT builds"; | ||
| std::vector<const char*> args = {"-include", "new"}; |
There was a problem hiding this comment.
warning: no header providing "std::vector" is directly included [misc-include-cleaner]
unittests/CppInterOp/CAPITest.cpp:9:
+ #include <vector>| Cpp::Declare("namespace CAPIStrNs { enum E1 { P, Q }; enum E2 { R }; }"); | ||
|
|
||
| auto* ns = cppinterop_GetNamed("CAPIStrNs", nullptr); | ||
| Cpp::CppInterOpStringArray enums = cppinterop_GetEnums(ns); |
There was a problem hiding this comment.
warning: no header providing "CppImpl::CppInterOpStringArray" is directly included [misc-include-cleaner]
Cpp::CppInterOpStringArray enums = cppinterop_GetEnums(ns);
^| Cpp::CppInterOpStringArray enums = cppinterop_GetEnums(ns); | ||
| EXPECT_EQ(enums.size, 2U); | ||
| // Verify the enum names are present (order may vary). | ||
| bool foundE1 = false, foundE2 = false; |
There was a problem hiding this comment.
warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
| bool foundE1 = false, foundE2 = false; | |
| bool foundE1 = false; | |
| bool foundE2 = false; |
|
|
||
| TYPED_TEST(CppInterOpTest, CAPI_CreateInterpreterInParam) { | ||
| // Test the vector in-param pattern: CreateInterpreter(ptr, size, ptr, size). | ||
| const char* args[] = {"-std=c++17"}; |
There was a problem hiding this comment.
warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]
const char* args[] = {"-std=c++17"};
^| TYPED_TEST(CppInterOpTest, CAPI_CreateInterpreterInParam) { | ||
| // Test the vector in-param pattern: CreateInterpreter(ptr, size, ptr, size). | ||
| const char* args[] = {"-std=c++17"}; | ||
| auto* I = cppinterop_CreateInterpreter(args, 1, nullptr, 0); |
There was a problem hiding this comment.
warning: do not implicitly decay an array into a pointer; consider using gsl::array_view or an explicit cast instead [cppcoreguidelines-pro-bounds-array-to-pointer-decay]
auto* I = cppinterop_CreateInterpreter(args, 1, nullptr, 0);
^|
|
||
| namespace { | ||
| /// RAII wrapper for a dlopen handle used by the C API dispatch tests. | ||
| class DlHandle { |
There was a problem hiding this comment.
warning: class 'DlHandle' defines a non-default destructor, a copy constructor and a copy assignment operator but does not define a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
.
^| DlHandle& operator=(const DlHandle&) = delete; | ||
| explicit DlHandle(const char* path) { | ||
| #ifndef _WIN32 | ||
| H = dlopen(path, RTLD_LOCAL | RTLD_NOW); |
There was a problem hiding this comment.
warning: no header providing "RTLD_LOCAL" is directly included [misc-include-cleaner]
2
^| DlHandle& operator=(const DlHandle&) = delete; | ||
| explicit DlHandle(const char* path) { | ||
| #ifndef _WIN32 | ||
| H = dlopen(path, RTLD_LOCAL | RTLD_NOW); |
There was a problem hiding this comment.
warning: no header providing "RTLD_NOW" is directly included [misc-include-cleaner]
2
^7a5e50f to
bf639b0
Compare
The old clang-c/CXCppInterOp.h and CXCppInterOp.cpp maintained ~470 lines of hand-written C forwarding code that drifted from the C++ API on every change. CTypeMap records in CppInterOpAPI.td now drive the emitter to produce CXCppInterOpDecl.inc (C-compatible declarations) and CXCppInterOpImpl.inc (C++ implementations) as two separate files with no preprocessor guards needed at include sites. CTypeMap supports scalar passthrough, opaque pointer erasure, string and enum conversions, and collection signature rewriting (vector returns, out-params, in-params, string collections). CppInterOpArray, CppInterOpStringArray, and TemplateArgInfo use typedef struct for C compatibility, living at file scope in C and inside namespace CppImpl in C++. Functions the emitter cannot handle mechanically (GetClassTemplatedMethods, MakeFunctionCallable, GetDimensions) are marked NoCWrapper; GetClassTemplatedMethods has a hand-written wrapper in CXCppInterOp.cpp. Adds CAPITestC.c (pure C compilation of all declarations), C API dispatch tests via dlsym, and focused tests for scalars, enums, collections, and vector in-params.
bf639b0 to
9c32d55
Compare
The old clang-c/CXCppInterOp.h and CXCppInterOp.cpp maintained ~470 lines of hand-written C forwarding code that drifted from the C++ API on every change. CTypeMap records in CppInterOpAPI.td now drive the emitter to produce CXCppInterOpDecl.inc (C-compatible declarations) and CXCppInterOpImpl.inc (C++ implementations) as two separate files with no preprocessor guards needed at include sites.
CTypeMap supports scalar passthrough, opaque pointer erasure, string and enum conversions, and collection signature rewriting (vector returns, out-params, in-params, string collections). CppInterOpArray, CppInterOpStringArray, and TemplateArgInfo use typedef struct for C compatibility, living at file scope in C and inside namespace CppImpl in C++. Functions the emitter cannot handle mechanically (GetClassTemplatedMethods, MakeFunctionCallable, GetDimensions) are marked NoCWrapper; GetClassTemplatedMethods has a hand-written wrapper in CXCppInterOp.cpp.
Adds CAPITestC.c (pure C compilation of all declarations), C API dispatch tests via dlsym, and focused tests for scalars, enums, collections, and vector in-params.