Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
interop_wrapper.cxx
Go to the documentation of this file.
1#ifndef _WIN32
2#ifndef _CRT_SECURE_NO_WARNINGS
3// silence warnings about getenv, strncpy, etc.
4#define _CRT_SECURE_NO_WARNINGS
5#endif
6#endif
7
8#include "precommondefs.h" // This defines several system feature macros and should be included before any system header.
9
10// Bindings
11#include "cppjit_interop.h"
12
13using namespace cppjit;
14#include "callcontext.h"
15
16// ROOT
17#include "TClass.h"
18#include "TInterpreter.h"
19#include "TROOT.h"
20#include "TSystem.h"
21
23
24static inline size_t CALL_NARGS(size_t nargs) { return nargs & ~DIRECT_CALL; }
25
26// Standard
27#include <algorithm> // for std::count, std::remove
28#include <array>
29#include <cassert>
30#include <csignal>
31#include <cstdlib> // for getenv
32#include <cstring>
33#include <iostream>
34#include <map>
35#include <mutex>
36#include <new>
37#include <regex>
38#include <set>
39#include <sstream>
40#include <stdexcept>
41#include <typeinfo>
42#include <vector>
43
44// Take ROOT's interpreter lock (when it exists) around every CppInterOp
45// entry point, so Python-side reflection serializes against TCling users on
46// other threads (I/O, RDataFrame workers).
47// The private recursive mutex still serializes concurrent Python-side callers
48// when ROOT thread safety is not enabled. Lock order is global-then-local; no
49// deadlock is possible since threads coming through TCling only ever take the
50// global mutex.
52public:
53 void lock() {
54 // gInterpreterMutex may get created at any point (by
55 // ROOT::EnableThreadSafety), so remember for each acquisition what was
56 // actually locked, to release exactly that in unlock().
58 if (global)
59 global->Lock();
60 fLocal.lock();
61 fGlobalLocked.push_back(global);
62 }
63
64 void unlock() {
65 TVirtualMutex* global = fGlobalLocked.back();
66 fGlobalLocked.pop_back();
67 fLocal.unlock();
68 if (global)
69 global->UnLock();
70 }
71
72private:
73 std::recursive_mutex fLocal;
74 // Lock/unlock pairs are properly nested per thread, so a per-thread stack
75 // suffices to match each unlock() to its lock().
76 static thread_local std::vector<TVirtualMutex*> fGlobalLocked;
77};
78
79thread_local std::vector<TVirtualMutex*> RInterOpMutex::fGlobalLocked;
80
82
83// builtin types
84static std::set<std::string> g_builtins = {"bool",
85 "char",
86 "signed char",
87 "unsigned char",
88 "wchar_t",
89 "short",
90 "unsigned short",
91 "int",
92 "unsigned int",
93 "long",
94 "unsigned long",
95 "long long",
96 "unsigned long long",
97 "float",
98 "double",
99 "long double",
100 "void"};
101
102// configuration
103static bool gEnableFastPath = true;
104
105// global initialization -----------------------------------------------------
106namespace {
107
108static inline bool is_integral(std::string& s) {
109 if (s == "false") {
110 s = "0";
111 return true;
112 } else if (s == "true") {
113 s = "1";
114 return true;
115 }
116 return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) {
117 return !std::isdigit(c);
118 }) == s.end();
119}
120
121struct InterOpPaths {
122 std::string Library;
123 std::vector<std::string> IncludeDirs;
124 std::string ClangIncludeDir; // empty when no usable resource dir is known
125};
126
127// In ROOT the dispatch API comes from libCling (loadDispatchAPI) and the
128// CppInterOp headers are staged under ROOT's etc directory, etc/cppinterop
129// in the build tree and in the install tree alike, so TROOT::GetEtcDir()
130// locates them in both.
131static InterOpPaths cppinterop_paths() {
132 InterOpPaths Paths;
133 std::string dir = TROOT::GetEtcDir().Data();
134 dir += "/cppinterop";
135 if (gSystem->AccessPathName(dir.c_str()))
136 std::cerr << "[cppjit] CppInterOp headers not found in " << dir
137 << std::endl;
138 Paths.IncludeDirs.push_back(dir);
139 return Paths;
140}
141
142// The one place the dispatch source is dlopen'd. In ROOT, CppInterOp is
143// compiled into libCling, so the dispatch API is loaded from there instead of
144// from a standalone libclangCppInterOp.
145static bool loadDispatchAPI(const InterOpPaths& /*Paths*/) {
146 (void)gROOT;
147 char* libcling = gSystem->DynamicPathName("libCling");
148
149 if (!libcling) {
150 std::cerr << "[cppjit] Failed to find libCling" << std::endl;
151 return false;
152 }
153 if (!Cpp::LoadDispatchAPI(libcling)) {
154 std::cerr << "[cppjit] Failed to load CppInterOp" << std::endl;
155 return false;
156 }
157 return true;
158}
159
160// CppInterOp itself appends CPPINTEROP_EXTRA_INTERPRETER_ARGS inside
161// CreateInterpreter, so nothing needs to be forwarded from here.
163acquireOrCreateInterpreter(const InterOpPaths& Paths) {
164 if (auto existingInterp = Cpp::GetInterpreter())
165 return existingInterp;
166
167 std::vector<const char*> args = {"-std=c++17"};
168#if !(defined(__arm64__) && defined(__APPLE__))
169 // apple silicon clang rejects -march=native
170 args.push_back("-march=native");
171#endif
172 // Without clang's builtin headers the interpreter fails at its first
173 // #include. Prefer the bundled copy: it matches the build clang and
174 // needs no LLVM on the host. DetectResourceDir refuses version
175 // mismatches, and CppInterOp itself probes only bare `clang`.
176 std::string resourceDir = Paths.ClangIncludeDir;
177 if (resourceDir.empty())
178 resourceDir = Cpp::DetectResourceDir("clang-" CPPJIT_CLANG_MAJOR);
179 if (!resourceDir.empty()) {
180 args.push_back("-resource-dir");
181 args.push_back(resourceDir.c_str());
182 }
183 return Cpp::CreateInterpreter(args, /*GpuArgs=*/{});
184}
185
186static void configureInterpreter(const InterOpPaths& Paths) {
187 std::set<std::string> bi{g_builtins};
188 for (const auto& name : bi) {
189 for (const char* a : {"*", "&", "*&", "[]", "*[]"})
190 g_builtins.insert(name + a);
191 }
192
193 if (getenv("CPPJIT_DISABLE_FASTPATH"))
194 gEnableFastPath = false;
195
196 // set opt level (default to 2 if not given; Cling itself defaults to 0)
197 int optLevel = 2;
198
199 if (getenv("CPPJIT_OPT_LEVEL"))
200 optLevel = atoi(getenv("CPPJIT_OPT_LEVEL"));
201
202 if (optLevel != 0) {
203 std::ostringstream s;
204 s << "#pragma cling optimize " << optLevel;
205 Cpp::Process(s.str().c_str());
206 }
207
208 for (const std::string& dir : Paths.IncludeDirs)
209 Cpp::AddIncludePath(dir.c_str());
210 Cpp::LoadLibrary("libstdc++", /* lookup= */ true);
211}
212
213static bool preloadHeaders() {
214 const char* code = "#include <algorithm>\n"
215 "#include <numeric>\n"
216 "#include <complex>\n"
217 "#include <iostream>\n"
218 "#include <string.h>\n" // for strcpy
219 "#include <string>\n"
220 "#include <vector>\n"
221 "#include <utility>\n"
222 "#include <memory>\n"
223 "#include <functional>\n" // for the dispatcher code to
224 // use std::function
225 "#include <map>\n" // FIXME: Replace with modules
226 "#include <sstream>\n" // FIXME: Replace with modules
227 "#include <array>\n" // FIXME: Replace with modules
228 "#include <list>\n" // FIXME: Replace with modules
229 "#include <deque>\n" // FIXME: Replace with modules
230 "#include <tuple>\n" // FIXME: Replace with modules
231 "#include <set>\n" // FIXME: Replace with modules
232 "#include <chrono>\n" // FIXME: Replace with modules
233 "#include <cmath>\n" // FIXME: Replace with modules
234 "#if __has_include(<optional>)\n"
235 "#include <optional>\n"
236 "#endif\n"
237 "#include <CppInterOp/Dispatch.h>\n";
238 return Cpp::Process(code) == 0;
239}
240
241static void defineRuntimeHelpers() {
242 Cpp::Declare("namespace __cppjit_internal { template<class C1, class C2>"
243 " bool is_equal(const C1& c1, const C2& c2) { return "
244 "(bool)(c1 == c2); } }",
245 /*silent=*/false);
246 Cpp::Declare("namespace __cppjit_internal { template<class C1, class C2>"
247 " bool is_not_equal(const C1& c1, const C2& c2) { return "
248 "(bool)(c1 != c2); } }",
249 /*silent=*/false);
250
251 // helper for multiple inheritance
252 Cpp::Declare("namespace __cppjit_internal { struct Sep; }",
253 /*silent=*/false);
254}
255
256} // unnamed namespace
257
258// Load CppInterOp and set up the interpreter. A dlopen during static
259// initialization is unsafe, so _cpython_cppjit.py calls this explicitly
260// before the first libcppjit use. Thread-safe and idempotent; returns 1 on
261// success.
262extern "C" {
264}
265
266extern "C" int LoadCppInterOp() {
267 static std::once_flag Once;
268 static int Loaded = 0;
269 std::call_once(Once, [] {
270 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
271 const InterOpPaths Paths = cppinterop_paths();
272 if (!loadDispatchAPI(Paths))
273 return;
274
275 if (!acquireOrCreateInterpreter(Paths)) {
276 std::cerr << "[cppjit] Failed to create the C++ interpreter" << std::endl;
277 return;
278 }
279 configureInterpreter(Paths);
280 if (!preloadHeaders()) {
281 std::cerr << "[cppjit] The C++ standard headers do not parse, see the "
282 "diagnostic above. Install a C++ toolchain such as g++ or "
283 "the conda package cxx-compiler."
284 << std::endl;
285 return;
286 }
287 defineRuntimeHelpers();
288
289 Loaded = 1;
290 });
291 return Loaded;
292}
293
294// local helpers -------------------------------------------------------------
295static inline char* cppstring_to_cstring(const std::string& cppstr) {
296 char* cstr = (char*)malloc(cppstr.size() + 1);
297 memcpy(cstr, cppstr.c_str(), cppstr.size() + 1);
298 return cstr;
299}
300
301// direct interpreter access -------------------------------------------------
302// Returns false on failure and true on success
303bool interop::Compile(const std::string& code, bool silent) {
304 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
305 // Declare returns an enum which equals 0 on success
306 return !Cpp::Declare(code.c_str(), silent);
307}
308
310
312
314 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
315 if (klass && obj && !Cpp::IsNamespace(klass))
316 return Cpp::ObjToString(Cpp::GetQualifiedCompleteName(klass).c_str(),
317 obj.data);
318 return "";
319}
320
321// // name to opaque C++ scope representation
322// -----------------------------------
323std::string interop::ResolveName(const std::string& name) {
324 if (!name.empty()) {
326 interop::GetType(name, /*enable_slow_lookup=*/true))
328 return name;
329 }
330 return "";
331}
332
334 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
335 if (Cpp::GetValueKind(type) != Cpp::ValueKind::LValue)
336 return type;
337
338 TCppType_t nonReferenceType = Cpp::GetNonReferenceType(type);
339 if (Cpp::IsEnumType(nonReferenceType)) {
340 TCppType_t underlying_type =
341 Cpp::GetIntegerTypeFromEnumType(nonReferenceType);
342 return Cpp::GetReferencedType(underlying_type, /*rvalue=*/false);
343 }
344 return type;
345}
346
348 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
349 if (!Cpp::IsPointerType(type))
350 return type;
351
352 TCppType_t PointeeType = Cpp::GetPointeeType(type);
353 if (Cpp::IsEnumType(PointeeType)) {
354 TCppType_t underlying_type = Cpp::GetIntegerTypeFromEnumType(PointeeType);
355 return Cpp::GetPointerType(underlying_type);
356 }
357 return type;
358}
359
361 interop::TCppType_t check_int_typedefs = type;
362 if (Cpp::IsPointerType(check_int_typedefs))
363 check_int_typedefs = Cpp::GetPointeeType(check_int_typedefs);
364 if (Cpp::IsReferenceType(check_int_typedefs))
365 check_int_typedefs =
366 Cpp::GetReferencedType(check_int_typedefs, /*rvalue=*/false);
367
368 if (Cpp::GetTypeAsString(check_int_typedefs) == "int8_t" ||
369 Cpp::GetTypeAsString(check_int_typedefs) == "uint8_t")
370 return check_int_typedefs;
371 return nullptr;
372}
373
375 if (!type)
376 return type;
377
378 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
379
380 TCppType_t check_int_typedefs = int_like_type(type);
381 if (check_int_typedefs)
382 return type;
383
384 interop::TCppType_t canonType = Cpp::GetCanonicalType(type);
385
386 if (Cpp::IsEnumType(canonType)) {
387 if (Cpp::GetTypeAsString(type) != "std::byte")
388 return Cpp::GetIntegerTypeFromEnumType(canonType);
389 }
390 if (Cpp::HasTypeQualifier(canonType, Cpp::QualKind::Restrict)) {
391 return Cpp::RemoveTypeQualifier(canonType, Cpp::QualKind::Restrict);
392 }
393
394 return canonType;
395}
396
398 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
399 TCppType_t check_int_typedefs = int_like_type(type);
400 if (check_int_typedefs)
401 return check_int_typedefs;
402 return Cpp::GetUnderlyingType(type);
403}
404
406 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
407 return Cpp::GetPointerType(type);
408}
409
411 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
412 return Cpp::GetReferencedType(type, rvalue);
413}
414
416 return Cpp::GetValueKind(type) == Cpp::ValueKind::RValue;
417}
418
420 return Cpp::GetValueKind(type) == Cpp::ValueKind::LValue;
421}
422
423// Whether the callee can rebind a pointer through this reference (T*&);
424// false for T* const& and for references to non-pointers.
426 if (!Cpp::IsReferenceType(type))
427 return false;
428 TCppType_t nonref = Cpp::GetNonReferenceType(type);
429 return Cpp::IsPointerType(nonref) &&
430 !Cpp::HasTypeQualifier(nonref, Cpp::QualKind::Const);
431}
432
433bool interop::IsClassType(TCppType_t type) { return Cpp::IsRecordType(type); }
434
435bool interop::IsIntegerType(TCppType_t type, bool* is_signed /*= nullptr*/) {
436 if (is_signed) {
437 Cpp::Signedness sign;
438 bool res = Cpp::IsIntegerType(type, &sign);
439 *is_signed = (sign == Cpp::Signedness::kSigned);
440 return res;
441 }
442 return Cpp::IsIntegerType(type, nullptr);
443}
444
446 return Cpp::IsPointerType(type);
447}
448
450 return Cpp::IsFunctionPointerType(type);
451}
452
453std::string trim(const std::string& line) {
454 if (line.empty())
455 return "";
456 const char* WhiteSpace = " \t\v\r\n";
457 std::size_t start = line.find_first_not_of(WhiteSpace);
458 std::size_t end = line.find_last_not_of(WhiteSpace);
459 return line.substr(start, end - start + 1);
460}
461
462// returns false of angular brackets dont match, else true
463bool split_comma_saparated_types(const std::string& name,
464 std::vector<std::string>& types) {
465 std::string trimed_name = trim(name);
466 size_t start_pos = 0;
467 size_t end_pos = 0;
468 int matching_angular_brackets = 0;
469 while (end_pos < trimed_name.size()) {
470 switch (trimed_name[end_pos]) {
471 case ',': {
472 if (!matching_angular_brackets) {
473 if (end_pos > start_pos)
474 types.push_back(
475 trim(trimed_name.substr(start_pos, end_pos - start_pos)));
476 start_pos = end_pos + 1;
477 }
478 break;
479 }
480 case '<': {
481 matching_angular_brackets++;
482 break;
483 }
484 case '>': {
485 matching_angular_brackets--;
486 break;
487 }
488 }
489 end_pos++;
490 }
491 if (start_pos < trimed_name.size())
492 types.push_back(trim(trimed_name.substr(start_pos, end_pos - start_pos)));
493 return true;
494}
495
497 std::string delim = "::";
498 size_t start = 0;
499 size_t end = name.find(delim);
500 interop::TCppScope_t curr_scope;
501 while (end != std::string::npos) {
502 curr_scope = Cpp::GetNamed(name.substr(start, end - start), curr_scope);
503 start = end + delim.length();
504 end = name.find(delim, start);
505 }
506 return Cpp::GetNamed(name.substr(start, end), curr_scope);
507}
508static bool is_identifier(std::string_view s) {
509 if (s.empty())
510 return false;
511 auto is_valid_start = [](unsigned char c) {
512 return std::isalpha(c) || c == '_';
513 };
514 auto is_valid_body = [](unsigned char c) {
515 return std::isalnum(c) || c == '_';
516 };
517 return is_valid_start(s[0]) &&
518 std::all_of(s.begin() + 1, s.end(), is_valid_body);
519};
520
521// returns true if no new type was added.
522bool interop::AppendTypesSlow(const std::string& name,
523 std::vector<Cpp::TemplateArgInfo>& types,
524 interop::TCppScope_t parent) {
525
526 // Add no new type if string is empty
527 if (name.empty())
528 return true;
529
530 // The ast printer gave us garbage.
531 if (name == "<unnamed>")
532 return true;
533
534 // A type inside an anonymous namespace cannot be spelled in injected
535 // code; attempting it crashes codegen on the ill-formed recovery (e.g.
536 // auto-downcasting to an anonymous FuncExporter<...> instantiation).
537 if (name.find("(anonymous namespace)") != std::string::npos)
538 return true;
539
540 auto replace_all = [](std::string& str, const std::string& from,
541 const std::string& to) {
542 if (from.empty())
543 return;
544 size_t start_pos = 0;
545 while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
546 str.replace(start_pos, from.length(), to);
547 start_pos += to.length();
548 }
549 };
550
551 std::string resolved_name = name;
552 replace_all(resolved_name, "std::initializer_list<",
553 "std::vector<"); // replace initializer_list with vector
554
555 // If we have a single identifier, we don't need anything complicated.
556 // Try scoped lookup first (catches type aliases / nested types declared
557 // inside `parent`), then fall back to TU (catches typedefs declared
558 // outside the query scope, e.g. `typedef Foo Bar;` at TU consulted
559 // from a method on Foo).
560 if (is_identifier(name)) {
561 TCppType_t type = parent ? Cpp::GetType(name, parent) : nullptr;
562 if (!type)
563 type = Cpp::GetType(name);
564 if (type) {
565 types.emplace_back(type.data);
566 return false;
567 }
568 if (!parent || parent == Cpp::GetGlobalScope())
569 return true;
570 // Fall through: the name may live in a scope enclosing `parent` (e.g.
571 // "RVecF" from within ROOT::RDF::RInterface<...> resolves to
572 // ROOT::RVecF); the trampoline below applies real unqualified lookup.
573 }
574
575 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
576
577 // Every lookup below declares code through the interpreter, which parses,
578 // commits a transaction and leaves a new declaration in the AST, so each one
579 // costs more than the last. The same names come back constantly (overload
580 // ranking resolves every argument type of every candidate on each call that
581 // is not memoized), so remember what a name resolved to. A name that fails
582 // may still resolve once more declarations are available, so only successes
583 // are cached.
584 using SlowTypesKey = std::pair<std::string, const void*>;
585 static std::map<SlowTypesKey, std::vector<Cpp::TemplateArgInfo>> s_cache;
586 const SlowTypesKey cache_key{name, parent.data};
587 // the integral values are strings whose ownership passes to the caller
588 // (which frees them), so the cache keeps and hands out copies of its own
589 auto copy_of = [](const Cpp::TemplateArgInfo& info) {
590 return Cpp::TemplateArgInfo(info.m_Type, info.m_IntegralValue
591 ? strdup(info.m_IntegralValue)
592 : nullptr);
593 };
594 if (auto cached = s_cache.find(cache_key); cached != s_cache.end()) {
595 for (const Cpp::TemplateArgInfo& info : cached->second)
596 types.push_back(copy_of(info));
597 return false;
598 }
599 const size_t n_types_before = types.size();
600 auto remember = [&] {
601 std::vector<Cpp::TemplateArgInfo> resolved;
602 for (size_t i = n_types_before; i < types.size(); ++i)
603 resolved.push_back(copy_of(types[i]));
604 s_cache.emplace(cache_key, std::move(resolved));
605 };
606
607 // We might have an entire expression such as int, double.
608 static unsigned long long struct_count = 0;
609 std::string code =
610 "template<typename ...T> struct __cppjit_interop_AppendTypesSlow {};\n";
611 if (!struct_count)
612 Cpp::Declare(code.c_str(), /*silent=*/true); // initialize the trampoline
613
614 // Perform the lookup from within the innermost (reopenable) namespace
615 // enclosing `parent` -- `parent` itself if it is a namespace, else the
616 // namespace its class chain lives in -- by reopening that namespace around
617 // the trampoline declaration: the compiler then applies real C++ name
618 // lookup, walking outward with proper shadowing of outer names, which a
619 // global declaration with a parent-qualified fallback cannot emulate (e.g.
620 // "RVecF" from within ROOT::RDF::RInterface<...> resolves to ROOT::RVecF).
621 std::string lookup_ns;
622 TCppScope_t lookup_scope = nullptr;
623 for (TCppScope_t s = parent; s && s != Cpp::GetGlobalScope();
624 s = Cpp::GetParentScope(s)) {
625 if (!Cpp::IsNamespace(s))
626 continue;
627 std::string qname = Cpp::GetQualifiedName(s);
628 if (!qname.empty() && qname.find('(') == std::string::npos) { // unnamed?
629 lookup_ns = qname;
630 lookup_scope = s;
631 }
632 break;
633 }
634
635 // The reopened namespace doesn't see names nested in a class parent, so a
636 // name written relative to it (e.g. a nested type) won't resolve. Try the
637 // name as given, then qualified by the parent.
638 std::vector<std::string> candidates = {resolved_name};
639 if (parent && parent != Cpp::GetGlobalScope() && parent != lookup_scope &&
640 (interop::IsNamespace(parent) || interop::IsClass(parent)))
641 candidates.push_back(Cpp::GetQualifiedCompleteName(parent) +
642 "::" + resolved_name);
643
644 for (const std::string& candidate : candidates) {
645 std::string var = "__cppjit_interop_s" + std::to_string(struct_count++);
646 // nodebug: with -g the variable's debug info would carry the full DIE
647 // tree of every template argument (all member declarations included) --
648 // a large, uncacheable per-lookup cost on heavyweight types.
649 std::string decl = "__cppjit_interop_AppendTypesSlow<" + candidate +
650 "> __attribute__((nodebug)) " + var + ";\n";
651 if (!lookup_ns.empty())
652 decl = "namespace " + lookup_ns + " { " + decl + "}\n";
653 if (!Cpp::Declare(decl.c_str(), /*silent=*/true)) {
654 TCppType_t varN = Cpp::GetVariableType(Cpp::GetNamed(
655 var.c_str(), lookup_ns.empty() ? nullptr : lookup_scope));
656 TCppScope_t instance_class = Cpp::GetScopeFromType(varN);
657 if (!instance_class)
658 continue; // recovered-but-broken decl; try next candidate or split path
659 size_t oldSize = types.size();
660 Cpp::GetClassTemplateInstantiationArgs(instance_class, types);
661 if (oldSize == types.size())
662 return true;
663 remember();
664 return false;
665 }
666 }
667
668 // We split each individual types based on , and resolve it
669 // FIXME: see discussion on should we support template instantiation with
670 // string:
671 // https://github.com/compiler-research/cppyy-backend/pull/137#discussion_r2079357491
672 // We should consider eliminating the `split_comma_saparated_types` and
673 // `is_integral` string parsing.
674 std::vector<std::string> individual_types;
675 if (!split_comma_saparated_types(resolved_name, individual_types))
676 return true;
677
678 for (std::string& i : individual_types) {
679 // Try going via interop::GetType first.
680 const char* integral_value = nullptr;
681 interop::TCppType_t type = nullptr;
682
683 if (!lookup_ns.empty()) {
684 // resolve from within the parent namespace, honouring C++ name lookup
685 // and shadowing (see the trampoline declaration above)
686 std::string id = "__cppjit_interop_s" + std::to_string(struct_count++);
687 if (!Cpp::Declare(("namespace " + lookup_ns + " { using " + id +
688 " = __typeof__(" + i + "); }\n")
689 .c_str(),
690 /*silent=*/true))
691 type = Cpp::GetCanonicalType(
692 Cpp::GetTypeFromScope(Cpp::GetNamed(id, lookup_scope)));
693 } else {
694 type = GetType(i, /*enable_slow_lookup=*/true);
695 if (!type && parent &&
696 (interop::IsNamespace(parent) || interop::IsClass(parent))) {
697 type =
698 interop::GetTypeFromScope(interop::GetNamed(resolved_name, parent));
699 }
700 }
701
702 if (!type) {
703 types.clear();
704 return true;
705 }
706
707 if (is_integral(i))
708 integral_value = strdup(i.c_str());
710 if (Cpp::IsEnumConstant(scope))
711 integral_value =
712 strdup(std::to_string(Cpp::GetEnumConstantValue(scope)).c_str());
713 types.emplace_back(type.data, integral_value);
714 }
715 remember();
716 return false;
717}
718
720 bool enable_slow_lookup /* = false */) {
721 // The ast printer gave us garbage.
722 if (name == "<unnamed>")
723 return nullptr;
724 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
725
726 if (auto type = Cpp::GetType(name))
727 return type;
728
729 // Plain identifiers don't need the heavy __typeof__ trampoline:
730 // Cpp::GetType above already covers builtin types and named
731 // scopes. Exception: the three identifier-shaped C++ value-
732 // literals -- `true`, `false`, `nullptr` -- aren't reachable by
733 // name (no type called "false") but appear as non-type template
734 // args in libstdc++ types like _Node_iterator<..., false, false>;
735 // map them to their underlying type directly so the per-chunk
736 // fallback in AppendTypesSlow gets a real type without paying
737 // the trampoline cost.
738 if (is_identifier(name)) {
739 if (name == "true" || name == "false")
740 return Cpp::GetType("bool");
741 if (name == "nullptr")
742 return Cpp::GetType("nullptr_t", Cpp::GetNamed("std"));
743 return nullptr;
744 }
745
746 if (!enable_slow_lookup) {
747 if (name.find("::") != std::string::npos)
748 throw std::runtime_error(
749 "Calling interop::GetType with qualified name '" + name + "'\n");
750 return nullptr;
751 }
752
753 // Here we might need to deal with integral types such as 3.14.
754
755 // Declaring the trampoline parses and compiles code, and each one adds a
756 // new alias to the AST, so memoize the resolved types by name. A name that
757 // fails to resolve may succeed once more declarations are available, so
758 // failures are retried.
759 static std::map<std::string, TCppType_t> s_slow_type_cache;
760 auto cached = s_slow_type_cache.find(name);
761 if (cached != s_slow_type_cache.end())
762 return cached->second;
763
764 static unsigned long long var_count = 0;
765 std::string id = "__cppjit_interop_GetType_" + std::to_string(var_count++);
766 std::string using_clause = "using " + id + " = __typeof__(" + name + ");\n";
767
768 if (!Cpp::Declare(using_clause.c_str(), /*silent=*/true)) {
769 TCppScope_t lookup = Cpp::GetNamed(id);
770 TCppType_t lookup_ty = Cpp::GetTypeFromScope(lookup);
771 TCppType_t result = Cpp::GetCanonicalType(lookup_ty);
772 if (result)
773 s_slow_type_cache.emplace(name, result);
774 return result;
775 }
776 return nullptr;
777}
778
780 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
781 return Cpp::GetComplexType(Cpp::GetType(name));
782}
783
785 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
786 std::string type =
787 Cpp::GetTypeAsString(Cpp::GetIntegerTypeFromEnumScope(handle));
788 if (type == "signed char")
789 return "char";
790 return type;
791}
792
794 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
795 return Cpp::GetUnderlyingScope(scope);
796}
797
799 TCppScope_t parent_scope) {
800 std::unique_lock<RInterOpMutex> Lock(InterOpMutex);
801 // Resolving a templated name below declares a new variable in the
802 // interpreter (see AppendTypesSlow), so remember the result to not declare
803 // one, and grow the memory, on every lookup of the same name.
804 static std::map<std::pair<void*, std::string>, TCppScope_t> template_scopes;
805 const bool is_template = name.find('<') != std::string::npos;
806 const auto key = std::make_pair(parent_scope.data, name);
807 if (is_template)
808 if (auto it = template_scopes.find(key); it != template_scopes.end())
809 return it->second;
810
811 // CppInterOp directly looks at the AST which is not enough.
812 // We require lazy module loading that ROOT relies on, so we do it here
813 // first. Use TClass::GetClass to trigger auto-loading of dictionaries and
814 // modules.
815 // This also covers nested names: declarations come from the C++ modules,
816 // so e.g. ROOT::RDF can be used before its libraries are loaded, and the
817 // JIT then finds their symbols only by scanning every library on the
818 // search path (about half a second).
819 if (!parent_scope || parent_scope == Cpp::GetGlobalScope())
820 TClass::GetClass(name.c_str(), true /* load */, true /* silent */);
821 else if (Cpp::IsNamespace(parent_scope) || Cpp::IsClass(parent_scope))
823 (Cpp::GetQualifiedCompleteName(parent_scope) + "::" + name).c_str(),
824 true /* load */, true /* silent */);
825
826 if (interop::TCppScope_t scope = Cpp::GetScope(name, parent_scope))
827 return scope;
828 if (!parent_scope || parent_scope == Cpp::GetGlobalScope()) {
829 if (interop::TCppScope_t scope = Cpp::GetScopeFromCompleteName(name))
830 return scope;
831 } else if (name.find('<') == std::string::npos) {
832 // Cpp::GetScope handles single identifiers only, so walk the components
833 // of a qualified name. Templated names take the branch below: "::" inside
834 // a template argument list cannot be split naively.
835 TCppScope_t curr = parent_scope;
836 size_t start = 0, end;
837 while (curr && (end = name.find("::", start)) != std::string::npos) {
838 curr = Cpp::GetScope(name.substr(start, end - start), curr);
839 start = end + 2;
840 }
841 if (curr && curr != parent_scope)
842 if (interop::TCppScope_t scope = Cpp::GetScope(name.substr(start), curr))
843 return scope;
844 }
845
846 // FIXME: avoid string parsing here
847 if (is_template) {
848 // Templated type; may need instantiation. Resolve the whole type
849 // expression (e.g. "std::array<float, 3>") and read back its scope.
850 // Splitting off the argument list and resolving it directly cannot
851 // represent non-type arguments such as the `3` in std::array<float, 3>.
852 std::vector<Cpp::TemplateArgInfo> types;
853 Lock.unlock(); // unlock to allow AppendTypesSlow
854 bool added_new_type =
855 !interop::AppendTypesSlow(name, types, /*parent=*/parent_scope);
856 Lock.lock();
857 if (added_new_type && types.size() == 1) {
858 // A pointer or reference spelling (e.g. "std::chrono::nanoseconds *",
859 // the return type of std::array<nanoseconds, N>::begin()) does not
860 // name a scope; GetScopeFromType would silently strip the pointer and
861 // return the pointee's scope, misclassifying the name.
862 if (Cpp::IsPointerType(types[0].m_Type) ||
863 Cpp::IsReferenceType(types[0].m_Type))
864 return nullptr;
865 TCppScope_t scope = Cpp::GetScopeFromType(types[0].m_Type);
866 // Naming the type as a template argument above does not instantiate
867 // it, so the specialization may still be declared-but-undefined.
868 // Force its definition: callers expect a complete scope, e.g. to
869 // walk its base classes.
870 if (scope) {
871 Cpp::IsComplete(scope);
872 template_scopes[key] = scope;
873 }
874 return scope;
875 }
876 }
877 return nullptr;
878}
879
883
885 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
886 return Cpp::GetScopeFromType(Cpp::GetVariableType(var));
887}
888
890 TCppScope_t parent_scope) {
891 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
892 if (TCppScope_t named = Cpp::GetNamed(name, parent_scope))
893 return named;
894
895 // "gROOT" is a macro expanding to ROOT::GetROOT(); ROOT master exposed it
896 // to cppyy as a TGlobalMappedFunction, a mechanism this backend does not
897 // consult. Resolve it to the underlying variable instead (making sure it
898 // has been initialized first).
899 if (name == "gROOT" &&
900 (!parent_scope || parent_scope == Cpp::GetGlobalScope())) {
902 return Cpp::GetNamed(
903 "gROOTLocal",
904 Cpp::GetNamed("Internal", Cpp::GetNamed("ROOT", nullptr)));
905 }
906
907 return nullptr;
908}
909
910bool interop::IsVisibleName(const std::string& name, TCppScope_t parent_scope) {
911 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
912 return Cpp::IsVisibleName(name, parent_scope);
913}
914
916 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
917 return Cpp::GetParentScope(scope);
918}
919
921 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
922 return Cpp::GetScopeFromType(type);
923}
924
926 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
927 return Cpp::GetTypeFromScope(klass);
928}
929
931 // The global scope (the first declaration of the interpreter's translation
932 // unit) never changes, but this is called on every method call that
933 // receives 'self' as its first argument, so avoid the lock and the query.
934 static const TCppScope_t s_global = [] {
935 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
936 return Cpp::GetGlobalScope();
937 }();
938 return s_global;
939}
940
941bool interop::IsTemplate(TCppScope_t handle) { return Cpp::IsTemplate(handle); }
942
944 return Cpp::IsTemplateSpecialization(handle);
945}
946
948 return Cpp::IsTypedefed(handle);
949}
950
951namespace {
952class AutoCastRTTI {
953public:
954 virtual ~AutoCastRTTI() {}
955};
956} // namespace
957
959 TCppObject_t obj) {
960 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
961
962 if (!obj || !Cpp::IsClassPolymorphic(klass))
963 return klass;
964
965 // Skip the std stream hierarchy: autocasting it is not useful, and on MSVC
966 // its virtual inheritance puts the vbptr, not a vfptr, at offset 0, so the
967 // RTTI probe below follows a garbage pointer.
968 static const std::array<TCppScope_t, 3> stream_bases = {
969 interop::GetScope("std::ios_base"), interop::GetScope("std::streambuf"),
970 interop::GetScope("std::wstreambuf")};
971 for (TCppScope_t base : stream_bases)
972 if (base && interop::IsSubclass(klass, base))
973 return klass;
974
975 // This is hit for every polymorphic object handed to Python, and the name
976 // lookups below cost microseconds, so their outcomes are memoized. Keyed on
977 // (static class, dynamic type). Only outcomes that cannot change later are
978 // cached; a failed lookup may succeed once more declarations are available,
979 // so failures are retried.
980 static std::map<std::pair<const void*, const std::type_info*>, TCppScope_t>
981 s_actual_class_cache;
982 const std::type_info* typ = &typeid(*(AutoCastRTTI*)obj.data);
983 if (!typ)
984 return klass;
985 const auto cacheKey = std::make_pair((const void*)klass.data, typ);
986 auto cached = s_actual_class_cache.find(cacheKey);
987 if (cached != s_actual_class_cache.end())
988 return cached->second;
989
990#ifdef _WIN32
991 // MSVC's type_info::name() is already human-readable, but prefixed with
992 // the tag kind ("class TWinNTSystem"), which plain name lookup does not
993 // accept. Strip the prefix by hand; respelling through type resolution
994 // would JIT an uncached declaration per downcast. Template arguments
995 // keep their tags, which is harmless as templated names go through type
996 // resolution rather than plain lookup.
997 std::string demangled_name = typ->name();
998 for (const char* prefix : {"class ", "struct ", "union ", "enum "}) {
999 if (demangled_name.compare(0, strlen(prefix), prefix) == 0) {
1000 demangled_name = demangled_name.substr(strlen(prefix));
1001 break;
1002 }
1003 }
1004#else
1005 std::string mangled_name = typ->name();
1006 std::string demangled_name = Cpp::Demangle(mangled_name);
1007#endif
1008
1009 // A type in an anonymous namespace cannot be named in injected code and
1010 // has no dictionary, so looking it up would fail; worse, its unspellable
1011 // name crashes the interpreter ("(anonymous namespace)" on Itanium,
1012 // "`anonymous namespace'" on MSVC). Keep the base type.
1013 if (demangled_name.find("anonymous namespace") != std::string::npos)
1014 return klass;
1015
1016 if (TCppScope_t scope = interop::GetScope(demangled_name)) {
1017 // A type inside an anonymous namespace cannot be spelled in injected
1018 // code (e.g. the dispatcher's), so it is unusable as a cast target;
1019 // keep the static type. Anonymous namespaces are the ones without a
1020 // name of their own.
1021 for (TCppScope_t p = Cpp::GetParentScope(scope); p;
1022 p = Cpp::GetParentScope(p))
1023 if (Cpp::IsNamespace(p) && Cpp::GetName(p).empty())
1024 return klass;
1025 // Only return the derived type once it has a complete definition. Under
1026 // runtime_cxxmodules=OFF, autoloading the dictionary registers only a
1027 // forward declaration, so force the definition into the AST here. Internal
1028 // classes like TCling have no header and cannot be completed (their
1029 // CXXRecordDecl has no DefinitionData); GetOrForceDefinition returns null
1030 // for them and we fall back to the base type, avoiding a crash when
1031 // querying offsets.
1032 if (Cpp::GetOrForceDefinition(scope)) {
1033 s_actual_class_cache.emplace(cacheKey, scope);
1034 return scope;
1035 }
1036 }
1037
1038 return klass;
1039}
1040
1042 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1043 return Cpp::SizeOf(klass);
1044}
1045
1047 if (!klass)
1048 return 0;
1049 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1050 return Cpp::GetSizeOfType(klass);
1051}
1052
1053bool interop::IsBuiltin(const std::string& type_name) {
1054 static std::set<std::string> s_builtins = {"bool",
1055 "char",
1056 "signed char",
1057 "unsigned char",
1058 "wchar_t",
1059 "short",
1060 "unsigned short",
1061 "int",
1062 "unsigned int",
1063 "long",
1064 "unsigned long",
1065 "long long",
1066 "unsigned long long",
1067 "float",
1068 "double",
1069 "long double",
1070 "void"};
1071 if (s_builtins.find(trim(type_name)) != s_builtins.end())
1072 return true;
1073
1074 if (strstr(type_name.c_str(), "std::complex"))
1075 return true;
1076
1077 return false;
1078}
1079
1080bool interop::IsBuiltin(TCppType_t type) { return Cpp::IsBuiltin(type); }
1081
1083 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1084 return Cpp::IsComplete(scope);
1085}
1086
1087// // memory management
1088// ---------------------------------------------------------
1090 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1091 return Cpp::Allocate(scope, /*count=*/1);
1092}
1093
1095 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1096 Cpp::Deallocate(scope, instance, /*count=*/1);
1097}
1098
1099// Constructors and destructors are resolved (and JIT-compiled) under the
1100// lock and run without it, as WrapperCall does: their bodies are user code.
1102 void* arena /*=nullptr*/) {
1103 std::unique_lock<RInterOpMutex> Lock(InterOpMutex);
1104 if (!Cpp::HasDefaultConstructor(scope))
1105 return nullptr;
1106 Cpp::JitCall JC =
1107 Cpp::MakeFunctionCallable(Cpp::GetDefaultConstructor(scope));
1108 Lock.unlock();
1109 if (!JC)
1110 return nullptr;
1111 void* result = arena;
1112 JC.InvokeConstructor(&result, /*nary=*/1, /*args=*/{},
1113 /*is_arena=*/arena ? reinterpret_cast<void*>(1)
1114 : nullptr);
1115 return result;
1116}
1117
1118static Cpp::JitCall destructor_callable(interop::TCppScope_t scope) {
1119 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1120 return Cpp::MakeFunctionCallable(Cpp::GetDestructor(scope));
1121}
1122
1124 if (Cpp::JitCall JC = destructor_callable(scope))
1125 JC.InvokeDestructor(instance.data, /*nary=*/0, /*withFree=*/true);
1126}
1127
1128static inline bool copy_args(Parameter* args, size_t nargs, void** vargs) {
1129 bool runRelease = false;
1130 for (size_t i = 0; i < nargs; ++i) {
1131 switch (args[i].fTypeCode) {
1132 case 'X': /* (void*)type& with free */
1133 runRelease = true;
1134 case 'V': /* (void*)type& */
1135 vargs[i] = args[i].fValue.fVoidp;
1136 break;
1137 case 'r': /* const type& */
1138 vargs[i] = args[i].fRef;
1139 break;
1140 default: /* all other types in union */
1141 vargs[i] = (void*)&args[i].fValue.fVoidp;
1142 break;
1143 }
1144 }
1145 return runRelease;
1146}
1147
1148static inline void release_args(Parameter* args, size_t nargs) {
1149 for (size_t i = 0; i < nargs; ++i) {
1150 if (args[i].fTypeCode == 'X')
1151 free(args[i].fValue.fVoidp);
1152 }
1153}
1154
1155static inline bool WrapperCall(interop::TCppMethod_t method, size_t nargs,
1156 void* args_, void* self, void* result) {
1157 Parameter* args = (Parameter*)args_;
1158 // bool is_direct = nargs & DIRECT_CALL;
1159 nargs = CALL_NARGS(nargs);
1160
1161 // if (!is_ready(wrap, is_direct))
1162 // return false; // happens with compilation error
1164 if (Cpp::JitCall JC = Cpp::MakeFunctionCallable(method)) {
1166 bool runRelease = false;
1167 // const auto& fgen = /* is_direct ? faceptr.fDirect : */ faceptr;
1168 if (nargs <= cpyrt::SMALL_ARGS_N) {
1169 void* smallbuf[cpyrt::SMALL_ARGS_N];
1170 if (nargs)
1171 runRelease = copy_args(args, nargs, smallbuf);
1172 // CLING_CATCH_UNCAUGHT_
1173 JC.Invoke(result, {smallbuf, nargs}, self);
1174 // _CLING_CATCH_UNCAUGHT
1175 } else {
1176 std::vector<void*> buf(nargs);
1177 runRelease = copy_args(args, nargs, buf.data());
1178 // CLING_CATCH_UNCAUGHT_
1179 JC.Invoke(result, {buf.data(), nargs}, self);
1180 // _CLING_CATCH_UNCAUGHT
1181 }
1182 if (runRelease)
1183 release_args(args, nargs);
1184 return true;
1185 }
1187 return false;
1188}
1189
1190template <typename T>
1192 size_t nargs, void* args) {
1193 T t{};
1194 if (WrapperCall(method, nargs, args, self.data, &t))
1195 return t;
1196 throw std::runtime_error("failed to resolve function");
1197 return (T)-1;
1198}
1199
1200#ifdef PRINT_DEBUG
1201#define _IMP_CALL_PRINT_STMT(type) printf("IMP CALL with type: %s\n", #type);
1202#else
1203#define _IMP_CALL_PRINT_STMT(type)
1204#endif
1205
1206#define CPPJIT_IMP_CALL(typecode, rtype) \
1207 rtype interop::Call##typecode(TCppMethod_t method, TCppObject_t self, \
1208 size_t nargs, void* args) { \
1209 _IMP_CALL_PRINT_STMT(rtype) \
1210 return CallT<rtype>(method, self, nargs, args); \
1211 }
1212
1213void interop::CallV(TCppMethod_t method, TCppObject_t self, size_t nargs,
1214 void* args) {
1215 if (!WrapperCall(method, nargs, args, self.data, nullptr))
1216 return /* TODO ... report error */;
1217}
1218
1219// clang-format off
1220CPPJIT_IMP_CALL(B, unsigned char)
1225CPPJIT_IMP_CALL(LL, long long )
1228CPPJIT_IMP_CALL(LD, long double )
1229// clang-format on
1230
1231void* interop::CallR(TCppMethod_t method, TCppObject_t self, size_t nargs,
1232 void* args) {
1233 void* r = nullptr;
1234 if (WrapperCall(method, nargs, args, self.data, &r))
1235 return r;
1236 return nullptr;
1237}
1238
1239char* interop::CallS(TCppMethod_t method, TCppObject_t self, size_t nargs,
1240 void* args, size_t* length) {
1241 char* cstr = nullptr;
1242 // TClassRef cr("std::string"); // TODO: Why is this required?
1243 std::string* cppresult = (std::string*)malloc(sizeof(std::string));
1244 if (WrapperCall(method, nargs, args, self.data, (void*)cppresult)) {
1245 cstr = cppstring_to_cstring(*cppresult);
1246 *length = cppresult->size();
1247 cppresult->std::string::~basic_string();
1248 } else
1249 *length = 0;
1250 free((void*)cppresult);
1251 return cstr;
1252}
1253
1255 TCppScope_t /*klass*/,
1256 size_t nargs, void* args) {
1257 void* obj = nullptr;
1258 WrapperCall(method, nargs, args, nullptr, &obj);
1259 return (TCppObject_t)obj;
1260}
1261
1263 if (Cpp::JitCall JC = destructor_callable(scope))
1264 JC.InvokeDestructor(self.data, /*nary=*/0, /*withFree=*/false);
1265}
1266
1268 size_t nargs, void* args,
1269 TCppType_t result_type) {
1270 size_t size = interop::SizeOfType(result_type);
1271 if (size == 0)
1272 return TCppObject_t{}; // unsizable return type; the caller reports
1273 void* obj = ::operator new(size);
1274 if (WrapperCall(method, nargs, args, self.data, obj))
1275 return (TCppObject_t)obj;
1276 ::operator delete(obj);
1277 return TCppObject_t{};
1278}
1279
1281 bool /*check_enabled*/) {
1282 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1283 return Cpp::GetFunctionAddress(method);
1284}
1285
1286// handling of function argument buffer --------------------------------------
1288 return new Parameter[nargs];
1289}
1290
1291void interop::DeallocateFunctionArgs(void* args) { delete[] (Parameter*)args; }
1292
1293size_t interop::GetFunctionArgSizeof() { return sizeof(Parameter); }
1294
1296 return offsetof(Parameter, fTypeCode);
1297}
1298
1299// scope reflection information ----------------------------------------------
1301 if (!scope)
1302 return false;
1303
1304 // Test if this scope represents a namespace.
1305 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1306 return Cpp::IsNamespace(scope) || Cpp::GetGlobalScope() == scope;
1307}
1308
1310 // Test if this scope represents a namespace.
1311 return Cpp::IsClass(scope);
1312}
1313//
1315 // Test if this type may not be instantiated.
1316 return Cpp::IsAbstract(scope);
1317}
1318
1319bool interop::IsEnumScope(TCppScope_t scope) { return Cpp::IsEnumScope(scope); }
1320
1322 return Cpp::IsEnumConstant(interop::GetUnderlyingScope(scope));
1323}
1324
1325bool interop::IsEnumType(TCppType_t type) { return Cpp::IsEnumType(type); }
1326
1328 // Test if this type is a "plain old data" type
1329 return Cpp::IsAggregate(type);
1330}
1331
1333 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1334 // Test if this type has a default constructor or is a "plain old data" type
1335 return Cpp::HasDefaultConstructor(scope);
1336}
1337
1338bool interop::IsVariable(TCppScope_t scope) { return Cpp::IsVariable(scope); }
1339
1341 std::set<std::string>& cppnames) {
1342 // Collect all known names of C++ entities under scope. This is useful for
1343 // IDEs employing tab-completion, for example. Note that functions names need
1344 // not be unique as they can be overloaded.
1345 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1346 Cpp::GetAllCppNames(scope, cppnames);
1347}
1348
1349// class reflection information ----------------------------------------------
1350std::vector<interop::TCppScope_t>
1352 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1353 return Cpp::GetUsingNamespaces(scope);
1354}
1355
1356// Normalize a type or scope name to cppyy's canonical form: no space after
1357// the commas separating template arguments, and pointers/references attached
1358// to the type. This is the form cpyrt itself constructs (e.g. when
1359// looking up cached template instantiations by name, see
1360// Utility::ConstructTemplateArgs) and the convention that user code and the
1361// test suite inherited from upstream cppyy; clang's printer instead emits
1362// "a, b", "T *" and "T &".
1363static std::string cppyy_normalize_name(std::string name) {
1364 std::string::size_type pos = 0;
1365 while ((pos = name.find(", ", pos)) != std::string::npos)
1366 name.erase(pos + 1, 1);
1367 pos = 0;
1368 while ((pos = name.find(" *", pos)) != std::string::npos)
1369 name.erase(pos, 1);
1370 pos = 0;
1371 while ((pos = name.find(" &", pos)) != std::string::npos)
1372 name.erase(pos, 1);
1373 return name;
1374}
1375
1376// class reflection information ----------------------------------------------
1378 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1379 return cppyy_normalize_name(
1380 Cpp::GetCompleteName(Cpp::GetUnderlyingScope(klass)));
1381}
1382
1384 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1385 return cppyy_normalize_name(Cpp::GetQualifiedCompleteName(klass));
1386}
1387
1389 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1390 TCppMethod_t func = Cpp::GetDestructor(scope);
1391 return Cpp::IsVirtualMethod(func);
1392}
1393
1395 // Get the total number of base classes that this class has.
1396 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1397 // Autoloading may have registered only a forward declaration of the class;
1398 // complete it, or the proxy is built without its bases.
1399 Cpp::GetOrForceDefinition(klass);
1400 // Cpp::GetNumBases() instantiates a class template specialization with
1401 // diagnostics, printing an error for one that can't be defined, e.g. a
1402 // specialization of a template that is only declared. IsComplete()
1403 // instantiates it silently.
1404 if (!Cpp::IsComplete(klass))
1405 return 0;
1406 return Cpp::GetNumBases(klass);
1407}
1408
1409////////////////////////////////////////////////////////////////////////////////
1410/// \fn interop::TCppIndex_t interop::GetNumBasesLongestBranch(TCppScope_t
1411/// klass) \brief Retrieve number of base classes in the longest branch of the
1412/// inheritance tree of the input class.
1413/// \param[in] klass The class to start the retrieval process from.
1414///
1415/// This is a helper function for interop::GetNumBasesLongestBranch.
1416/// Given an inheritance tree, the function assigns weight 1 to each class that
1417/// has at least one base. Starting from the input class, the function is
1418/// called recursively on all the bases. For each base the return value is one
1419/// (the weight of the base itself) plus the maximum value retrieved for their
1420/// bases in turn. For example, given the following inheritance tree:
1421///
1422/// ~~~{.cpp}
1423/// class A {}; class B: public A {};
1424/// class X {}; class Y: public X {}; class Z: public Y {};
1425/// class C: public B, Z {};
1426/// ~~~
1427///
1428/// calling this function on an instance of `C` will return 3, the steps
1429/// required to go from C to X.
1431 std::vector<size_t> num;
1432 for (TCppIndex_t ibase = 0; ibase < GetNumBases(klass); ++ibase)
1433 num.push_back(
1435 if (num.empty())
1436 return 0;
1437 return *std::max_element(num.begin(), num.end()) + 1;
1438}
1439
1441 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1442 return Cpp::GetName(Cpp::GetBaseClass(klass, ibase));
1443}
1444
1446 TCppIndex_t ibase) {
1447 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1448 return Cpp::GetBaseClass(klass, ibase);
1449}
1450
1452 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1453 // Checked on every method call that receives 'self' as its first argument
1454 // (e.g. from pythonizations and protocol slots), so memoize per class
1455 // pair. A class that is still incomplete can gain bases once its
1456 // definition is loaded, so a negative answer is only cached for complete
1457 // classes.
1458 static std::map<std::pair<const void*, const void*>, bool> s_subclass_cache;
1459 const auto cacheKey = std::make_pair(derived.data, base.data);
1460 auto cached = s_subclass_cache.find(cacheKey);
1461 if (cached != s_subclass_cache.end())
1462 return cached->second;
1463 bool result = Cpp::IsSubclass(derived, base);
1464 if (result || (Cpp::IsComplete(derived) && Cpp::IsComplete(base)))
1465 s_subclass_cache.emplace(cacheKey, result);
1466 return result;
1467}
1468
1469static std::set<std::string> gSmartPtrTypes = {
1470 "std::auto_ptr", "std::shared_ptr", "std::unique_ptr", "std::weak_ptr"};
1471
1473 const std::string& rn = interop::GetScopedFinalName(klass);
1474 if (gSmartPtrTypes.find(rn.substr(0, rn.find("<"))) != gSmartPtrTypes.end())
1475 return true;
1476 return false;
1477}
1478
1480 TCppMethod_t* deref) {
1482 if (!scope || !interop::IsSmartPtr(scope))
1483 return false;
1484
1485 if (!raw && !deref)
1486 return true;
1487
1488 std::vector<TCppMethod_t> ops;
1489 {
1490 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1491 // the operator can only be found once the specialization is instantiated
1492 if (!Cpp::IsComplete(scope))
1493 return false;
1494 Cpp::GetOperator(scope, Cpp::Operator::OP_Arrow, ops,
1495 /*kind=*/Cpp::OperatorArity::kBoth);
1496 if (ops.size() != 1)
1497 return false;
1498
1499 // The dereference operator can be a member template: MSVC's
1500 // std::shared_ptr::operator-> is SFINAE-constrained on the element
1501 // type not being an array. Its return type is dependent, so it has
1502 // to be instantiated (it takes no arguments) before the pointee
1503 // type can be determined - and before it can be called.
1504 if (Cpp::IsTemplatedFunction(ops[0]))
1505 ops[0] = Cpp::BestOverloadFunctionMatch(ops, {}, {});
1506 if (!ops[0])
1507 return false;
1508 }
1509
1510 if (deref)
1511 *deref = ops[0];
1512 if (raw)
1514 return (!deref || *deref) && (!raw || *raw);
1515}
1516
1517// type offsets --------------------------------------------------------------
1519 TCppObject_t address, int direction,
1520 bool rerror) {
1521 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1522
1523 // Up-casts may start from a subobject whose complete type is unknown. For
1524 // down-casts, callers obtain the complete type with GetActualClass, so use
1525 // its static layout; address points to the base rather than the derived type.
1526 const void* object = direction > 0 ? address.data : nullptr;
1527
1528 // Queried for every downcast object handed to Python; computing it walks
1529 // the inheritance paths, so memoize per class pair. Only the static layout
1530 // (no object address) is fixed per pair once both records are complete; an
1531 // offset read from the object's vtable is not cached.
1532 static std::map<std::pair<const void*, const void*>, intptr_t>
1533 s_base_offset_cache;
1534 const auto cacheKey = std::make_pair(derived.data, base.data);
1535 auto cached =
1536 object ? s_base_offset_cache.end() : s_base_offset_cache.find(cacheKey);
1537 intptr_t offset = 0;
1538 if (cached != s_base_offset_cache.end()) {
1539 offset = cached->second;
1540 } else {
1541 // An incomplete record has no walkable bases (GetBaseClassOffset would
1542 // read an empty base path), and the answer may change once the
1543 // definition is loaded, so treat it like the error case below and retry
1544 // next time instead of caching.
1545 if (!Cpp::IsComplete(derived) || !Cpp::IsComplete(base))
1546 return rerror ? (ptrdiff_t)-1 : 0;
1547
1548 offset = Cpp::GetBaseClassOffset(derived, base, object);
1549
1550 if (offset == -1) // Cling error, treat silently
1551 return rerror ? (ptrdiff_t)offset : 0;
1552
1553 if (!object)
1554 s_base_offset_cache.emplace(cacheKey, offset);
1555 }
1556
1557 return (ptrdiff_t)(direction < 0 ? -offset : offset);
1558}
1559
1560// method/function reflection information ------------------------------------
1561// A deleted overload is not callable, and leaving it in the set adds a
1562// spurious conversion error to every failed-call report.
1563static void
1564remove_deleted_methods(std::vector<interop::TCppMethod_t>& methods) {
1565 methods.erase(std::remove_if(methods.begin(), methods.end(),
1567 return Cpp::IsFunctionDeleted(m);
1568 }),
1569 methods.end());
1570}
1571
1573 std::vector<interop::TCppMethod_t>& methods) {
1574 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1575 Cpp::GetClassMethods(scope, methods);
1576 remove_deleted_methods(methods);
1577}
1578
1579std::vector<interop::TCppMethod_t>
1581 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1582 std::vector<interop::TCppMethod_t> methods =
1583 Cpp::GetFunctionsUsingName(scope, name);
1584 remove_deleted_methods(methods);
1585 return methods;
1586}
1587
1588std::string interop::GetName(TCppScope_t method) {
1589 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1590 return Cpp::GetName(method);
1591}
1592
1594 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1595 return cppyy_normalize_name(Cpp::GetCompleteName(method));
1596}
1597
1599 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1600 return Cpp::GetFunctionReturnType(method);
1601}
1602
1604 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1605 TCppType_t ret = Cpp::GetCanonicalType(Cpp::GetFunctionReturnType(method));
1606 // C++ deletes top-level cv-qualifiers on non-class return types from the
1607 // function type ([dcl.fct]); mirror that so name matching sees the plain
1608 // type, e.g. "unsigned long" for `static const size_t size()`. Class
1609 // types keep the qualifier: it stays part of the function type there
1610 // (e.g. for override matching in the dispatcher).
1611 if (ret && !Cpp::IsRecordType(ret))
1612 ret = Cpp::RemoveTypeQualifier(ret,
1613 Cpp::QualKind::Const | Cpp::QualKind::Volatile);
1614 return Cpp::GetTypeAsString(ret);
1615}
1616
1618 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1619 return Cpp::GetFunctionNumArgs(method);
1620}
1621
1623 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1624 return Cpp::GetFunctionRequiredArgs(method);
1625}
1626
1628 if (!method)
1629 return "<unknown>";
1630
1631 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1632 return Cpp::GetFunctionArgName(method, iarg);
1633}
1634
1636 TCppIndex_t iarg) {
1637 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1638 return Cpp::GetFunctionArgType(method, iarg);
1639}
1640
1642 TCppIndex_t iarg) {
1643 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1644 return Cpp::GetTypeAsString(Cpp::RemoveTypeQualifier(
1645 Cpp::GetFunctionArgType(method, iarg), Cpp::QualKind::Const));
1646}
1647
1649 TCppIndex_t iarg) {
1650 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1651 return Cpp::GetTypeAsString(
1652 Cpp::GetCanonicalType(Cpp::GetFunctionArgType(method, iarg)));
1653}
1654
1656 TCppIndex_t iarg) {
1657 if (!method)
1658 return "";
1659
1660 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1661 return Cpp::GetFunctionArgDefault(method, iarg);
1662}
1663
1666 const std::string& /*req_type*/) {
1667 // if (method) {
1668 // TFunction* f = m2f(method);
1669 // TMethodArg* arg = (TMethodArg
1670 // *)f->GetListOfMethodArgs()->At((int)iarg); void *argqtp =
1671 // gInterpreter->TypeInfo_QualTypePtr(arg->GetTypeInfo());
1672
1673 // TypeInfo_t *reqti = gInterpreter->TypeInfo_Factory(req_type.c_str());
1674 // void *reqqtp = gInterpreter->TypeInfo_QualTypePtr(reqti);
1675
1676 // if (ArgSimilarityScore(argqtp, reqqtp) < 10) {
1677 // return ArgSimilarityScore(argqtp, reqqtp);
1678 // }
1679 // else { // Match using underlying types
1680 // if(gInterpreter->IsPointerType(argqtp))
1681 // argqtp =
1682 // gInterpreter->TypeInfo_QualTypePtr(gInterpreter->GetPointerType(argqtp));
1683
1684 // // Handles reference types and strips qualifiers
1685 // TypeInfo_t *arg_ul = gInterpreter->GetNonReferenceType(argqtp);
1686 // TypeInfo_t *req_ul = gInterpreter->GetNonReferenceType(reqqtp);
1687 // argqtp =
1688 // gInterpreter->TypeInfo_QualTypePtr(gInterpreter->GetUnqualifiedType(gInterpreter->TypeInfo_QualTypePtr(arg_ul)));
1689 // reqqtp =
1690 // gInterpreter->TypeInfo_QualTypePtr(gInterpreter->GetUnqualifiedType(gInterpreter->TypeInfo_QualTypePtr(req_ul)));
1691
1692 // return ArgSimilarityScore(argqtp, reqqtp);
1693 // }
1694 // }
1695 return 0; // Method is not valid
1696}
1697
1699 bool show_formal_args,
1700 TCppIndex_t max_args) {
1701 std::ostringstream sig;
1702 sig << "(";
1703 int nArgs = GetMethodNumArgs(method);
1704 if (max_args != (TCppIndex_t)-1)
1705 nArgs = std::min(nArgs, (int)max_args);
1706 for (int iarg = 0; iarg < nArgs; ++iarg) {
1707 sig << cppyy_normalize_name(
1708 interop::GetMethodArgTypeAsString(method, iarg));
1709 if (show_formal_args) {
1710 std::string argname = interop::GetMethodArgName(method, iarg);
1711 if (!argname.empty())
1712 sig << " " << argname;
1713 std::string defvalue = interop::GetMethodArgDefault(method, iarg);
1714 if (!defvalue.empty())
1715 sig << " = " << defvalue;
1716 }
1717 if (iarg != nArgs - 1)
1718 sig << ", ";
1719 }
1720 sig << ")";
1721 return sig.str();
1722}
1723
1725 fn_type = Cpp::IsReferenceType(fn_type) ? Cpp::GetNonReferenceType(fn_type)
1726 : fn_type;
1727 fn_type =
1728 Cpp::IsPointerType(fn_type) ? Cpp::GetPointeeType(fn_type) : fn_type;
1729 TCppScope_t scope = Cpp::GetScopeFromType(fn_type);
1730 std::vector<Cpp::TemplateArgInfo> args;
1731 Cpp::GetClassTemplateArgs(scope, args);
1732 assert(args.size() == 1);
1733 if (args.size() == 1)
1734 return args[0].m_Type;
1735 return nullptr;
1736}
1737
1739 std::vector<TCppType_t>& arg_types) {
1740 if (!fn_type)
1741 return;
1742 fn_type = Cpp::IsReferenceType(fn_type) ? Cpp::GetNonReferenceType(fn_type)
1743 : fn_type;
1744 fn_type =
1745 Cpp::IsPointerType(fn_type) ? Cpp::GetPointeeType(fn_type) : fn_type;
1746 // Cpp::GetFnTypeSignature() requires a function prototype: anything else
1747 // (e.g. a member function pointer, or a plain object type) yields nothing
1748 if (!Cpp::IsFunctionProtoType(fn_type))
1749 return;
1750 Cpp::GetFnTypeSignature(fn_type, arg_types);
1751}
1752
1753bool interop::GetFnTypeSigAsString(TCppType_t fn_type, std::string& ret_type,
1754 std::string& signature) {
1755 // the first entry is the return type, followed by the argument types
1756 std::vector<TCppType_t> types;
1757 GetFnTypeSig(fn_type, types);
1758 if (types.empty())
1759 return false;
1760
1761 ret_type = GetTypeAsString(types[0]);
1762
1763 std::ostringstream sig;
1764 sig << "(";
1765 for (std::size_t iarg = 1; iarg < types.size(); ++iarg) {
1766 if (iarg != 1)
1767 sig << ", ";
1768 sig << GetTypeAsString(types[iarg]);
1769 }
1770 sig << ")";
1771 signature = sig.str();
1772 return true;
1773}
1774
1776 return Cpp::IsSameType(typ1, typ2);
1777}
1778
1780 typ = Cpp::IsReferenceType(typ) ? Cpp::GetNonReferenceType(typ) : typ;
1781 typ = Cpp::IsPointerType(typ) ? Cpp::GetPointeeType(typ) : typ;
1782 return Cpp::IsFunctionProtoType(typ);
1783}
1784
1786 typ1 = Cpp::IsReferenceType(typ1) ? Cpp::GetNonReferenceType(typ1) : typ1;
1787 typ2 = Cpp::IsReferenceType(typ2) ? Cpp::GetNonReferenceType(typ2) : typ2;
1788 typ1 = Cpp::IsPointerType(typ1) ? Cpp::GetPointeeType(typ1) : typ1;
1789 typ2 = Cpp::IsPointerType(typ2) ? Cpp::GetPointeeType(typ2) : typ2;
1790 return Cpp::IsSameType(typ1, typ2);
1791}
1792
1793std::string interop::GetDoxygenComment(TCppScope_t scope, bool strip_markers) {
1794 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1795 return Cpp::GetDoxygenComment(scope, strip_markers);
1796}
1797
1799 if (!method)
1800 return false;
1801 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1802 return Cpp::IsConstMethod(method);
1803}
1804
1806 std::vector<interop::TCppMethod_t>& methods) {
1807 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1808 Cpp::GetFunctionTemplatedDecls(scope, methods);
1809}
1810
1812interop::GetNumTemplatedMethods(TCppScope_t scope, bool /*accept_namespace*/) {
1813 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1814 std::vector<interop::TCppMethod_t> mc;
1815 Cpp::GetFunctionTemplatedDecls(scope, mc);
1816 return mc.size();
1817}
1818
1820 TCppIndex_t imeth) {
1821 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1822 std::vector<interop::TCppMethod_t> mc;
1823 Cpp::GetFunctionTemplatedDecls(scope, mc);
1824
1825 if (imeth < mc.size())
1826 return Cpp::GetName(TCppScope_t(mc[imeth].data));
1827
1828 return "";
1829}
1830
1831bool interop::ExistsMethodTemplate(TCppScope_t scope, const std::string& name) {
1832 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1833 return Cpp::ExistsFunctionTemplate(name, scope);
1834}
1835
1837 return Cpp::IsTemplatedFunction(method);
1838}
1839
1840bool interop::IsStaticTemplate(TCppScope_t scope, const std::string& name) {
1841 std::vector<TCppMethod_t> candidate_methods;
1842 Cpp::GetClassTemplatedMethods(name, scope, candidate_methods);
1843 bool is_static = true;
1844 for (auto i : candidate_methods) {
1845 if (!Cpp::IsStaticMethod(i)) {
1846 is_static = false;
1847 break;
1848 }
1849 }
1850 return is_static;
1851}
1852
1854 const std::string& name,
1855 const std::string& proto) {
1856 std::string pureName;
1857 std::string explicit_params;
1858
1859 if ((name.find("operator<") != 0) && (name.find('<') != std::string::npos)) {
1860 pureName = name.substr(0, name.find('<'));
1861 size_t start = name.find('<');
1862 size_t end = name.rfind('>');
1863 explicit_params = name.substr(start + 1, end - start - 1);
1864 } else {
1865 pureName = name;
1866 }
1867
1868 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1869
1870 std::vector<interop::TCppMethod_t> unresolved_candidate_methods;
1871 Cpp::GetClassTemplatedMethods(pureName, scope, unresolved_candidate_methods);
1872 if (unresolved_candidate_methods.empty() && name.find("operator") == 0) {
1873 // try operators
1874 interop::GetClassOperators(scope, pureName, unresolved_candidate_methods);
1875 }
1876
1877 // cpyrt assumes that we attempt instantiation here
1878 std::vector<Cpp::TemplateArgInfo> arg_types;
1879 std::vector<Cpp::TemplateArgInfo> templ_params;
1880 interop::AppendTypesSlow(proto, arg_types, scope);
1881 interop::AppendTypesSlow(explicit_params, templ_params, scope);
1882 interop::TCppMethod_t cppmeth = nullptr;
1883 cppmeth = Cpp::BestOverloadFunctionMatch(unresolved_candidate_methods,
1884 templ_params, arg_types);
1885
1886 // If overload resolution failed but explicit template arguments were
1887 // supplied, fall back to direct template-argument substitution: ask Sema
1888 // to instantiate each candidate with the explicit args. Sema's SFINAE
1889 // rejects overloads whose substitution fails (e.g. the initializer_list
1890 // form of std::make_any with non-init-list explicit args), so iterating
1891 // gives back exactly the viable specialisation. The wrapper-side argument
1892 // conversion then handles e.g. taking the address of an instance when the
1893 // substituted parameter is a pointer.
1894 if (!cppmeth && !templ_params.empty()) {
1895 for (const auto& cand : unresolved_candidate_methods) {
1896 if (Cpp::DeclRef spec = Cpp::InstantiateTemplate(
1897 TCppScope_t(cand.data), templ_params.data(), templ_params.size(),
1898 /*instantiate_body=*/false)) {
1899 cppmeth = spec.data;
1900 break;
1901 }
1902 }
1903 }
1904
1905 return TCppMethod_t(cppmeth.data);
1906 // if it fails, use Sema to propogate info about why it failed (DeductionInfo)
1907}
1908
1909static inline bool is_basic_string_of(const std::string& n, const char* charT) {
1910 // Match "std::basic_string<charT" whether or not the default template
1911 // arguments (char_traits, allocator) are spelled out; the character type
1912 // must be followed by ',' or '>' so that e.g. "char" does not match
1913 // "char16_t".
1914 std::string prefix = "std::basic_string<";
1915 prefix += charT;
1916 if (n.compare(0, prefix.size(), prefix) != 0)
1917 return false;
1918 return n.size() > prefix.size() &&
1919 (n[prefix.size()] == ',' || n[prefix.size()] == '>');
1920}
1921
1922static inline std::string type_remap(const std::string& n1,
1923 const std::string& n2) {
1924 // Operator lookups of (C++ string, Python str) should succeed for the
1925 // combos of string/str, wstring/str, string/unicode and wstring/unicode;
1926 // since C++ does not have a operator+(std::string, std::wstring), we'll
1927 // have to look up the same type and rely on the converters in
1928 // cpyrt/_cppjit.
1929 if (n1 == "str" || n1 == "unicode" || is_basic_string_of(n1, "char")) {
1930 if (is_basic_string_of(n2, "wchar_t"))
1931 return "std::basic_string<wchar_t>&"; // match like for like
1932 return "std::basic_string<char>&"; // probably best bet
1933 } else if (is_basic_string_of(n1, "wchar_t")) {
1934 return "std::basic_string<wchar_t>&";
1935 } else if (n1 == "complex") {
1936 return "std::complex<double>";
1937 }
1938 return n1;
1939}
1940
1942 const std::string& opname,
1943 std::vector<TCppMethod_t>& operators) {
1944 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
1945 std::string op = opname.substr(8);
1946 Cpp::GetOperator(klass, Cpp::GetOperatorFromSpelling(op), operators,
1947 /*kind=*/Cpp::OperatorArity::kBoth);
1948}
1949
1951 const std::string& lc,
1952 const std::string& rc,
1953 const std::string& opname) {
1954 std::string rc_type = type_remap(rc, lc);
1955 std::string lc_type = type_remap(lc, rc);
1956
1957 std::vector<TCppMethod_t> overloads;
1958 Cpp::GetOperator(scope, Cpp::GetOperatorFromSpelling(opname), overloads,
1959 /*kind=*/Cpp::OperatorArity::kBoth);
1960
1961 // Avoid pushing nullptr into arg_types which would crash
1962 // BestOverloadFunctionMatch when it dereferences each entry's QualType.
1963 auto resolve_arg_type = [](const std::string& name) -> interop::TCppType_t {
1964 if (auto s = interop::GetScope(name))
1965 if (auto t = interop::GetTypeFromScope(s))
1967 return interop::GetType(name, /*enable_slow_lookup=*/true);
1968 };
1969
1970 std::vector<Cpp::TemplateArgInfo> arg_types;
1971 if (auto l = resolve_arg_type(lc_type))
1972 arg_types.emplace_back(l.data);
1973 else
1974 return nullptr;
1975
1976 if (!rc_type.empty()) {
1977 if (auto r = resolve_arg_type(rc_type))
1978 arg_types.emplace_back(r.data);
1979 else
1980 return nullptr;
1981 }
1982 interop::TCppMethod_t cppmeth =
1983 Cpp::BestOverloadFunctionMatch(overloads, {}, arg_types);
1984 if (cppmeth)
1985 return cppmeth;
1986 return nullptr;
1987}
1988
1989// method properties ---------------------------------------------------------
1991 return Cpp::IsFunctionDeleted(method);
1992}
1993
1995 return Cpp::IsPublicMethod(method);
1996}
1997
1999 return Cpp::IsProtectedMethod(method);
2000}
2001
2003 return Cpp::IsPrivateMethod(method);
2004}
2005
2007 return Cpp::IsConstructor(method);
2008}
2009
2011 return Cpp::IsDestructor(method);
2012}
2013
2015 return Cpp::IsStaticMethod(method);
2016}
2017
2019 return Cpp::IsExplicit(method);
2020}
2021
2022// data member reflection information ----------------------------------------
2024 std::vector<TCppScope_t>& datamembers) {
2025 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2026 Cpp::GetDatamembers(scope, datamembers);
2027 Cpp::GetStaticDatamembers(scope, datamembers);
2028 Cpp::GetEnumConstantDatamembers(scope, datamembers, false);
2029}
2030
2031bool interop::CheckDatamember(TCppScope_t scope, const std::string& name) {
2032 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2033 return (bool)Cpp::LookupDatamember(name, scope);
2034}
2035
2037 return Cpp::IsLambdaClass(type);
2038}
2039
2041 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2042 std::ostringstream code;
2043 std::string name = interop::GetFinalName(var);
2044 code << "namespace __cppjit_internal_wrap_g {\n"
2045 << " " << "std::function " << name
2046 << " = ::" << Cpp::GetQualifiedName(var) << ";\n"
2047 << "}\n";
2048
2049 if (interop::Compile(code.str().c_str())) {
2050 TCppScope_t res = Cpp::GetNamed(
2051 name, Cpp::GetScope("__cppjit_internal_wrap_g", /*parent=*/nullptr));
2052 if (res)
2053 return res;
2054 }
2055 return var;
2056}
2057
2060 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2061
2062 std::string fn_name = Cpp::GetQualifiedCompleteName(TCppScope_t(fn.data));
2063 std::string signature = interop::GetMethodSignature(fn, true);
2064
2065 std::ostringstream call;
2066 call << "(";
2067 for (size_t i = 0, n = interop::GetMethodNumArgs(fn); i < n; i++) {
2068 call << interop::GetMethodArgName(fn, i);
2069 if (i != n - 1)
2070 call << ", ";
2071 }
2072 call << ")";
2073
2074 std::ostringstream code;
2075 static int i = 0;
2076 std::string name = "lambda_return_convert_" + std::to_string(++i);
2077 code << "namespace __cppjit_internal_wrap_g {\n"
2078 << "auto " << name << signature << "{" << "return std::function("
2079 << fn_name << call.str() << "); }\n"
2080 << "}\n";
2081 if (interop::Compile(code.str().c_str())) {
2082 TCppScope_t res = Cpp::GetNamed(
2083 name, Cpp::GetScope("__cppjit_internal_wrap_g", /*parent=*/nullptr));
2084 if (res)
2085 return TCppMethod_t(res.data);
2086 }
2087 return fn;
2088}
2089
2091 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2092 return Cpp::GetVariableType(Cpp::GetUnderlyingScope(var));
2093}
2094
2096 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2097 return Cpp::GetTypeAsString(
2098 Cpp::GetVariableType(Cpp::GetUnderlyingScope(scope)));
2099}
2100
2102 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2103 return Cpp::GetTypeAsString(type);
2104}
2105
2107 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2108 return Cpp::GetVariableOffset(Cpp::GetUnderlyingScope(var), klass);
2109}
2110
2111// data member properties ----------------------------------------------------
2113 return Cpp::IsPublicVariable(datamem);
2114}
2115
2117 return Cpp::IsProtectedVariable(datamem);
2118}
2119
2121 return Cpp::IsPrivateVariable(datamem);
2122}
2123
2125 return Cpp::IsStaticVariable(interop::GetUnderlyingScope(var));
2126}
2127
2128bool interop::IsConstVar(TCppScope_t var) { return Cpp::IsConstVariable(var); }
2129
2131 TCppType_t reduce) {
2132 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2133
2134 std::string fn_name = Cpp::GetQualifiedCompleteName(TCppScope_t(fn.data));
2135 std::string signature = interop::GetMethodSignature(fn, true);
2136 std::string result_type = interop::GetTypeAsString(reduce);
2137
2138 std::ostringstream call;
2139 call << "(";
2140 for (size_t i = 0, n = interop::GetMethodNumArgs(fn); i < n; i++) {
2141 call << interop::GetMethodArgName(fn, i);
2142 if (i != n - 1)
2143 call << ", ";
2144 }
2145 call << ")";
2146
2147 std::ostringstream code;
2148 static int i = 0;
2149 std::string name = "reduced_function_" + std::to_string(++i);
2150 code << "namespace __cppjit_internal_wrap_g {\n"
2151 << result_type << " " << name << signature << "{" << "return ("
2152 << result_type << ")::" << fn_name << call.str() << "; }\n"
2153 << "}\n";
2154 if (interop::Compile(code.str().c_str())) {
2155 TCppScope_t res = Cpp::GetNamed(
2156 name, Cpp::GetScope("__cppjit_internal_wrap_g", /*parent=*/nullptr));
2157 if (res)
2158 return TCppMethod_t(res.data);
2159 }
2160 return fn;
2161}
2162
2164 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2165 return Cpp::GetDimensions(type);
2166}
2167
2168// enum properties -----------------------------------------------------------
2169std::vector<interop::TCppScope_t> interop::GetEnumConstants(TCppScope_t scope) {
2170 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2171 return Cpp::GetEnumConstants(scope);
2172}
2173
2175 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2176 return Cpp::GetEnumConstantType(Cpp::GetUnderlyingScope(scope));
2177}
2178
2180 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2181 return Cpp::GetEnumConstantValue(scope);
2182}
2183
2185 Cpp::TemplateArgInfo* args,
2186 size_t args_size) {
2187 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2188 return Cpp::InstantiateTemplate(tmpl, args, args_size,
2189 /*instantiate_body=*/false);
2190}
2191
2193 std::lock_guard<RInterOpMutex> Lock(InterOpMutex);
2194 Cpp::DumpScope(scope);
2195}
free(fBuffer)
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
static Roo_reg_AGKInteg1D instance
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
static void GetTypeAsString(QualType QT, string &type_name, ASTContext &C, PrintingPolicy Policy)
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h length
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:142
R__EXTERN TVirtualMutex * gInterpreterMutex
#define gROOT
Definition TROOT.h:417
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
std::recursive_mutex fLocal
static thread_local std::vector< TVirtualMutex * > fGlobalLocked
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2999
static const TString & GetEtcDir()
Get the sysconfig directory in the installation. Static utility function.
Definition TROOT.cxx:3396
const char * Data() const
Definition TString.h:385
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
Definition TSystem.cxx:1318
char * DynamicPathName(const char *lib, Bool_t quiet=kFALSE)
Find a dynamic library called lib using the system search paths.
Definition TSystem.cxx:2042
This class implements a mutex interface.
virtual Int_t UnLock()=0
virtual Int_t Lock()=0
TLine * line
std::string trim(const std::string &line)
static std::string type_remap(const std::string &n1, const std::string &n2)
static bool is_identifier(std::string_view s)
static bool copy_args(Parameter *args, size_t nargs, void **vargs)
interop::TCppType_t int_like_type(interop::TCppType_t type)
static std::set< std::string > gSmartPtrTypes
interop::TCppScope_t GetEnumFromCompleteName(const std::string &name)
static char * cppstring_to_cstring(const std::string &cppstr)
static void release_args(Parameter *args, size_t nargs)
static bool WrapperCall(interop::TCppMethod_t method, size_t nargs, void *args_, void *self, void *result)
static std::set< std::string > g_builtins
cppjit::cpyrt::Parameter Parameter
static bool is_basic_string_of(const std::string &n, const char *charT)
static bool gEnableFastPath
static T CallT(interop::TCppMethod_t method, interop::TCppObject_t self, size_t nargs, void *args)
static void remove_deleted_methods(std::vector< interop::TCppMethod_t > &methods)
#define CPPJIT_IMP_CALL(typecode, rtype)
static Cpp::JitCall destructor_callable(interop::TCppScope_t scope)
bool split_comma_saparated_types(const std::string &name, std::vector< std::string > &types)
static size_t CALL_NARGS(size_t nargs)
int LoadCppInterOp()
static std::string cppyy_normalize_name(std::string name)
RInterOpMutex InterOpMutex
const Int_t n
Definition legend1.C:16
TROOT * GetROOT()
Definition TROOT.cxx:550
const int SMALL_ARGS_N
Definition callcontext.h:14
bool IsConstVar(TCppScope_t var)
Cpp::TypeRef TCppType_t
Definition API.h:42
TCppScope_t GetFullScope(const std::string &scope_name)
void GetDatamembers(TCppScope_t scope, std::vector< TCppScope_t > &datamembers)
Cpp::ObjectRef TCppObject_t
Definition API.h:43
TCppType_t GetTypeFromScope(TCppScope_t klass)
std::string GetTypeAsString(TCppType_t type)
TCppType_t GetDatamemberType(TCppScope_t data)
TCppType_t ResolveEnumPointerType(TCppType_t type)
bool ExistsMethodTemplate(TCppScope_t scope, const std::string &name)
bool IsFunctionType(TCppType_t typ)
TCppScope_t WrapLambdaFromVariable(TCppScope_t var)
void DeallocateFunctionArgs(void *args)
bool IsBuiltin(const std::string &type_name)
void CallV(TCppMethod_t method, TCppObject_t self, size_t nargs, void *args)
std::string GetName(TCppScope_t)
std::string ToString(TCppScope_t klass, TCppObject_t obj)
bool IsClassType(TCppType_t type)
void CallDestructor(TCppScope_t type, TCppObject_t self)
bool CheckDatamember(TCppScope_t scope, const std::string &name)
TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string &name, const std::string &proto)
bool IsTemplateInstantiation(TCppScope_t scope)
TCppMethod_t AdaptFunctionForLambdaReturn(TCppMethod_t fn)
bool IsStaticTemplate(TCppScope_t scope, const std::string &name)
bool IsConstructor(TCppMethod_t method)
std::string GetMethodArgCanonTypeAsString(TCppMethod_t method, TCppIndex_t iarg)
bool HasVirtualDestructor(TCppScope_t type)
TCppIndex_t GetNumTemplatedMethods(TCppScope_t scope, bool accept_namespace=false)
TCppScope_t GetActualClass(TCppScope_t klass, TCppObject_t obj)
void GetTemplatedMethods(TCppScope_t scope, std::vector< TCppMethod_t > &methods)
std::string ResolveName(const std::string &cppitem_name)
std::string GetMethodArgTypeAsString(TCppMethod_t method, TCppIndex_t iarg)
TCppIndex_t GetMethodReqArgs(TCppMethod_t)
bool AppendTypesSlow(const std::string &name, std::vector< Cpp::TemplateArgInfo > &types, TCppScope_t parent=nullptr)
bool IsAbstract(TCppScope_t scope)
TCppScope_t GetParentScope(TCppScope_t scope)
std::string GetMethodReturnTypeAsString(TCppMethod_t)
TCppType_t GetReferencedType(TCppType_t type, bool rvalue=false)
TCppIndex_t GetMethodNumArgs(TCppMethod_t)
bool IsStaticDatamember(TCppScope_t var)
bool IsProtectedMethod(TCppMethod_t method)
Cpp::InterpRef TInterp_t
bool IsClass(TCppScope_t scope)
bool GetFnTypeSigAsString(TCppType_t fn_type, std::string &ret_type, std::string &signature)
Spells out a function type, or a pointer or reference to one, as the return type and the parenthesize...
void Destruct(TCppScope_t scope, TCppObject_t instance)
TCppIndex_t GetNumBases(TCppScope_t klass)
TCppIndex_t CompareMethodArgType(TCppMethod_t, TCppIndex_t iarg, const std::string &req_type)
bool IsSubclass(TCppScope_t derived, TCppScope_t base)
TCppScope_t GetNamed(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
intptr_t GetDatamemberOffset(TCppScope_t var, TCppScope_t klass=nullptr)
size_t SizeOfType(TCppType_t type)
Cpp::FuncRef TCppMethod_t
Definition API.h:44
bool IsPublicData(TCppScope_t var)
TCppType_t GetMethodArgType(TCppMethod_t, TCppIndex_t iarg)
TCppScope_t GetBaseScope(TCppScope_t klass, TCppIndex_t ibase)
bool IsIntegerType(TCppType_t type, bool *is_signed=nullptr)
bool IsFunctionPointerType(TCppType_t type)
bool IsTypedefed(TCppScope_t scope)
bool IsPrivateMethod(TCppMethod_t method)
TCppObject_t Allocate(TCppScope_t scope)
bool IsEnumScope(TCppScope_t scope)
bool IsSimilarFnTypes(TCppType_t typ1, TCppType_t typ2)
void GetClassMethods(TCppScope_t scope, std::vector< TCppMethod_t > &methods)
TCppMethod_t GetGlobalOperator(TCppScope_t scope, const std::string &lc, const std::string &rc, const std::string &op)
bool GetSmartPtrInfo(TCppScope_t klass, TCppScope_t *raw, TCppMethod_t *deref)
TCppMethod_t ReduceReturnType(TCppMethod_t fn, TCppType_t reduce)
void * AllocateFunctionArgs(size_t nargs)
void GetClassOperators(TCppScope_t klass, const std::string &opname, std::vector< TCppMethod_t > &operators)
TCppObject_t CallO(TCppMethod_t method, TCppObject_t self, size_t nargs, void *args, TCppType_t result_type)
bool IsPointerType(TCppType_t type)
std::string GetMethodArgName(TCppMethod_t, TCppIndex_t iarg)
void GetFnTypeSig(TCppType_t fn_type, std::vector< TCppType_t > &arg_types)
TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
long long GetEnumDataValue(TCppScope_t scope)
size_t SizeOf(TCppScope_t klass)
std::string GetDoxygenComment(TCppScope_t scope, bool strip_markers=true)
TCppType_t GetMethodReturnType(TCppMethod_t)
TCppType_t ResolveType(TCppType_t cppitem_name)
TCppScope_t GetUnderlyingScope(TCppScope_t scope)
std::string GetMethodSignature(TCppMethod_t, bool show_formal_args, TCppIndex_t max_args=(TCppIndex_t) -1)
bool IsExplicit(TCppMethod_t method)
TCppScope_t GetGlobalScope()
std::string GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth)
bool IsVisibleName(const std::string &name, TCppScope_t parent_scope=TCppScope_t{})
std::string GetFullName(TCppScope_t)
TCppScope_t GetTypeScope(TCppScope_t klass)
bool IsTemplate(TCppScope_t scope)
bool IsDestructor(TCppMethod_t method)
std::vector< TCppScope_t > GetEnumConstants(TCppScope_t scope)
bool Compile(const std::string &code, bool silent=false)
TCppType_t GetPointerType(TCppType_t type)
std::vector< TCppMethod_t > GetMethodsFromName(TCppScope_t scope, const std::string &name)
std::string ResolveEnum(TCppScope_t enum_scope)
size_t TCppIndex_t
Definition API.h:45
bool IsDefaultConstructable(TCppScope_t scope)
TCppObject_t CallConstructor(TCppMethod_t method, TCppScope_t klass, size_t nargs, void *args)
bool IsDeletedMethod(TCppMethod_t method)
bool IsLValueReferenceType(TCppType_t type)
bool IsNamespace(TCppScope_t scope)
bool IsTemplatedMethod(TCppMethod_t method)
void DumpScope(TCppScope_t scope)
TCppScope_t GetScopeFromType(TCppType_t type)
TCppType_t GetFnTypeFromStdFn(TCppType_t fn_type)
std::string GetBaseName(TCppScope_t klass, TCppIndex_t ibase)
TCppType_t GetRealType(TCppType_t type)
TCppIndex_t GetNumBasesLongestBranch(TCppScope_t klass)
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
TCppType_t GetEnumConstantType(TCppScope_t scope)
bool IsSameType(TCppType_t typ1, TCppType_t typ2)
bool IsEnumConstant(TCppScope_t scope)
std::string GetFinalName(TCppScope_t type)
bool IsProtectedData(TCppScope_t var)
bool IsLambdaClass(TCppType_t type)
bool IsConstMethod(TCppMethod_t)
ptrdiff_t GetBaseOffset(TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror=false)
TCppObject_t Construct(TCppScope_t scope, void *arena=nullptr)
bool IsPrivateData(TCppScope_t var)
void Deallocate(TCppScope_t scope, TCppObject_t instance)
void * TCppFuncAddr_t
Definition API.h:46
bool IsMutablePtrRefType(TCppType_t type)
bool IsVariable(TCppScope_t scope)
bool IsSmartPtr(TCppScope_t klass)
std::string GetMethodArgDefault(TCppMethod_t, TCppIndex_t iarg)
bool IsStaticMethod(TCppMethod_t method)
char * CallS(TCppMethod_t method, TCppObject_t self, size_t nargs, void *args, size_t *length)
bool IsRValueReferenceType(TCppType_t type)
TCppType_t GetComplexType(const std::string &element_type)
std::vector< TCppScope_t > GetUsingNamespaces(TCppScope_t)
bool IsPublicMethod(TCppMethod_t method)
TCppType_t GetType(const std::string &name, bool enable_slow_lookup=false)
bool IsAggregate(TCppScope_t type)
std::string GetScopedFinalName(TCppScope_t type)
void GetAllCppNames(TCppScope_t scope, std::set< std::string > &cppnames)
size_t GetFunctionArgTypeoffset()
std::string GetDatamemberTypeAsString(TCppScope_t var)
TCppType_t ResolveEnumReferenceType(TCppType_t type)
Cpp::DeclRef TCppScope_t
Definition API.h:41
std::vector< long int > GetDimensions(TCppType_t type)
bool IsEnumType(TCppType_t type)
bool IsComplete(TCppScope_t type)
TCppScope_t InstantiateTemplate(TCppScope_t tmpl, Cpp::TemplateArgInfo *args, size_t args_size)
Definition API.cxx:34
#define RPY_EXPORTED
union cppjit::cpyrt::Parameter::Value fValue
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4