1919
2020namespace {
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
2747std::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+
35113CXChildVisitResult 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\t return 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, " \t return metacall_value_create_string(ret, ret == NULL ? 0 : strlen(ret));\n }\n\n " );
216+ }
217+ else
218+ {
219+ std::fprintf (out, " \t return %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, " \t if (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+ " \t const char *info = metacall_print_info();\n "
252+ " \t return 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+ " \n int metacall_self_register_all(void)\n {\n "
270+ " \t if (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, " \t return 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+
56283int 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
66293int 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