Skip to content

Commit 32eddb0

Browse files
committed
metacallbindgen: extract types, generate wrappers, emit self-register.c
1 parent 4c0cfde commit 32eddb0

1 file changed

Lines changed: 253 additions & 28 deletions

File tree

  • source/cli/metacallbindgen/source

source/cli/metacallbindgen/source/main.cpp

Lines changed: 253 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,29 @@
1919

2020
namespace {
2121

22-
struct visitor_state
22+
struct type_map
2323
{
24-
std::vector<std::string> function_names;
24+
std::string metacall_id; // e.g. "METACALL_INT"
25+
std::string unwrap; // e.g. "metacall_value_to_int"
26+
std::string wrap; // e.g. "metacall_value_create_int"
27+
std::string cast; // e.g. "(int)" — applied to unwrap result before real call
28+
};
29+
30+
struct arg_info
31+
{
32+
std::string name;
33+
type_map t;
34+
};
35+
36+
struct fn_info
37+
{
38+
std::string name;
39+
CXType return_cx_type;
40+
type_map return_t;
41+
bool return_is_void = false;
42+
std::vector<arg_info> args;
43+
bool skip = false;
44+
std::string skip_reason;
2545
};
2646

2747
std::string cx_string_to_std(CXString s)
@@ -32,9 +52,67 @@ std::string cx_string_to_std(CXString s)
3252
return result;
3353
}
3454

55+
// Map a CXType to metacall type helpers. Returns false if unsupported.
56+
bool map_cxtype(CXType t, type_map &out)
57+
{
58+
CXType canonical = clang_getCanonicalType(t);
59+
60+
switch (canonical.kind)
61+
{
62+
case CXType_Void:
63+
out = { "METACALL_NULL", "", "metacall_value_create_null", "" };
64+
return true;
65+
case CXType_Bool:
66+
out = { "METACALL_BOOL", "metacall_value_to_bool", "metacall_value_create_bool", "" };
67+
return true;
68+
case CXType_Char_S:
69+
case CXType_Char_U:
70+
case CXType_SChar:
71+
case CXType_UChar:
72+
out = { "METACALL_CHAR", "metacall_value_to_char", "metacall_value_create_char", "" };
73+
return true;
74+
case CXType_Short:
75+
case CXType_UShort:
76+
out = { "METACALL_SHORT", "metacall_value_to_short", "metacall_value_create_short", "" };
77+
return true;
78+
case CXType_Int:
79+
case CXType_UInt:
80+
out = { "METACALL_INT", "metacall_value_to_int", "metacall_value_create_int", "" };
81+
return true;
82+
case CXType_Long:
83+
case CXType_ULong:
84+
case CXType_LongLong:
85+
case CXType_ULongLong:
86+
out = { "METACALL_LONG", "metacall_value_to_long", "metacall_value_create_long", "" };
87+
return true;
88+
case CXType_Float:
89+
out = { "METACALL_FLOAT", "metacall_value_to_float", "metacall_value_create_float", "" };
90+
return true;
91+
case CXType_Double:
92+
out = { "METACALL_DOUBLE", "metacall_value_to_double", "metacall_value_create_double", "" };
93+
return true;
94+
case CXType_Pointer:
95+
{
96+
CXType pointee = clang_getCanonicalType(clang_getPointeeType(canonical));
97+
// char * / const char * -> string
98+
if (pointee.kind == CXType_Char_S || pointee.kind == CXType_Char_U ||
99+
pointee.kind == CXType_SChar || pointee.kind == CXType_UChar)
100+
{
101+
out = { "METACALL_STRING", "metacall_value_to_string", "metacall_value_create_string", "" };
102+
return true;
103+
}
104+
// Any other pointer -> generic PTR
105+
out = { "METACALL_PTR", "metacall_value_to_ptr", "metacall_value_create_ptr", "(void *)" };
106+
return true;
107+
}
108+
default:
109+
return false;
110+
}
111+
}
112+
35113
CXChildVisitResult visit(CXCursor cursor, CXCursor /*parent*/, CXClientData data)
36114
{
37-
auto *state = static_cast<visitor_state *>(data);
115+
auto *fns = static_cast<std::vector<fn_info> *>(data);
38116

39117
if (clang_getCursorKind(cursor) != CXCursor_FunctionDecl)
40118
{
@@ -43,20 +121,169 @@ CXChildVisitResult visit(CXCursor cursor, CXCursor /*parent*/, CXClientData data
43121

44122
std::string name = cx_string_to_std(clang_getCursorSpelling(cursor));
45123

46-
if (name.rfind("metacall_", 0) != 0 && name != "metacallv" && name.rfind("metacall", 0) != 0)
124+
if (name.rfind("metacall", 0) != 0)
47125
{
48126
return CXChildVisit_Continue;
49127
}
50128

51-
state->function_names.push_back(name);
129+
// Skip the API we prototype-registered by hand.
130+
if (name == "metacall_print_info")
131+
{
132+
return CXChildVisit_Continue;
133+
}
134+
135+
fn_info fn;
136+
fn.name = name;
137+
138+
if (clang_Cursor_isVariadic(cursor))
139+
{
140+
fn.skip = true;
141+
fn.skip_reason = "variadic";
142+
}
52143

144+
CXType return_type = clang_getCursorResultType(cursor);
145+
fn.return_cx_type = return_type;
146+
fn.return_is_void = (clang_getCanonicalType(return_type).kind == CXType_Void);
147+
148+
if (!fn.skip && !map_cxtype(return_type, fn.return_t))
149+
{
150+
fn.skip = true;
151+
fn.skip_reason = std::string("unsupported return type: ") +
152+
cx_string_to_std(clang_getTypeSpelling(return_type));
153+
}
154+
155+
int num_args = clang_Cursor_getNumArguments(cursor);
156+
for (int i = 0; !fn.skip && i < num_args; ++i)
157+
{
158+
CXCursor arg_cursor = clang_Cursor_getArgument(cursor, i);
159+
arg_info arg;
160+
arg.name = cx_string_to_std(clang_getCursorSpelling(arg_cursor));
161+
if (arg.name.empty())
162+
{
163+
arg.name = "arg" + std::to_string(i);
164+
}
165+
CXType arg_type = clang_getCursorType(arg_cursor);
166+
if (!map_cxtype(arg_type, arg.t))
167+
{
168+
fn.skip = true;
169+
fn.skip_reason = std::string("unsupported arg[") + std::to_string(i) + "] type: " +
170+
cx_string_to_std(clang_getTypeSpelling(arg_type));
171+
}
172+
else
173+
{
174+
fn.args.push_back(arg);
175+
}
176+
}
177+
178+
fns->push_back(fn);
53179
return CXChildVisit_Continue;
54180
}
55181

182+
void emit_call_args(FILE *out, const fn_info &fn)
183+
{
184+
for (size_t i = 0; i < fn.args.size(); ++i)
185+
{
186+
if (i > 0) std::fprintf(out, ", ");
187+
std::fprintf(out, "%s%s(args[%zu])",
188+
fn.args[i].t.cast.c_str(),
189+
fn.args[i].t.unwrap.c_str(),
190+
i);
191+
}
192+
}
193+
194+
void emit_wrapper(FILE *out, const fn_info &fn)
195+
{
196+
std::fprintf(out, "static void *bindgen_%s(size_t argc, void *args[], void *data)\n{\n", fn.name.c_str());
197+
std::fprintf(out, "\t(void)argc;\n\t(void)args;\n\t(void)data;\n\n");
198+
199+
if (fn.return_is_void)
200+
{
201+
std::fprintf(out, "\t%s(", fn.name.c_str());
202+
emit_call_args(out, fn);
203+
std::fprintf(out, ");\n\n\treturn metacall_value_create_null();\n}\n\n");
204+
return;
205+
}
206+
207+
std::string return_spelling = cx_string_to_std(clang_getTypeSpelling(fn.return_cx_type));
208+
209+
std::fprintf(out, "\t%s ret = %s(", return_spelling.c_str(), fn.name.c_str());
210+
emit_call_args(out, fn);
211+
std::fprintf(out, ");\n");
212+
213+
if (fn.return_t.metacall_id == "METACALL_STRING")
214+
{
215+
std::fprintf(out, "\treturn metacall_value_create_string(ret, ret == NULL ? 0 : strlen(ret));\n}\n\n");
216+
}
217+
else
218+
{
219+
std::fprintf(out, "\treturn %s(ret);\n}\n\n", fn.return_t.wrap.c_str());
220+
}
221+
}
222+
223+
void emit_register_call(FILE *out, const fn_info &fn)
224+
{
225+
std::fprintf(out, "\tif (metacall_register(\"%s\", &bindgen_%s, NULL, %s, %zu",
226+
fn.name.c_str(), fn.name.c_str(), fn.return_t.metacall_id.c_str(), fn.args.size());
227+
for (const auto &a : fn.args)
228+
{
229+
std::fprintf(out, ", %s", a.t.metacall_id.c_str());
230+
}
231+
std::fprintf(out, ") != 0) return 1;\n");
232+
}
233+
234+
int emit_output(const std::vector<fn_info> &fns, const char *out_path)
235+
{
236+
FILE *out = std::fopen(out_path, "w");
237+
if (!out)
238+
{
239+
std::fprintf(stderr, "metacallbindgen: cannot open %s\n", out_path);
240+
return 1;
241+
}
242+
243+
std::fprintf(out,
244+
"/* Auto-generated by metacallbindgen. Do not edit by hand. */\n\n"
245+
"#include <metacall/metacall.h>\n"
246+
"#include <metacall/metacall_self_register.h>\n\n"
247+
"#include <string.h>\n\n"
248+
"static void *bindgen_metacall_print_info(size_t argc, void *args[], void *data)\n"
249+
"{\n"
250+
"\t(void)argc; (void)args; (void)data;\n"
251+
"\tconst char *info = metacall_print_info();\n"
252+
"\treturn metacall_value_create_string(info, info == NULL ? 0 : strlen(info));\n"
253+
"}\n\n");
254+
255+
size_t emitted = 0, skipped = 0;
256+
for (const auto &fn : fns)
257+
{
258+
if (fn.skip)
259+
{
260+
std::fprintf(out, "/* skipped: %s — %s */\n", fn.name.c_str(), fn.skip_reason.c_str());
261+
++skipped;
262+
continue;
263+
}
264+
emit_wrapper(out, fn);
265+
++emitted;
266+
}
267+
268+
std::fprintf(out,
269+
"\nint metacall_self_register_all(void)\n{\n"
270+
"\tif (metacall_register(\"metacall_print_info\", &bindgen_metacall_print_info, NULL, METACALL_STRING, 0) != 0) return 1;\n");
271+
for (const auto &fn : fns)
272+
{
273+
if (fn.skip) continue;
274+
emit_register_call(out, fn);
275+
}
276+
std::fprintf(out, "\treturn 0;\n}\n");
277+
278+
std::fclose(out);
279+
std::fprintf(stderr, "metacallbindgen: emitted %zu wrappers, skipped %zu\n", emitted, skipped);
280+
return 0;
281+
}
282+
56283
int usage(const char *argv0)
57284
{
58285
std::fprintf(stderr,
59-
"usage: %s <path/to/metacall.h> [-I<include> ...]\n",
286+
"usage: %s <path/to/metacall.h> -o <output.c> [-I<include> ...]\n",
60287
argv0);
61288
return 1;
62289
}
@@ -65,29 +292,32 @@ int usage(const char *argv0)
65292

66293
int main(int argc, char *argv[])
67294
{
68-
if (argc < 2)
69-
{
70-
return usage(argv[0]);
71-
}
295+
if (argc < 4) return usage(argv[0]);
72296

73297
const char *header_path = argv[1];
74-
298+
const char *out_path = nullptr;
75299
std::vector<const char *> clang_args;
300+
76301
for (int i = 2; i < argc; ++i)
77302
{
78-
clang_args.push_back(argv[i]);
303+
if (std::strcmp(argv[i], "-o") == 0 && i + 1 < argc)
304+
{
305+
out_path = argv[++i];
306+
}
307+
else
308+
{
309+
clang_args.push_back(argv[i]);
310+
}
79311
}
80312

81-
CXIndex index = clang_createIndex(0, 1);
313+
if (!out_path) return usage(argv[0]);
82314

315+
CXIndex index = clang_createIndex(0, 1);
83316
CXTranslationUnit unit = nullptr;
84317
CXErrorCode error = clang_parseTranslationUnit2(
85-
index,
86-
header_path,
87-
clang_args.data(),
88-
static_cast<int>(clang_args.size()),
89-
nullptr,
90-
0,
318+
index, header_path,
319+
clang_args.data(), static_cast<int>(clang_args.size()),
320+
nullptr, 0,
91321
CXTranslationUnit_SkipFunctionBodies,
92322
&unit);
93323

@@ -98,18 +328,13 @@ int main(int argc, char *argv[])
98328
return 2;
99329
}
100330

101-
visitor_state state;
331+
std::vector<fn_info> fns;
102332
CXCursor cursor = clang_getTranslationUnitCursor(unit);
103-
clang_visitChildren(cursor, &visit, &state);
333+
clang_visitChildren(cursor, &visit, &fns);
104334

105-
std::printf("Discovered %zu metacall_* function declarations:\n", state.function_names.size());
106-
for (const auto &name : state.function_names)
107-
{
108-
std::printf(" %s\n", name.c_str());
109-
}
335+
int rc = emit_output(fns, out_path);
110336

111337
clang_disposeTranslationUnit(unit);
112338
clang_disposeIndex(index);
113-
114-
return 0;
339+
return rc;
115340
}

0 commit comments

Comments
 (0)