genns.cpp 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542
  1. #include "genns.hpp"
  2. #include "fs.hpp"
  3. #include "function.hpp"
  4. #include "genbase.hpp"
  5. #include <boost/algorithm/string/classification.hpp>
  6. #include <boost/algorithm/string/join.hpp>
  7. #include <boost/algorithm/string/split.hpp>
  8. #include <boost/property_tree/xml_parser.hpp>
  9. #include <iostream>
  10. #include <ostream>
  11. #include <sstream>
  12. #include <tuple>
  13. namespace
  14. {
  15. class File : public std::ofstream
  16. {
  17. std::string ns;
  18. std::string fname;
  19. bool preamble;
  20. bool guard;
  21. NamespaceGuard nsg;
  22. static std::string root;
  23. public:
  24. // ok, single threaded processing
  25. static void set_root(const std::string &dir) { root = dir; }
  26. static std::string prepdirs(const std::string &_ns, const std::string &_fname)
  27. {
  28. fs::path p(root);
  29. p /= tolower(_ns);
  30. fs::create_directories(p);
  31. p /= tolower(_fname);
  32. return p.native();
  33. }
  34. File(const std::string &_ns, const std::string _fname, bool _need_ns = true,
  35. bool _need_guard = true)
  36. : std::ofstream(prepdirs(_ns, _fname)), ns(_ns), fname(_fname),
  37. preamble(_need_ns), guard(_need_guard), nsg(*this)
  38. {
  39. write_pre();
  40. }
  41. std::string get_rel_path() const
  42. {
  43. return (fs::path(tolower(ns)) / tolower(fname)).native();
  44. }
  45. void write_pre()
  46. {
  47. *this << "// AUTO-GENERATED\n\n";
  48. auto definc = fmt::format("_GI_{}_{}_", toupper(ns), toupper(fname));
  49. for (auto &v : definc)
  50. if (!isalnum(v))
  51. v = '_';
  52. if (guard) {
  53. *this << "#ifndef " << definc << std::endl;
  54. *this << "#define " << definc << std::endl;
  55. *this << std::endl;
  56. }
  57. if (preamble)
  58. nsg.push(ns);
  59. }
  60. void write_post()
  61. {
  62. if (preamble)
  63. nsg.pop();
  64. if (guard)
  65. *this << "#endif" << std::endl;
  66. }
  67. ~File() { write_post(); }
  68. };
  69. std::string File::root;
  70. class NamespaceGeneratorImpl : private GeneratorBase, public NamespaceGenerator
  71. {
  72. std::string version_;
  73. std::vector<std::string> deps_;
  74. pt::ptree root_;
  75. pt::ptree tree_;
  76. // helper exp
  77. std::regex re_unqualify_;
  78. bool allow_deprecated_{};
  79. public:
  80. NamespaceGeneratorImpl(GeneratorContext &ctx, const std::string &filename)
  81. : GeneratorBase(ctx, "")
  82. {
  83. logger(Log::INFO, "reading {}", filename);
  84. pt::read_xml(filename, root_);
  85. // extract dependencies
  86. for (auto &&n : root_.get_child("repository")) {
  87. if (n.first == "include") {
  88. auto &&name = get_name(n.second);
  89. auto &&version = get_attribute(n.second, AT_VERSION);
  90. deps_.emplace_back(name + "-" + version);
  91. logger(Log::INFO, indent + "dependency " + deps_.back());
  92. }
  93. }
  94. // all else is in a subtree
  95. tree_ = root_.get_child("repository.namespace");
  96. ns = get_name(tree_);
  97. version_ = get_attribute(tree_, AT_VERSION);
  98. re_unqualify_ =
  99. std::regex(fmt::format("^{}(::|\\.)", ns), std::regex::optimize);
  100. }
  101. std::string get_ns() const { return ns + "-" + version_; }
  102. std::vector<std::string> get_dependencies() const { return deps_; }
  103. private:
  104. // in most cases no problems arise if both M1.SomeName and M2.SomeOtherName
  105. // both map to GSomeName
  106. // but some generated code, like declare_XXX template specialization assumes
  107. // a 1-to-1 mapping, as should be the case in with sane GIR
  108. // however, if the latter are somehow not sane, then duplicate definitions
  109. // might arise (ODR and all that), although it *really is* a GIR bug
  110. //
  111. // so try to mitigate that here
  112. // check that only 1 (qualified) girname/cppname claims/maps to a ctype
  113. void check_odr(const std::string &cpptype, const std::string &ctype) const
  114. {
  115. // verify this ctype has not been seen before
  116. // a particular ctype can only be defined/claimed by one GIR symbol
  117. // (in one module), otherwise lots of things will go wrong
  118. // GIRs also have an "alias" concept, so that should be used instead
  119. auto conflict = ctx.repo.check_odr(cpptype, ctype);
  120. if (!conflict.empty()) {
  121. throw std::runtime_error(
  122. fmt::format("{} maps to {}, already claimed by {};\n"
  123. "Please verify/fix GIRs and/or add ignore as needed.",
  124. cpptype, ctype, conflict));
  125. }
  126. }
  127. std::string make_declare(bool decl_ctype, const std::string &cpptype,
  128. const std::string &ctype) const
  129. {
  130. check_odr(cpptype, ctype);
  131. return fmt::format(
  132. "template<> struct declare_{}_of<{}>\n{{ typedef {} type; }}; ",
  133. (decl_ctype ? "ctype" : "cpptype"), (decl_ctype ? cpptype : ctype),
  134. (decl_ctype ? ctype : cpptype));
  135. }
  136. void process_element_alias(const pt::ptree &node, std::ostream &out) const
  137. {
  138. std::ostringstream oss;
  139. auto name = get_name(node);
  140. // skip some stuff
  141. if (name.find("autoptr") != name.npos)
  142. throw skip("autoptr", skip::OK);
  143. auto ctype = get_attribute(node, AT_CTYPE);
  144. std::string deftype;
  145. TypeInfo tinfo;
  146. auto tnode = node.get_child(EL_TYPE);
  147. auto btype = get_name(tnode);
  148. parse_typeinfo(btype, tinfo);
  149. if (tinfo.flags & TYPE_BASIC) {
  150. // if typedef refers to a basic type, let's refer here to (global
  151. // scope) C type (this is mostly the case, e.g. GstClockTime, etc)
  152. deftype = GI_SCOPE + ctype;
  153. } else if (tinfo.flags) {
  154. assert(tinfo.cpptype.size());
  155. // otherwise we alias to the corresponding wrapped type
  156. // (e.g. GtkAllocation)
  157. deftype = tinfo.cpptype;
  158. }
  159. auto aliasfmt = "typedef {} {};\n";
  160. if (deftype.size())
  161. out << fmt::format(aliasfmt, deftype, name);
  162. // also mind ref type
  163. if (tinfo.flags & TYPE_BOXED) {
  164. assert(deftype.size());
  165. out << fmt::format(
  166. aliasfmt, deftype + GI_SUFFIX_REF, name + GI_SUFFIX_REF);
  167. }
  168. out << std::endl;
  169. }
  170. void process_element_enum(const pt::ptree::value_type &n, std::ostream &out,
  171. std::ostream *_out_impl) const
  172. {
  173. auto &kind = n.first;
  174. auto &node = n.second;
  175. std::ostringstream oss;
  176. auto name = get_name(node);
  177. auto ctype = get_attribute(node, AT_CTYPE);
  178. if (_out_impl) {
  179. auto &out_impl = *_out_impl;
  180. // need to generate namespaced function members
  181. std::ostringstream oss_decl;
  182. std::ostringstream oss_impl;
  183. std::set<std::string> dummy;
  184. for (const auto &n : node) {
  185. if (n.first == EL_FUNCTION) {
  186. process_element_function(n, oss_decl, oss_impl, "", "", dummy);
  187. }
  188. }
  189. if (oss_decl.tellp()) {
  190. auto enumns = name + "NS_";
  191. NamespaceGuard ns_d(out);
  192. ns_d.push(enumns, false);
  193. out << oss_decl.str();
  194. NamespaceGuard ns_i(out_impl);
  195. ns_i.push(enumns, false);
  196. out_impl << oss_impl.str();
  197. }
  198. return;
  199. }
  200. // enum name might end up matching (uppercase) define
  201. name = unreserve(name);
  202. // otherwise generate enum
  203. NamespaceGuard nsg(oss);
  204. nsg.push(ns);
  205. // auto-adjust underlying type to avoid warnings;
  206. // outside the range of underlying type ‘int’
  207. // (no forward declare of enum in C, so type should be complete)
  208. oss << fmt::format(
  209. "enum class {} : std::underlying_type<{}>::type {{\n", name, ctype);
  210. std::set<std::string> names;
  211. for (const auto &n : node) {
  212. if (n.first == EL_MEMBER) {
  213. auto value = get_attribute(n.second, AT_CIDENTIFIER);
  214. auto name = get_name(n.second);
  215. // some enums/flags declare different names with same value
  216. // which seems to confuse gobject-introspection
  217. // (e.g. GstVideoFrameFlags, GstVideoBufferFlags, etc)
  218. // and then they end up with repeated/duplicate name in GIR
  219. // so let's only pick the first one of those
  220. if (!std::get<1>(names.insert(name))) {
  221. logger(Log::WARNING, "{} skipping duplicate {}", ctype, value);
  222. continue;
  223. }
  224. oss << indent
  225. << fmt::format("{} = {},\n", unreserve(toupper(name)), value);
  226. }
  227. }
  228. oss << "};\n\n";
  229. // add operators in case of flag = bitfield
  230. if (kind == EL_FLAGS)
  231. oss << fmt::format("GI_FLAG_OPERATORS({})\n\n", name);
  232. nsg.pop();
  233. // declare ctype info
  234. nsg.push(GI_REPOSITORY_NS);
  235. name = ns + "::" + name;
  236. oss << make_declare(true, name, ctype) << std::endl;
  237. oss << make_declare(false, name, ctype) << std::endl;
  238. oss << std::endl;
  239. // declare gtype info
  240. auto gtype = get_attribute(node, AT_GLIB_GET_TYPE, "");
  241. if (gtype.size()) {
  242. oss << fmt::format("template<> struct declare_gtype_of<{}>\n"
  243. "{{ static GType get_type() {{ return {}(); }} }};",
  244. name, gtype)
  245. << std::endl;
  246. oss << std::endl;
  247. }
  248. // in case of a flag = bitfield, also mark it as such
  249. if (kind == EL_FLAGS) {
  250. oss << fmt::format(
  251. "template<> struct is_bitfield<{}> : public std::true_type\n"
  252. "{{}};",
  253. name)
  254. << std::endl;
  255. oss << std::endl;
  256. }
  257. oss << std::endl;
  258. nsg.pop();
  259. out << oss.str() << std::endl;
  260. }
  261. void process_element_const(const pt::ptree &node, std::ostream &out) const
  262. {
  263. auto name = get_name(node);
  264. std::ostringstream oss;
  265. try {
  266. ArgInfo tinfo = parse_arginfo(node);
  267. auto cpptype = tinfo.cpptype;
  268. auto value = get_attribute(node, AT_CTYPE);
  269. std::string cast;
  270. std::string namesuffix;
  271. if (tinfo.flags & TYPE_BASIC) {
  272. if (tinfo.flags & TYPE_CLASS) {
  273. // special string case
  274. // need some suffix to make the variable (pointer) const
  275. cpptype = "gchar";
  276. namesuffix = "[]";
  277. } else {
  278. // normalize back to C (e.g. char*)
  279. cpptype = tinfo.ctype;
  280. }
  281. } else {
  282. // need to cast to enum type (flags/enum)
  283. if (tinfo.flags & TYPE_ENUM) {
  284. cast = fmt::format("({}) ", cpptype);
  285. } else if (!(tinfo.flags & TYPE_VALUE)) {
  286. throw skip("constant type " + cpptype + " not supported");
  287. }
  288. // accept alias typedef
  289. }
  290. // avoid clashes with existing defines
  291. name = unreserve(name);
  292. // NOTE what about an inline var in C++17
  293. // recall that const implies static in C++
  294. oss << fmt::format(
  295. "const {} {}{} = {}{};", cpptype, name, namesuffix, cast, value);
  296. } catch (skip &ex) {
  297. oss << "// SKIP constant " << name << "; " << ex.what();
  298. }
  299. out << oss.str() << std::endl << std::endl;
  300. }
  301. void process_element_property(const pt::ptree::value_type &entry,
  302. std::ostream &out, std::ostream &impl, const std::string &klass,
  303. std::set<std::string> &deps) const
  304. {
  305. auto &node = entry.second;
  306. ArgInfo tinfo;
  307. auto name = get_name(node);
  308. auto read = get_attribute<int>(node, AT_READABLE, 1);
  309. auto write = get_attribute<int>(node, AT_WRITABLE, 1);
  310. tinfo = parse_arginfo(node);
  311. auto &&cpptype = tinfo.cpptype;
  312. if (!tinfo.flags)
  313. throw skip("unknown type " + cpptype);
  314. // let's not go here for now
  315. if (tinfo.flags & TYPE_CONTAINER)
  316. throw skip("container property not supported", skip::TODO);
  317. track_dependency(deps, tinfo);
  318. // directly write all in decl
  319. auto &oss = out;
  320. (void)impl;
  321. auto decl_name = name;
  322. std::replace(decl_name.begin(), decl_name.end(), '-', '_');
  323. std::string proptype = "property_proxy";
  324. if (!read) {
  325. proptype = "property_proxy_write";
  326. } else if (!write) {
  327. proptype = "property_proxy_read";
  328. }
  329. proptype = GI_NS_SCOPED + proptype;
  330. auto qklass = qualify(klass, TYPE_OBJECT);
  331. if (write)
  332. oss << fmt::format("{3}<{0}, {4}> property_{2}()\n"
  333. "{{ return {3}<{0}, {4}> (*this, \"{1}\"); }}",
  334. cpptype, name, decl_name, proptype, qklass)
  335. << std::endl;
  336. if (read)
  337. oss << fmt::format("const {3}<{0}, {4}> property_{2}() const\n"
  338. "{{ return {3}<{0}, {4}> (*this, \"{1}\"); }}",
  339. cpptype, name, decl_name, proptype, qklass)
  340. << std::endl;
  341. oss << std::endl;
  342. }
  343. void process_element_field(const pt::ptree::value_type &entry,
  344. std::ostream &out, std::ostream &impl, const std::string &klass,
  345. const std::string &klasstype, std::set<std::string> &deps) const
  346. {
  347. auto &node = entry.second;
  348. auto name = get_name(node);
  349. auto readable = get_attribute<int>(node, AT_READABLE, 1);
  350. auto writable = get_attribute<int>(node, AT_WRITABLE, 1);
  351. auto priv = get_attribute<int>(node, AT_PRIVATE, 0);
  352. ArgInfo tinfo;
  353. try {
  354. tinfo = parse_arginfo(node);
  355. // only very plain basic fields
  356. // type must be known
  357. // no private
  358. // no int* cookies or other strange things
  359. if (!tinfo.flags || priv ||
  360. get_pointer_depth(tinfo.ctype) != tinfo.pdepth ||
  361. is_volatile(tinfo.ctype))
  362. return;
  363. } catch (...) {
  364. // simply fail silently here and never mind
  365. return;
  366. }
  367. if (!(tinfo.flags & (TYPE_VALUE | TYPE_CLASS)))
  368. return;
  369. // unconditionally reserve this to avoid clash with non-field member
  370. ElementFunction func;
  371. func.kind = EL_METHOD;
  372. func.name = unreserve(name, true);
  373. func.c_id = klasstype + "::" + name;
  374. Parameter instance;
  375. // real one, no base
  376. parse_typeinfo(klasstype, instance.tinfo);
  377. instance.instance = true;
  378. instance.name = "obj";
  379. instance.direction = DIR_IN;
  380. // field parameter
  381. Parameter param;
  382. param.tinfo = tinfo;
  383. param.name = "_value";
  384. param.transfer = TRANSFER_NOTHING;
  385. // common helper lambda for below
  386. auto make_wrapper = [&](const std::string &funcname,
  387. const std::string funcdef,
  388. const std::vector<Parameter> &params) {
  389. func.functionexp = funcname;
  390. // buffer result
  391. std::ostringstream oss;
  392. auto def = ::process_element_function(
  393. ctx, ns, func, params, out, oss, klass, klasstype, deps);
  394. // now write all if wrapping accessor did not fail
  395. if (def.name.size())
  396. impl << funcdef << std::endl << oss.str();
  397. };
  398. // a static helper is used in the following,
  399. // rather than a compact local lambda,
  400. // as the latter does not always cast to any function type
  401. // also, internal casts are applied in the helper to handle enum/int
  402. // and pointer type conversions
  403. // also, the ctype info is derived from the cpptype as with func
  404. // argument due to either
  405. // + ctype missing, e.g. enum,
  406. // + or minor discrepancies, e.g. gpointer vs gconstpointer
  407. if (readable) {
  408. param.direction = DIR_RETURN;
  409. param.tinfo.ctype = make_ctype(param.tinfo, param.direction, false);
  410. instance.tinfo.ctype = fmt::format("const {}*", instance.tinfo.dtype);
  411. // define helper function
  412. auto funcname = "_field_" + name + "_get";
  413. auto funcdef =
  414. fmt::format("static {} {} ({} {}) {{ return ({}) obj->{}; }}",
  415. param.tinfo.ctype, funcname, instance.tinfo.ctype, instance.name,
  416. param.tinfo.ctype, name);
  417. make_wrapper(funcname, funcdef, {param, instance});
  418. }
  419. // not safe to assume any particular ownership of some struct field
  420. // so let's not meddle with it other than the very basic cases
  421. if (writable && (tinfo.flags & TYPE_VALUE)) {
  422. param.direction = DIR_IN;
  423. param.tinfo.ctype = make_ctype(param.tinfo, param.direction, false);
  424. instance.tinfo.ctype = instance.tinfo.dtype + "*";
  425. // define helper function
  426. auto funcname = "_field_" + name + "_set";
  427. auto funcdef = fmt::format("static void {} ({} {}, {} {}) {{ "
  428. "obj->{} = (decltype(obj->{})) {}; }}",
  429. funcname, instance.tinfo.ctype, instance.name, param.tinfo.ctype,
  430. param.name, name, name, param.name);
  431. // void return
  432. Parameter vparam;
  433. parse_typeinfo(GIR_VOID, vparam.tinfo);
  434. vparam.direction = DIR_RETURN;
  435. make_wrapper(funcname, funcdef, {vparam, param, instance});
  436. }
  437. }
  438. FunctionDefinition process_element_function(
  439. const pt::ptree::value_type &entry, std::ostream &out, std::ostream &impl,
  440. const std::string &klass, const std::string &klasstype,
  441. std::set<std::string> &deps) const
  442. {
  443. return ::process_element_function(
  444. ctx, ns, entry, out, impl, klass, klasstype, deps, allow_deprecated_);
  445. }
  446. // unqualify (current ns qualifed) type
  447. std::string unqualify(const std::string &name) const
  448. {
  449. return std::regex_replace(name, re_unqualify_, "");
  450. }
  451. static std::string get_record_filename(const std::string &rname, bool impl)
  452. {
  453. return tolower(rname) + (impl ? "_impl" : "") + ".hpp";
  454. }
  455. static std::string make_include(const std::string &hname, bool local)
  456. {
  457. if (hname.empty())
  458. return hname;
  459. char open = local ? '"' : '<';
  460. char close = local ? '"' : '>';
  461. std::ostringstream oss;
  462. oss << "#include " << open << hname << close;
  463. return oss.str();
  464. }
  465. // make include for a qualified dependency
  466. // (not in current namespace)
  467. std::string make_dep_include(const std::string &girname) const
  468. {
  469. auto lname = unqualify(girname);
  470. return lname.size() && !is_qualified(lname)
  471. ? make_include(get_record_filename(lname, false), true) + '\n'
  472. : EMPTY;
  473. }
  474. std::string make_dep_declare(const std::set<std::string> &deps) const
  475. {
  476. std::ostringstream oss;
  477. for (const auto &d : deps) {
  478. auto c = unqualify(d);
  479. if (!is_qualified(c))
  480. oss << "class " << c << ";" << std::endl;
  481. }
  482. return oss.str();
  483. }
  484. static std::string make_conditional_include(
  485. const std::string &hname, bool local)
  486. {
  487. if (hname.empty())
  488. return hname;
  489. auto templ = R"|(
  490. #if defined(__has_include)
  491. #if __has_include({}{}{})
  492. #include {}{}{}
  493. #endif
  494. #endif
  495. )|";
  496. char open = local ? '"' : '<';
  497. char close = local ? '"' : '>';
  498. return fmt::format(templ, open, hname, close, open, hname, close);
  499. }
  500. static constexpr const char *const CLASS_PLACEHOLDER{"CLASS_PLACEHOLDER"};
  501. // minor convencience helper type
  502. struct TypeClassInfo
  503. {
  504. // (optionally) gir qualified
  505. std::string parentgir;
  506. // info on type-struct
  507. TypeInfo ti;
  508. };
  509. TypeClassInfo collect_type_class_info(const std::string &girname) const
  510. {
  511. // class struct is skipped above,
  512. // but we do look for it here to find *Class/*Interface struct
  513. TypeClassInfo result;
  514. auto &repo = ctx.repo;
  515. auto &node = repo.tree(girname).second;
  516. result.parentgir = get_attribute(node, AT_PARENT, "");
  517. // NOTE parent might be in different ns
  518. if (result.parentgir.size())
  519. result.parentgir = repo.qualify(result.parentgir, girname);
  520. auto cpptype = get_attribute(node, AT_GLIB_TYPE_STRUCT, "");
  521. if (cpptype.empty())
  522. throw skip(girname + " missing type-struct info");
  523. cpptype = repo.qualify(cpptype, girname);
  524. parse_typeinfo(cpptype, result.ti);
  525. // more useful in this setting
  526. result.ti.cpptype = unqualify(result.ti.cpptype);
  527. if (result.ti.dtype.empty())
  528. throw skip(girname + " missing C type-struct info");
  529. return result;
  530. }
  531. typedef std::vector<std::pair<std::string, FunctionDefinition>>
  532. VirtualMethods;
  533. void process_element_record_class(const pt::ptree::value_type &entry,
  534. const std::vector<TypeInfo> &interfaces, const std::string &decl,
  535. const std::string &impl, const VirtualMethods &methods,
  536. std::ostream &out_decl, std::ostream &out_impl) const
  537. {
  538. auto &node = entry.second;
  539. auto name = get_name(node);
  540. bool interface = (entry.first == EL_INTERFACE);
  541. // run up parent hierarchy to check if all those can be properly
  542. // generated
  543. auto class_info = collect_type_class_info(name);
  544. TypeClassInfo parent_class_info;
  545. // no parent for interface
  546. if (class_info.parentgir.size()) {
  547. parent_class_info = collect_type_class_info(class_info.parentgir);
  548. auto rec_info = parent_class_info;
  549. while (rec_info.parentgir.size())
  550. rec_info = collect_type_class_info(rec_info.parentgir);
  551. }
  552. // collect ok interfaces
  553. std::vector<TypeInfo> itfs_info;
  554. for (auto &&itf : interfaces) {
  555. try {
  556. itfs_info.emplace_back(collect_type_class_info(itf.girname).ti);
  557. // base class needs to be declared
  558. out_decl << make_dep_include(itf.girname);
  559. } catch (...) {
  560. }
  561. }
  562. out_decl << std::endl;
  563. // put into inner namespace to avoid name clash
  564. // declaration part
  565. NamespaceGuard ns_decl(out_decl);
  566. ns_decl.push(ns);
  567. ns_decl.push(GI_NS_IMPL);
  568. ns_decl.push(GI_NS_INTERNAL);
  569. // class definition
  570. // explicitly specify a non-public non-virtual destructor
  571. auto def_templ = R"|(
  572. class {0}
  573. {{
  574. typedef {0} self;
  575. public:
  576. typedef {1} instance_type;
  577. typedef {2} {3}_type;
  578. {6}
  579. struct TypeInitData;
  580. protected:
  581. {5} ~{0}() = default;
  582. static {5} void {3}_init (gpointer {3}_struct, gpointer );
  583. {4}
  584. }};
  585. )|";
  586. std::string kind = interface ? "interface" : "class";
  587. const auto klassnamedef = class_info.ti.cpptype + "Def";
  588. // used for interfaces
  589. const std::string suffix_class_impl = "ClassImpl";
  590. const auto klassname =
  591. class_info.ti.cpptype + (interface ? suffix_class_impl : "");
  592. // conflict check and type init lists
  593. std::string conflict_check, type_init, type_init_calc;
  594. conflict_check.reserve(1024);
  595. type_init.reserve(1024);
  596. type_init_calc.reserve(1024);
  597. std::string calc_indent = indent + indent;
  598. for (auto &&method : methods) {
  599. auto &&n = method.first;
  600. conflict_check +=
  601. fmt::format("using GI_MEMBER_CHECK_CONFLICT({}) = self;\n", n);
  602. type_init +=
  603. fmt::format("{}GI_MEMBER_DEFINE({}, {})\n", indent, klassname, n);
  604. type_init_calc +=
  605. fmt::format("{}{}GI_MEMBER_HAS_DEFINITION(SubClass, DefData, {})",
  606. type_init_calc.empty() ? "" : ",\n", calc_indent, n);
  607. }
  608. // (note that e.g. xlib cases prefix might not help, or ns interference)
  609. // drop inline mark on pure virtual interface functions;
  610. // does not quite make sense and might otherwise lead to compiler
  611. // warnings
  612. static const std::regex re_inline(GI_INLINE + ' ', std::regex::optimize);
  613. out_decl << fmt::format(def_templ, klassnamedef, qualify(name, TYPE_OBJECT),
  614. class_info.ti.dtype, kind, std::regex_replace(decl, re_inline, ""),
  615. GI_INLINE, conflict_check);
  616. if (interface)
  617. out_decl << fmt::format("using {}Impl = detail::InterfaceImpl<{}>;", name,
  618. klassnamedef)
  619. << std::endl;
  620. // avoid ambiguous unqualified name lookup of friend class below
  621. // (as the scopes of parent classes are involved then as well)
  622. auto class_templ = R"|(
  623. class {0}: public {1}
  624. {{
  625. friend class internal::{4};
  626. typedef {0} self;
  627. typedef {1} super;
  628. protected:
  629. using super::super;
  630. {2}
  631. {3}
  632. }};
  633. )|";
  634. auto classextra = R"|(
  635. private:
  636. // make local helpers private
  637. using super::get_struct_;
  638. using super::gobj_;
  639. protected:
  640. // disambiguation helper types
  641. {}
  642. )|";
  643. // qualification helper; ensure impl::internal qualified
  644. // (also adds current ns if needed)
  645. auto implqualify = [&](const std::string &cpptype) {
  646. auto result = cpptype;
  647. auto pos = result.find(GI_SCOPE.c_str());
  648. auto insert = GI_SCOPE + GI_NS_IMPL + GI_SCOPE + GI_NS_INTERNAL;
  649. if (pos != result.npos) {
  650. result.insert(pos, insert);
  651. } else {
  652. insert = ns + insert + GI_SCOPE;
  653. result.insert(0, insert);
  654. }
  655. return result;
  656. };
  657. // collect interface types and helper types
  658. std::ostringstream oss_types;
  659. std::vector<std::string> itfs;
  660. for (auto &&itf : itfs_info) {
  661. auto kname = itf.cpptype + suffix_class_impl;
  662. kname = implqualify(kname);
  663. itfs.push_back(kname);
  664. // fall back to ctype as it needs to be fully qualified
  665. static const std::regex re_descope(GI_SCOPE, std::regex::optimize);
  666. auto tprefix = std::regex_replace(itf.dtype, re_descope, "");
  667. oss_types << fmt::format("typedef {} {}_type;\n", kname, tprefix);
  668. }
  669. auto extra = interface ? EMPTY : fmt::format(classextra, oss_types.str());
  670. // determine baseclasses
  671. // qualified klassnamedef to avoid ambiguous parent class lookup
  672. // (when used as a typedef within class)
  673. std::string superclass =
  674. interface ? fmt::format("detail::InterfaceClassImpl<{}>", name + "Impl")
  675. : fmt::format("detail::ClassTemplate<{}, {}{}{}>",
  676. implqualify(klassnamedef),
  677. implqualify(parent_class_info.ti.cpptype),
  678. itfs.size() ? ", " : "",
  679. boost::algorithm::join(itfs, ", "));
  680. static const std::regex re_virtual("virtual ", std::regex::optimize);
  681. static const std::regex re_pure("= 0", std::regex::optimize);
  682. auto idecl = std::regex_replace(decl, re_virtual, "");
  683. idecl = std::regex_replace(idecl, re_pure, "override");
  684. // add macro for optional warning suppression
  685. if (!interface)
  686. out_decl << GI_CLASS_IMPL_BEGIN << std::endl << std::endl;
  687. out_decl << fmt::format(
  688. class_templ, klassname, superclass, extra, idecl, klassnamedef);
  689. // type init
  690. // at this later stage as it contains template definitions
  691. // that refer to the above class
  692. auto type_init_templ = R"|(
  693. struct {0}::TypeInitData
  694. {{
  695. {1}
  696. template<typename SubClass>
  697. constexpr static TypeInitData factory()
  698. {{
  699. {3}using DefData = detail::DefinitionData<SubClass, TypeInitData>;
  700. return {{
  701. {2}
  702. }};
  703. }}
  704. }};
  705. )|";
  706. out_decl << fmt::format(type_init_templ, klassnamedef, type_init,
  707. type_init_calc, methods.empty() ? "// " : "");
  708. // end internal ns
  709. ns_decl.pop(1);
  710. if (!interface)
  711. out_decl << GI_CLASS_IMPL_END << std::endl
  712. << std::endl
  713. << fmt::format("using {}Impl = detail::ObjectImpl<{}, {}::{}>;",
  714. name, name, GI_NS_INTERNAL, klassname)
  715. << std::endl
  716. << std::endl;
  717. // implementation part
  718. NamespaceGuard ns_impl(out_impl);
  719. ns_impl.push(ns);
  720. ns_impl.push(GI_NS_IMPL);
  721. ns_impl.push(GI_NS_INTERNAL);
  722. auto &ctype = class_info.ti.dtype;
  723. out_impl << fmt::format(
  724. "void {}::{}_init (gpointer {}_struct, gpointer factory)\n{{\n",
  725. klassnamedef, kind, kind);
  726. out_impl << indent
  727. << fmt::format(
  728. "{} *methods = ({} *) {}_struct;\n", ctype, ctype, kind);
  729. // avoid warning if no methods
  730. out_impl << indent << "(void) methods;" << std::endl << std::endl;
  731. // init data from factory
  732. out_impl
  733. << indent
  734. << "auto init_data = GI_MEMBER_INIT_DATA(TypeInitData, factory);\n";
  735. out_impl << indent << "(void) init_data;" << std::endl << std::endl;
  736. for (auto &&method : methods) {
  737. auto &&n = method.first;
  738. auto &&def = method.second;
  739. std::vector<std::string> args, transfers;
  740. for (auto &&arg : def.cpp_decl)
  741. args.push_back(arg.second);
  742. auto cpp_return =
  743. def.cpp_outputs.size() ? def.cpp_outputs[0].type : CPP_VOID;
  744. auto sig = fmt::format(
  745. "{} (*) ({})", cpp_return, boost::algorithm::join(args, ", "));
  746. std::string transferargs, precheck;
  747. if (def.arg_traits.empty()) {
  748. // only arrange to call the raw methods
  749. // if new-style detection (or explicit specification) is in place
  750. precheck = " && factory";
  751. transferargs = "std::nullptr_t";
  752. } else {
  753. transferargs = make_arg_traits(def.arg_traits, def.c_sig);
  754. }
  755. // add a hard cast to deal with const differences (e.g. string vs
  756. // const char*)
  757. out_impl << indent
  758. << fmt::format("if (init_data.{0}{3}) methods->{0} = (decltype "
  759. "(methods->{0})) "
  760. "detail::method_wrapper<self, {1}, "
  761. "{2}>::wrapper<&self::{0}_>;",
  762. n, sig, transferargs, precheck)
  763. << std::endl;
  764. }
  765. out_impl << "}" << std::endl << std::endl;
  766. auto iimpl =
  767. std::regex_replace(impl, std::regex(CLASS_PLACEHOLDER), klassname);
  768. out_impl << iimpl;
  769. }
  770. std::vector<TypeInfo> record_collect_interfaces(
  771. const pt::ptree::value_type &entry, const TypeInfo &current,
  772. const TypeInfo &parent, std::set<std::string> &deps) const
  773. {
  774. auto &repo = ctx.repo;
  775. auto &node = entry.second;
  776. auto name = get_name(node);
  777. // listed interfaces also include parent's interfaces
  778. // so we should subtract those for good measure
  779. auto collect_interfaces = [&](const pt::ptree &node,
  780. const std::string &basename) {
  781. std::set<std::string> result;
  782. auto p = node.equal_range(EL_IMPLEMENTS);
  783. assert(basename.size());
  784. for (auto &q = p.first; q != p.second; ++q) {
  785. auto n = get_name(q->second, std::nothrow);
  786. if (n.size()) {
  787. result.insert(repo.qualify(n, basename));
  788. }
  789. }
  790. return result;
  791. };
  792. std::vector<std::string> itfs;
  793. // ensure all GIR names fully qualified
  794. auto itfs_local = collect_interfaces(node, current.girname);
  795. if (parent.girname.size()) {
  796. // qualify relative to parent
  797. auto itfs_parent =
  798. collect_interfaces(repo.tree(parent.girname).second, parent.girname);
  799. // only keep those not matching a parent's interface (both fully
  800. // qualified)
  801. for (auto &&n : itfs_local) {
  802. if (itfs_parent.find(n) == itfs_parent.end())
  803. itfs.push_back(n);
  804. }
  805. } else {
  806. std::copy(itfs_local.begin(), itfs_local.end(), std::back_inserter(itfs));
  807. }
  808. std::vector<TypeInfo> interfaces;
  809. for (auto &n : itfs) {
  810. TypeInfo itf;
  811. parse_typeinfo(n, itf);
  812. // should know about this interface by now
  813. // but let's continue anyway
  814. if (!itf.flags) {
  815. logger(Log::WARNING, "{} implements unknown interface {}", name, n);
  816. continue;
  817. }
  818. deps.insert(itf.cpptype);
  819. interfaces.push_back(itf);
  820. }
  821. return interfaces;
  822. }
  823. // returns (decl header name, impl header name)
  824. std::tuple<std::string, std::string> process_element_record(
  825. const pt::ptree::value_type &entry, bool onlyverify) const
  826. {
  827. auto &kind = entry.first;
  828. auto &node = entry.second;
  829. auto name = get_name(node);
  830. logger(Log::LOG, "checking {} {} {}", kind, name, onlyverify);
  831. TypeInfo current;
  832. parse_typeinfo(name, current);
  833. // only consider real type, but keep info around
  834. if (!current.flags)
  835. return std::make_tuple("", "");
  836. auto &ctype = current.dtype;
  837. // could happen for a GType only case (e.g. GstFraction)
  838. if (!ctype.size())
  839. throw skip("no c:type info");
  840. bool is_object_base = (current.girname == GIR_GOBJECT);
  841. // check parent
  842. TypeInfo parent;
  843. auto &repo = ctx.repo;
  844. // 1 special case here
  845. if (kind == EL_OBJECT && !is_object_base) {
  846. auto parentgir = get_attribute(node, AT_PARENT, "");
  847. if (parentgir.empty())
  848. throw skip("missing parent class");
  849. parse_typeinfo(parentgir, parent);
  850. if (!parent.flags)
  851. throw skip("unknown parent " + parentgir);
  852. // parent might be found, but might have problems of its own
  853. // so that needs to be checked recursively
  854. // (at least if within current ns)
  855. if (onlyverify && !is_qualified(parentgir)) {
  856. try {
  857. process_element_record(repo.tree(parentgir), onlyverify);
  858. } catch (const skip &ex) {
  859. throw skip(std::string("parent problem; ") + ex.what());
  860. }
  861. }
  862. }
  863. if (onlyverify || is_object_base)
  864. return std::make_tuple("", "");
  865. // no throwing or giving up after this point
  866. // we have now committed to coming up with a class
  867. // declaration/definition (even if it is a pretty empty one)
  868. logger(Log::LOG, "processing {} {}", kind, name);
  869. // generate classes in subnamespace
  870. const std::string nsbase("base");
  871. auto namebase = name + "Base";
  872. // avoid qualification later on with standard ns (e.g. Gst)
  873. auto qnamebase = nsbase + "::" + namebase;
  874. // collect output of members and their dependencies
  875. std::ostringstream oss_decl, oss_class_decl;
  876. std::ostringstream oss_impl, oss_class_impl;
  877. VirtualMethods vmethods;
  878. std::set<std::string> deps;
  879. for (const auto &n : node) {
  880. auto el = n.first;
  881. int introspectable = get_attribute<int>(n.second, AT_INTROSPECTABLE, 1);
  882. try {
  883. // try even if marked introspectable
  884. // some are useful in non-runtime setting
  885. if (el == EL_FUNCTION || el == EL_CONSTRUCTOR || el == EL_METHOD ||
  886. el == EL_SIGNAL) {
  887. process_element_function(
  888. n, oss_decl, oss_impl, qnamebase, name, deps);
  889. } else if (el == EL_VIRTUAL_METHOD && introspectable &&
  890. ctx.options.classimpl) {
  891. // placeholder replaced suitably later on
  892. auto def = process_element_function(
  893. n, oss_class_decl, oss_class_impl, CLASS_PLACEHOLDER, name, deps);
  894. if (def.name.size())
  895. vmethods.push_back({def.name, def});
  896. } else if (el == EL_FIELD && introspectable) {
  897. process_element_field(n, oss_decl, oss_impl, qnamebase, name, deps);
  898. } else if (el == EL_PROPERTY && introspectable) {
  899. process_element_property(n, oss_decl, oss_impl, qnamebase, deps);
  900. }
  901. } catch (std::runtime_error &ex) {
  902. handle_exception(n, ex);
  903. }
  904. }
  905. // actual output to file
  906. auto fname_decl = get_record_filename(name, false);
  907. File out_decl(ns, fname_decl, false);
  908. auto fname_impl = get_record_filename(name, true);
  909. File out_impl(ns, fname_impl, false);
  910. // a superclass needs full declaration (not only forward declaration)
  911. out_decl << make_dep_include(parent.girname);
  912. out_decl << std::endl;
  913. // implemented interfaces are also dependency
  914. std::vector<TypeInfo> interfaces =
  915. record_collect_interfaces(entry, current, parent, deps);
  916. // namespace in decl before forward class decl
  917. NamespaceGuard ns_decl(out_decl);
  918. ns_decl.push(ns);
  919. // all declarations are included prior to implementation
  920. // forward class declarations in declaration
  921. deps.erase(current.cpptype);
  922. out_decl << make_dep_declare(deps);
  923. out_decl << std::endl;
  924. // also forward declare 'oneself' since only base is defined below
  925. out_decl << "class " << name << ";" << std::endl;
  926. out_decl << std::endl;
  927. // namespace in impl following includes
  928. NamespaceGuard ns_impl(out_impl);
  929. ns_impl.push(ns);
  930. // base class subnamespace
  931. ns_decl.push(nsbase);
  932. ns_impl.push(nsbase);
  933. // class definition
  934. auto obj_templ = R"|(
  935. #define {3} {4}::{0}
  936. class {0} : public {2}
  937. {{
  938. typedef {2} super_type;
  939. public:
  940. typedef {1} BaseObjectType;
  941. {0} (std::nullptr_t = nullptr) : super_type() {{}}
  942. BaseObjectType *gobj_() {{ return (BaseObjectType*) super_type::gobj_(); }}
  943. const BaseObjectType *gobj_() const {{ return (const BaseObjectType*) super_type::gobj_(); }}
  944. BaseObjectType *gobj_copy_() const {{ return (BaseObjectType*) super_type::gobj_copy_(); }}
  945. )|";
  946. auto boxed_templ = R"|(
  947. #define {2} {3}::{0}
  948. class {0} : public {1}
  949. {{
  950. typedef {1} super_type;
  951. public:
  952. {0} (std::nullptr_t = nullptr) : super_type() {{}}
  953. )|";
  954. auto gtype = get_attribute(node, AT_GLIB_GET_TYPE, "");
  955. auto basedef = toupper(fmt::format("GI_{}_{}_BASE", ns, name));
  956. // class definition
  957. bool is_variant = false;
  958. if (kind == EL_OBJECT) {
  959. out_decl << fmt::format(
  960. obj_templ, namebase, ctype, parent.cpptype, basedef, nsbase);
  961. } else if (kind == EL_INTERFACE) {
  962. out_decl << fmt::format(
  963. obj_templ, namebase, ctype, "gi::InterfaceBase", basedef, nsbase);
  964. } else {
  965. auto tmpl = GI_NS_DETAIL_SCOPED +
  966. (gtype.size() ? "GBoxedWrapperBase" : "CBoxedWrapperBase");
  967. auto parent = fmt::format("{}<{}, {}>", tmpl, namebase, ctype);
  968. // override if special fundamental boxed-like case
  969. if ((is_variant = (current.girname == GIR_GVARIANT))) {
  970. parent = "detail::VariantWrapper";
  971. gtype.clear();
  972. }
  973. out_decl << fmt::format(boxed_templ, namebase, parent, basedef, nsbase);
  974. }
  975. if (gtype.size()) {
  976. out_decl << fmt::format(
  977. "static GType get_type_ () G_GNUC_CONST {{ return {}(); }} ", gtype);
  978. out_decl << std::endl << std::endl;
  979. }
  980. // add some helpers to obtain known implemented interfaces
  981. for (auto &&_itf : interfaces) {
  982. // temp extend for wrapping below
  983. ArgInfo itf;
  984. (TypeInfo &)itf = _itf;
  985. auto decl_fmt = "{0} {1}interface_ (gi::interface_tag<{0}>)";
  986. auto impl_fmt = decl_fmt;
  987. auto decl = fmt::format(decl_fmt, itf.cpptype, "");
  988. out_decl << GI_INLINE << ' ' << decl << ";" << std::endl << std::endl;
  989. // wrap a properly casted extra reference (ensure no sink'ing)
  990. auto impl = fmt::format(impl_fmt, itf.cpptype, (namebase + "::"));
  991. out_impl << impl << std::endl;
  992. auto towrap =
  993. fmt::format("({}::BaseObjectType*) gobj_copy_()", itf.cpptype);
  994. auto w =
  995. fmt::format(make_wrap_format(ArgInfo{itf}, TRANSFER_FULL), towrap);
  996. out_impl << fmt::format("{{ return {}; }}", w) << std::endl << std::endl;
  997. // conversion operator
  998. auto op_decl = fmt::format("operator {} ()", itf.cpptype);
  999. auto op_impl = fmt::format(
  1000. "{}::{}\n{{ return interface_ (gi::interface_tag<{}>()); }}",
  1001. namebase, op_decl, itf.cpptype);
  1002. out_decl << GI_INLINE << ' ' << op_decl << ';' << std::endl << std::endl;
  1003. out_impl << op_impl << std::endl << std::endl;
  1004. }
  1005. out_decl << oss_decl.str();
  1006. out_decl << "}; // class" << std::endl;
  1007. out_decl << std::endl;
  1008. ns_decl.pop();
  1009. out_impl << oss_impl.str();
  1010. out_impl << std::endl;
  1011. ns_impl.pop();
  1012. // optionally include supplements/overrides for this class
  1013. auto include_extra = [&](File &out, bool impl) {
  1014. for (auto &&suffix : {"_extra_def", "_extra"}) {
  1015. auto header =
  1016. (fs::path(tolower(ns)) / get_record_filename(name + suffix, impl))
  1017. .native();
  1018. out << make_conditional_include(header, false) << std::endl;
  1019. }
  1020. };
  1021. include_extra(out_decl, false);
  1022. include_extra(out_impl, true);
  1023. ns_decl.push(GI_REPOSITORY_NS);
  1024. // make fragment to define final class
  1025. {
  1026. NamespaceGuard nst(out_decl);
  1027. nst.push(ns, false);
  1028. auto supertype = basedef;
  1029. auto reftype = name + GI_SUFFIX_REF;
  1030. if (kind != EL_OBJECT && kind != EL_INTERFACE && !is_variant) {
  1031. // boxed base templates define a few members with CppType return type
  1032. // so in the CppTypeBase defined above that is still CppTypeBase
  1033. // again add the template as subclass but now with final CppType
  1034. auto tmpl = GI_NS_DETAIL_SCOPED +
  1035. (gtype.size() ? "GBoxedWrapper" : "CBoxedWrapper");
  1036. supertype = fmt::format(
  1037. "{}<{}, {}, {}, {}>", tmpl, name, ctype, basedef, reftype);
  1038. // forward declaration of corresponding ref type
  1039. out_decl << fmt::format("class {};", reftype) << std::endl << std::endl;
  1040. }
  1041. auto fmtclass =
  1042. "class {0} : public {1}\n"
  1043. "{{ typedef {1} super_type; using super_type::super_type; }};\n";
  1044. out_decl << fmt::format(fmtclass, name, supertype) << std::endl;
  1045. // in case of boxed, also define ref type
  1046. if (kind != EL_OBJECT && kind != EL_INTERFACE && !is_variant) {
  1047. auto tmpl = GI_NS_DETAIL_SCOPED +
  1048. (gtype.size() ? "GBoxedRefWrapper" : "CBoxedRefWrapper");
  1049. supertype = fmt::format("{}<{}, {}, {}>", tmpl, name, ctype, basedef);
  1050. out_decl << std::endl
  1051. << fmt::format(fmtclass, reftype, supertype) << std::endl;
  1052. }
  1053. }
  1054. // GInitiallyUnowned is typedef'ed to GObject
  1055. // so we have to avoid duplicate definition
  1056. // FIXME generally no way to check for that using Gir, provide some
  1057. // override here ??
  1058. if (current.girname != GIR_GINITIALLYUNOWNED) {
  1059. // declare type info
  1060. out_decl << make_declare(false, ns + "::" + name, ctype) << std::endl;
  1061. out_decl << std::endl;
  1062. }
  1063. ns_decl.pop();
  1064. // now process the virtual method class parts
  1065. if (ctx.options.classimpl && (kind == EL_OBJECT || kind == EL_INTERFACE)) {
  1066. try {
  1067. process_element_record_class(entry, interfaces, oss_class_decl.str(),
  1068. oss_class_impl.str(), vmethods, out_decl, out_impl);
  1069. } catch (const skip &ex) {
  1070. logger(Log::WARNING, "skipping class generation for {}; {}", name,
  1071. ex.what());
  1072. }
  1073. }
  1074. return std::make_tuple(fname_decl, fname_impl);
  1075. }
  1076. std::string handle_exception(
  1077. const pt::ptree::value_type &n, const std::runtime_error &ex) const
  1078. {
  1079. auto &el = n.first;
  1080. const auto &name = get_name(n.second, std::nothrow);
  1081. auto ex_skip = dynamic_cast<const skip *>(&ex);
  1082. if (!ex_skip || ex_skip->cause == skip::INVALID) {
  1083. auto msg = fmt::format("{} {} {}; {}",
  1084. (ex_skip ? "skipping" : "EXCEPTION processing"), el, name, ex.what());
  1085. Log level = ex_skip ? Log::WARNING : Log::ERROR;
  1086. if (check_suppression(ns, el, name))
  1087. level = Log::DEBUG;
  1088. logger(level, msg);
  1089. } else {
  1090. logger(Log::DEBUG, "discarding {} {}; {}", el, name, ex.what());
  1091. }
  1092. return name;
  1093. }
  1094. typedef std::function<void(const pt::ptree::value_type &)> entry_processor;
  1095. void process_entries(const pt::ptree &node, const entry_processor &proc)
  1096. {
  1097. for (auto it = node.begin(); it != node.end(); ++it) {
  1098. auto &&n = *it;
  1099. if (n.first == PT_ATTR)
  1100. continue;
  1101. try {
  1102. proc(n);
  1103. } catch (std::runtime_error &ex) {
  1104. auto name = handle_exception(n, ex);
  1105. // in any case give up on this name
  1106. ctx.repo.discard(name);
  1107. }
  1108. }
  1109. }
  1110. bool visit_ok(const pt::ptree::value_type &n) const
  1111. {
  1112. const auto &girname = get_name(n.second, std::nothrow);
  1113. auto ninfo = ctx.repo.lookup(girname);
  1114. if (ninfo && !(ninfo->info && (ninfo->info->flags & TYPE_PREDEFINED))) {
  1115. logger(Log::LOG, "visiting {} {}", n.first, girname);
  1116. return true;
  1117. } else {
  1118. return false;
  1119. }
  1120. }
  1121. const char *process_libs()
  1122. {
  1123. // also find needed shared libraries in case of dlopen
  1124. auto sl = get_attribute(tree_, AT_SHARED_LIBRARY, "");
  1125. std::vector<std::string> shlibs, qshlibs;
  1126. boost::split(shlibs, sl, boost::is_any_of(","));
  1127. for (auto &l : shlibs)
  1128. if (l.size())
  1129. qshlibs.push_back(fmt::format("\"{}\"", l));
  1130. auto h_libs = "_libs.hpp";
  1131. File libs(ns, h_libs);
  1132. auto libs_templ = R"|(
  1133. namespace internal {{
  1134. GI_INLINE_DECL std::vector<const char*> _libs()
  1135. {{ return {{{0}}}; }}
  1136. }} // internal
  1137. )|";
  1138. libs << fmt::format(libs_templ, boost::join(qshlibs, ","));
  1139. return h_libs;
  1140. }
  1141. public:
  1142. std::string process_tree(const std::vector<std::string> &dep_headers)
  1143. {
  1144. logger(Log::INFO, "processing namespace {} {}", ns, version_);
  1145. // set state for ns processing
  1146. ctx.repo.set_ns(ns);
  1147. File::set_root(ctx.options.rootdir);
  1148. // check if deprecated should pass for this ns
  1149. allow_deprecated_ = ctx.match_ignore.matches("deprecated", ns, {version_});
  1150. // optionally process libs
  1151. auto h_libs = ctx.options.dl ? process_libs() : "";
  1152. auto h_types = "_types.hpp";
  1153. File types(ns, h_types);
  1154. // index run
  1155. // collect info by name
  1156. // also gather alias/type info
  1157. entry_processor proc_index = [&](const pt::ptree::value_type &n) {
  1158. const auto &name = get_name(n.second, std::nothrow);
  1159. auto deprecated = get_attribute<int>(n.second, AT_DEPRECATED, 0);
  1160. if (deprecated && !allow_deprecated_)
  1161. return;
  1162. auto &el = n.first;
  1163. // redirect to oblivion
  1164. // empty name might originate from a glib:boxed with glib:name attribute
  1165. // discard those as well, as that is a glib type without C-type
  1166. // (which are not really useful in our situation)
  1167. if (name.empty() || ctx.match_ignore.matches(ns, el, {name})) {
  1168. logger(Log::INFO, "ignoring {} {}", el, name);
  1169. } else {
  1170. ctx.repo.add(name, n);
  1171. if (n.first == EL_ALIAS && visit_ok(n)) {
  1172. process_element_alias(n.second, types);
  1173. }
  1174. }
  1175. };
  1176. process_entries(tree_, proc_index);
  1177. // process basic types and callbacks
  1178. auto h_enums = "_enums.hpp";
  1179. auto h_flags = "_flags.hpp";
  1180. auto h_constants = "_constants.hpp";
  1181. auto h_callbacks = "_callbacks.hpp";
  1182. auto h_callbacks_impl = "_callbacks_impl.hpp";
  1183. auto h_functions = "_functions.hpp";
  1184. auto h_functions_impl = "_functions_impl.hpp";
  1185. File enums(ns, h_enums, false);
  1186. File flags(ns, h_flags, false);
  1187. File constants(ns, h_constants);
  1188. File callbacks(ns, h_callbacks);
  1189. File callbacks_impl(ns, h_callbacks_impl);
  1190. File functions(ns, h_functions);
  1191. File functions_impl(ns, h_functions_impl);
  1192. entry_processor proc_pass_1 = [&](const pt::ptree::value_type &n) {
  1193. auto &el = n.first;
  1194. if (el == EL_ENUM && visit_ok(n)) {
  1195. process_element_enum(n, enums, nullptr);
  1196. } else if (el == EL_FLAGS && visit_ok(n)) {
  1197. process_element_enum(n, flags, nullptr);
  1198. } else if (el == EL_CONST && visit_ok(n)) {
  1199. process_element_const(n.second, constants);
  1200. }
  1201. };
  1202. process_entries(tree_, proc_pass_1);
  1203. // check class types we can handle and will provide a definition for
  1204. entry_processor proc_pass_class = [&](const pt::ptree::value_type &n) {
  1205. auto &el = n.first;
  1206. if ((el == EL_RECORD || el == EL_OBJECT || el == EL_INTERFACE) &&
  1207. visit_ok(n)) {
  1208. process_element_record(n, true);
  1209. }
  1210. };
  1211. process_entries(tree_, proc_pass_class);
  1212. // so the known classes will be declared/defined
  1213. // check what callback typedefs that allows for
  1214. // check class types we can handle and will provide a definition for
  1215. std::set<std::string> cb_deps;
  1216. std::ostringstream cb_decl;
  1217. entry_processor proc_pass_callbacks = [&](const pt::ptree::value_type &n) {
  1218. auto &el = n.first;
  1219. if (el == EL_CALLBACK && visit_ok(n)) {
  1220. std::ostringstream null;
  1221. process_element_function(n, cb_decl, callbacks_impl, "", "", cb_deps);
  1222. }
  1223. };
  1224. process_entries(tree_, proc_pass_callbacks);
  1225. // write callbacks
  1226. // need to declare deps first
  1227. callbacks << make_dep_declare(cb_deps);
  1228. callbacks << std::endl;
  1229. callbacks << cb_decl.str();
  1230. // now we know all supported types and supported callbacks
  1231. // pass over class types again and fill in
  1232. std::set<std::pair<std::string, std::string>> includes;
  1233. std::set<std::string> dummy;
  1234. entry_processor proc_pass_2 = [&](const pt::ptree::value_type &n) {
  1235. auto &el = n.first;
  1236. if ((el == EL_RECORD || el == EL_OBJECT || el == EL_INTERFACE) &&
  1237. visit_ok(n)) {
  1238. auto &&res = process_element_record(n, false);
  1239. includes.insert({std::get<0>(res), std::get<1>(res)});
  1240. } else if (el == EL_FUNCTION && visit_ok(n)) {
  1241. process_element_function(n, functions, functions_impl, "", "", dummy);
  1242. } else if ((el == EL_ENUM || el == EL_FLAGS) && visit_ok(n)) {
  1243. process_element_enum(n, functions, &functions_impl);
  1244. }
  1245. };
  1246. process_entries(tree_, proc_pass_2);
  1247. auto add_stub_include = [this](const std::string &suffix) {
  1248. auto fpath = (fs::path(tolower(ns)) / (tolower(ns) + suffix)).native();
  1249. return make_conditional_include(fpath, false);
  1250. };
  1251. auto add_stub_define = [this](bool impl, bool begin) {
  1252. auto nsu = toupper(ns);
  1253. auto infix = impl ? "IMPL_" : "";
  1254. auto suffix = begin ? "BEGIN" : "END";
  1255. auto macro = fmt::format("GI_INCLUDE_{}{}_{}", infix, nsu, suffix);
  1256. // avoid deprecated warning floods
  1257. auto guard = begin ? GI_DISABLE_DEPRECATED_WARN_BEGIN
  1258. : GI_DISABLE_DEPRECATED_WARN_END;
  1259. // also allow for custom/override tweak
  1260. return fmt::format("{}\n\n#ifdef {}\n{}\n#endif", guard, macro, macro);
  1261. };
  1262. auto h_ns = tolower(ns) + ".hpp";
  1263. auto h_ns_impl = tolower(ns) + "_impl.hpp";
  1264. // generate overall includes
  1265. File nsh(ns, h_ns, false);
  1266. // enable dl load coding
  1267. if (h_libs && *h_libs) {
  1268. nsh << "#ifndef GI_DL" << std::endl;
  1269. nsh << "#define GI_DL 1" << std::endl;
  1270. nsh << "#endif" << std::endl;
  1271. }
  1272. if (ctx.options.expected) {
  1273. nsh << "#ifndef GI_EXPECTED" << std::endl;
  1274. nsh << "#define GI_EXPECTED 1" << std::endl;
  1275. nsh << "#endif" << std::endl;
  1276. }
  1277. if (ctx.options.const_method) {
  1278. nsh << "#ifndef GI_CONST_METHOD" << std::endl;
  1279. nsh << "#define GI_CONST_METHOD 1" << std::endl;
  1280. nsh << "#endif" << std::endl;
  1281. }
  1282. nsh << make_include("gi/gi.hpp", false) << std::endl;
  1283. nsh << std::endl;
  1284. // include gi deps
  1285. for (auto &&d : dep_headers)
  1286. nsh << make_include(d, false) << std::endl;
  1287. nsh << std::endl;
  1288. // package includes
  1289. // in some cases, these are totally missing
  1290. // so the headers will have to be supplied by extra header override
  1291. // repo supplied
  1292. nsh << add_stub_include("_setup_pre_def.hpp") << std::endl;
  1293. // user supplied
  1294. nsh << add_stub_include("_setup_pre.hpp") << std::endl;
  1295. // include the above before the includes
  1296. // (so as to allow tweaking some defines in the overrides)
  1297. auto node = root_.get_child(EL_REPOSITORY);
  1298. auto p = node.equal_range(EL_CINCLUDE);
  1299. for (auto &q = p.first; q != p.second; ++q) {
  1300. // in some buggy cases, additional headers may have a full path
  1301. // let's only keep the last part
  1302. auto name = get_name(q->second);
  1303. if (name.size() && name[0] == '/') {
  1304. auto pos = name.rfind('/');
  1305. pos = (pos != name.npos && pos) ? name.rfind('/', pos - 1) : pos;
  1306. if (pos != name.npos)
  1307. name = name.substr(pos + 1);
  1308. }
  1309. nsh << make_include(name, false) << std::endl;
  1310. }
  1311. // we may also need to tweak or fix things after usual includes
  1312. // repo supplied
  1313. nsh << add_stub_include("_setup_post_def.hpp") << std::endl;
  1314. // user supplied
  1315. nsh << add_stub_include("_setup_post.hpp") << std::endl;
  1316. nsh << std::endl;
  1317. // guard begin
  1318. nsh << add_stub_define(false, true) << std::endl;
  1319. nsh << std::endl;
  1320. // various basic declaration parts
  1321. for (auto &h : {h_types, h_enums, h_flags, h_constants, h_callbacks})
  1322. nsh << make_include(h, true) << std::endl;
  1323. nsh << std::endl;
  1324. // declarations
  1325. for (auto &h : includes)
  1326. nsh << make_include(h.first, true) << std::endl;
  1327. nsh << std::endl;
  1328. // global functions when we have seen all else
  1329. nsh << make_include(h_functions, true) << std::endl;
  1330. nsh << std::endl;
  1331. // allow for override/supplements
  1332. // repo supplied
  1333. nsh << add_stub_include("_extra_def.hpp") << std::endl;
  1334. // user supplied
  1335. nsh << add_stub_include("_extra.hpp") << std::endl;
  1336. // guard end
  1337. nsh << add_stub_define(false, false) << std::endl;
  1338. nsh << std::endl;
  1339. // include implementation header in inline case
  1340. nsh << "#if defined(GI_INLINE) || defined(GI_INCLUDE_IMPL)" << std::endl;
  1341. nsh << make_include(h_ns_impl, true) << std::endl;
  1342. nsh << "#endif" << std::endl;
  1343. nsh << std::endl;
  1344. File nsh_impl(ns, h_ns_impl, false);
  1345. // include declaration
  1346. nsh_impl << make_include(h_ns, true) << std::endl;
  1347. nsh_impl << std::endl;
  1348. // guard begin
  1349. nsh_impl << add_stub_define(true, true) << std::endl;
  1350. nsh_impl << std::endl;
  1351. // lib helper for symbol load
  1352. nsh_impl << make_include(h_libs, true) << std::endl;
  1353. nsh_impl << std::endl;
  1354. // implementations
  1355. for (auto &h : includes)
  1356. nsh_impl << make_include(h.second, true) << std::endl;
  1357. nsh_impl << std::endl;
  1358. nsh_impl << make_include(h_callbacks_impl, true) << std::endl;
  1359. nsh_impl << make_include(h_functions_impl, true) << std::endl;
  1360. nsh_impl << std::endl;
  1361. // likewise for override/supplement implementation
  1362. // repo supplied
  1363. nsh_impl << add_stub_include("_extra_def_impl.hpp") << std::endl;
  1364. // user supplied
  1365. nsh_impl << add_stub_include("_extra_impl.hpp") << std::endl;
  1366. nsh_impl << std::endl;
  1367. // guard end
  1368. nsh_impl << add_stub_define(false, true) << std::endl;
  1369. nsh_impl << std::endl;
  1370. // a convenience cpp for non-inline
  1371. auto cpp_ns = tolower(ns) + ".cpp";
  1372. File cpp(ns, cpp_ns, false, false);
  1373. cpp << make_include(h_ns_impl, true) << std::endl;
  1374. return nsh.get_rel_path();
  1375. }
  1376. };
  1377. } // namespace
  1378. std::shared_ptr<NamespaceGenerator>
  1379. NamespaceGenerator::new_(GeneratorContext &ctx, const std::string &filename)
  1380. {
  1381. return std::make_shared<NamespaceGeneratorImpl>(ctx, filename);
  1382. }