| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542 |
- #include "genns.hpp"
- #include "fs.hpp"
- #include "function.hpp"
- #include "genbase.hpp"
- #include <boost/algorithm/string/classification.hpp>
- #include <boost/algorithm/string/join.hpp>
- #include <boost/algorithm/string/split.hpp>
- #include <boost/property_tree/xml_parser.hpp>
- #include <iostream>
- #include <ostream>
- #include <sstream>
- #include <tuple>
- namespace
- {
- class File : public std::ofstream
- {
- std::string ns;
- std::string fname;
- bool preamble;
- bool guard;
- NamespaceGuard nsg;
- static std::string root;
- public:
- // ok, single threaded processing
- static void set_root(const std::string &dir) { root = dir; }
- static std::string prepdirs(const std::string &_ns, const std::string &_fname)
- {
- fs::path p(root);
- p /= tolower(_ns);
- fs::create_directories(p);
- p /= tolower(_fname);
- return p.native();
- }
- File(const std::string &_ns, const std::string _fname, bool _need_ns = true,
- bool _need_guard = true)
- : std::ofstream(prepdirs(_ns, _fname)), ns(_ns), fname(_fname),
- preamble(_need_ns), guard(_need_guard), nsg(*this)
- {
- write_pre();
- }
- std::string get_rel_path() const
- {
- return (fs::path(tolower(ns)) / tolower(fname)).native();
- }
- void write_pre()
- {
- *this << "// AUTO-GENERATED\n\n";
- auto definc = fmt::format("_GI_{}_{}_", toupper(ns), toupper(fname));
- for (auto &v : definc)
- if (!isalnum(v))
- v = '_';
- if (guard) {
- *this << "#ifndef " << definc << std::endl;
- *this << "#define " << definc << std::endl;
- *this << std::endl;
- }
- if (preamble)
- nsg.push(ns);
- }
- void write_post()
- {
- if (preamble)
- nsg.pop();
- if (guard)
- *this << "#endif" << std::endl;
- }
- ~File() { write_post(); }
- };
- std::string File::root;
- class NamespaceGeneratorImpl : private GeneratorBase, public NamespaceGenerator
- {
- std::string version_;
- std::vector<std::string> deps_;
- pt::ptree root_;
- pt::ptree tree_;
- // helper exp
- std::regex re_unqualify_;
- bool allow_deprecated_{};
- public:
- NamespaceGeneratorImpl(GeneratorContext &ctx, const std::string &filename)
- : GeneratorBase(ctx, "")
- {
- logger(Log::INFO, "reading {}", filename);
- pt::read_xml(filename, root_);
- // extract dependencies
- for (auto &&n : root_.get_child("repository")) {
- if (n.first == "include") {
- auto &&name = get_name(n.second);
- auto &&version = get_attribute(n.second, AT_VERSION);
- deps_.emplace_back(name + "-" + version);
- logger(Log::INFO, indent + "dependency " + deps_.back());
- }
- }
- // all else is in a subtree
- tree_ = root_.get_child("repository.namespace");
- ns = get_name(tree_);
- version_ = get_attribute(tree_, AT_VERSION);
- re_unqualify_ =
- std::regex(fmt::format("^{}(::|\\.)", ns), std::regex::optimize);
- }
- std::string get_ns() const { return ns + "-" + version_; }
- std::vector<std::string> get_dependencies() const { return deps_; }
- private:
- // in most cases no problems arise if both M1.SomeName and M2.SomeOtherName
- // both map to GSomeName
- // but some generated code, like declare_XXX template specialization assumes
- // a 1-to-1 mapping, as should be the case in with sane GIR
- // however, if the latter are somehow not sane, then duplicate definitions
- // might arise (ODR and all that), although it *really is* a GIR bug
- //
- // so try to mitigate that here
- // check that only 1 (qualified) girname/cppname claims/maps to a ctype
- void check_odr(const std::string &cpptype, const std::string &ctype) const
- {
- // verify this ctype has not been seen before
- // a particular ctype can only be defined/claimed by one GIR symbol
- // (in one module), otherwise lots of things will go wrong
- // GIRs also have an "alias" concept, so that should be used instead
- auto conflict = ctx.repo.check_odr(cpptype, ctype);
- if (!conflict.empty()) {
- throw std::runtime_error(
- fmt::format("{} maps to {}, already claimed by {};\n"
- "Please verify/fix GIRs and/or add ignore as needed.",
- cpptype, ctype, conflict));
- }
- }
- std::string make_declare(bool decl_ctype, const std::string &cpptype,
- const std::string &ctype) const
- {
- check_odr(cpptype, ctype);
- return fmt::format(
- "template<> struct declare_{}_of<{}>\n{{ typedef {} type; }}; ",
- (decl_ctype ? "ctype" : "cpptype"), (decl_ctype ? cpptype : ctype),
- (decl_ctype ? ctype : cpptype));
- }
- void process_element_alias(const pt::ptree &node, std::ostream &out) const
- {
- std::ostringstream oss;
- auto name = get_name(node);
- // skip some stuff
- if (name.find("autoptr") != name.npos)
- throw skip("autoptr", skip::OK);
- auto ctype = get_attribute(node, AT_CTYPE);
- std::string deftype;
- TypeInfo tinfo;
- auto tnode = node.get_child(EL_TYPE);
- auto btype = get_name(tnode);
- parse_typeinfo(btype, tinfo);
- if (tinfo.flags & TYPE_BASIC) {
- // if typedef refers to a basic type, let's refer here to (global
- // scope) C type (this is mostly the case, e.g. GstClockTime, etc)
- deftype = GI_SCOPE + ctype;
- } else if (tinfo.flags) {
- assert(tinfo.cpptype.size());
- // otherwise we alias to the corresponding wrapped type
- // (e.g. GtkAllocation)
- deftype = tinfo.cpptype;
- }
- auto aliasfmt = "typedef {} {};\n";
- if (deftype.size())
- out << fmt::format(aliasfmt, deftype, name);
- // also mind ref type
- if (tinfo.flags & TYPE_BOXED) {
- assert(deftype.size());
- out << fmt::format(
- aliasfmt, deftype + GI_SUFFIX_REF, name + GI_SUFFIX_REF);
- }
- out << std::endl;
- }
- void process_element_enum(const pt::ptree::value_type &n, std::ostream &out,
- std::ostream *_out_impl) const
- {
- auto &kind = n.first;
- auto &node = n.second;
- std::ostringstream oss;
- auto name = get_name(node);
- auto ctype = get_attribute(node, AT_CTYPE);
- if (_out_impl) {
- auto &out_impl = *_out_impl;
- // need to generate namespaced function members
- std::ostringstream oss_decl;
- std::ostringstream oss_impl;
- std::set<std::string> dummy;
- for (const auto &n : node) {
- if (n.first == EL_FUNCTION) {
- process_element_function(n, oss_decl, oss_impl, "", "", dummy);
- }
- }
- if (oss_decl.tellp()) {
- auto enumns = name + "NS_";
- NamespaceGuard ns_d(out);
- ns_d.push(enumns, false);
- out << oss_decl.str();
- NamespaceGuard ns_i(out_impl);
- ns_i.push(enumns, false);
- out_impl << oss_impl.str();
- }
- return;
- }
- // enum name might end up matching (uppercase) define
- name = unreserve(name);
- // otherwise generate enum
- NamespaceGuard nsg(oss);
- nsg.push(ns);
- // auto-adjust underlying type to avoid warnings;
- // outside the range of underlying type ‘int’
- // (no forward declare of enum in C, so type should be complete)
- oss << fmt::format(
- "enum class {} : std::underlying_type<{}>::type {{\n", name, ctype);
- std::set<std::string> names;
- for (const auto &n : node) {
- if (n.first == EL_MEMBER) {
- auto value = get_attribute(n.second, AT_CIDENTIFIER);
- auto name = get_name(n.second);
- // some enums/flags declare different names with same value
- // which seems to confuse gobject-introspection
- // (e.g. GstVideoFrameFlags, GstVideoBufferFlags, etc)
- // and then they end up with repeated/duplicate name in GIR
- // so let's only pick the first one of those
- if (!std::get<1>(names.insert(name))) {
- logger(Log::WARNING, "{} skipping duplicate {}", ctype, value);
- continue;
- }
- oss << indent
- << fmt::format("{} = {},\n", unreserve(toupper(name)), value);
- }
- }
- oss << "};\n\n";
- // add operators in case of flag = bitfield
- if (kind == EL_FLAGS)
- oss << fmt::format("GI_FLAG_OPERATORS({})\n\n", name);
- nsg.pop();
- // declare ctype info
- nsg.push(GI_REPOSITORY_NS);
- name = ns + "::" + name;
- oss << make_declare(true, name, ctype) << std::endl;
- oss << make_declare(false, name, ctype) << std::endl;
- oss << std::endl;
- // declare gtype info
- auto gtype = get_attribute(node, AT_GLIB_GET_TYPE, "");
- if (gtype.size()) {
- oss << fmt::format("template<> struct declare_gtype_of<{}>\n"
- "{{ static GType get_type() {{ return {}(); }} }};",
- name, gtype)
- << std::endl;
- oss << std::endl;
- }
- // in case of a flag = bitfield, also mark it as such
- if (kind == EL_FLAGS) {
- oss << fmt::format(
- "template<> struct is_bitfield<{}> : public std::true_type\n"
- "{{}};",
- name)
- << std::endl;
- oss << std::endl;
- }
- oss << std::endl;
- nsg.pop();
- out << oss.str() << std::endl;
- }
- void process_element_const(const pt::ptree &node, std::ostream &out) const
- {
- auto name = get_name(node);
- std::ostringstream oss;
- try {
- ArgInfo tinfo = parse_arginfo(node);
- auto cpptype = tinfo.cpptype;
- auto value = get_attribute(node, AT_CTYPE);
- std::string cast;
- std::string namesuffix;
- if (tinfo.flags & TYPE_BASIC) {
- if (tinfo.flags & TYPE_CLASS) {
- // special string case
- // need some suffix to make the variable (pointer) const
- cpptype = "gchar";
- namesuffix = "[]";
- } else {
- // normalize back to C (e.g. char*)
- cpptype = tinfo.ctype;
- }
- } else {
- // need to cast to enum type (flags/enum)
- if (tinfo.flags & TYPE_ENUM) {
- cast = fmt::format("({}) ", cpptype);
- } else if (!(tinfo.flags & TYPE_VALUE)) {
- throw skip("constant type " + cpptype + " not supported");
- }
- // accept alias typedef
- }
- // avoid clashes with existing defines
- name = unreserve(name);
- // NOTE what about an inline var in C++17
- // recall that const implies static in C++
- oss << fmt::format(
- "const {} {}{} = {}{};", cpptype, name, namesuffix, cast, value);
- } catch (skip &ex) {
- oss << "// SKIP constant " << name << "; " << ex.what();
- }
- out << oss.str() << std::endl << std::endl;
- }
- void process_element_property(const pt::ptree::value_type &entry,
- std::ostream &out, std::ostream &impl, const std::string &klass,
- std::set<std::string> &deps) const
- {
- auto &node = entry.second;
- ArgInfo tinfo;
- auto name = get_name(node);
- auto read = get_attribute<int>(node, AT_READABLE, 1);
- auto write = get_attribute<int>(node, AT_WRITABLE, 1);
- tinfo = parse_arginfo(node);
- auto &&cpptype = tinfo.cpptype;
- if (!tinfo.flags)
- throw skip("unknown type " + cpptype);
- // let's not go here for now
- if (tinfo.flags & TYPE_CONTAINER)
- throw skip("container property not supported", skip::TODO);
- track_dependency(deps, tinfo);
- // directly write all in decl
- auto &oss = out;
- (void)impl;
- auto decl_name = name;
- std::replace(decl_name.begin(), decl_name.end(), '-', '_');
- std::string proptype = "property_proxy";
- if (!read) {
- proptype = "property_proxy_write";
- } else if (!write) {
- proptype = "property_proxy_read";
- }
- proptype = GI_NS_SCOPED + proptype;
- auto qklass = qualify(klass, TYPE_OBJECT);
- if (write)
- oss << fmt::format("{3}<{0}, {4}> property_{2}()\n"
- "{{ return {3}<{0}, {4}> (*this, \"{1}\"); }}",
- cpptype, name, decl_name, proptype, qklass)
- << std::endl;
- if (read)
- oss << fmt::format("const {3}<{0}, {4}> property_{2}() const\n"
- "{{ return {3}<{0}, {4}> (*this, \"{1}\"); }}",
- cpptype, name, decl_name, proptype, qklass)
- << std::endl;
- oss << std::endl;
- }
- void process_element_field(const pt::ptree::value_type &entry,
- std::ostream &out, std::ostream &impl, const std::string &klass,
- const std::string &klasstype, std::set<std::string> &deps) const
- {
- auto &node = entry.second;
- auto name = get_name(node);
- auto readable = get_attribute<int>(node, AT_READABLE, 1);
- auto writable = get_attribute<int>(node, AT_WRITABLE, 1);
- auto priv = get_attribute<int>(node, AT_PRIVATE, 0);
- ArgInfo tinfo;
- try {
- tinfo = parse_arginfo(node);
- // only very plain basic fields
- // type must be known
- // no private
- // no int* cookies or other strange things
- if (!tinfo.flags || priv ||
- get_pointer_depth(tinfo.ctype) != tinfo.pdepth ||
- is_volatile(tinfo.ctype))
- return;
- } catch (...) {
- // simply fail silently here and never mind
- return;
- }
- if (!(tinfo.flags & (TYPE_VALUE | TYPE_CLASS)))
- return;
- // unconditionally reserve this to avoid clash with non-field member
- ElementFunction func;
- func.kind = EL_METHOD;
- func.name = unreserve(name, true);
- func.c_id = klasstype + "::" + name;
- Parameter instance;
- // real one, no base
- parse_typeinfo(klasstype, instance.tinfo);
- instance.instance = true;
- instance.name = "obj";
- instance.direction = DIR_IN;
- // field parameter
- Parameter param;
- param.tinfo = tinfo;
- param.name = "_value";
- param.transfer = TRANSFER_NOTHING;
- // common helper lambda for below
- auto make_wrapper = [&](const std::string &funcname,
- const std::string funcdef,
- const std::vector<Parameter> ¶ms) {
- func.functionexp = funcname;
- // buffer result
- std::ostringstream oss;
- auto def = ::process_element_function(
- ctx, ns, func, params, out, oss, klass, klasstype, deps);
- // now write all if wrapping accessor did not fail
- if (def.name.size())
- impl << funcdef << std::endl << oss.str();
- };
- // a static helper is used in the following,
- // rather than a compact local lambda,
- // as the latter does not always cast to any function type
- // also, internal casts are applied in the helper to handle enum/int
- // and pointer type conversions
- // also, the ctype info is derived from the cpptype as with func
- // argument due to either
- // + ctype missing, e.g. enum,
- // + or minor discrepancies, e.g. gpointer vs gconstpointer
- if (readable) {
- param.direction = DIR_RETURN;
- param.tinfo.ctype = make_ctype(param.tinfo, param.direction, false);
- instance.tinfo.ctype = fmt::format("const {}*", instance.tinfo.dtype);
- // define helper function
- auto funcname = "_field_" + name + "_get";
- auto funcdef =
- fmt::format("static {} {} ({} {}) {{ return ({}) obj->{}; }}",
- param.tinfo.ctype, funcname, instance.tinfo.ctype, instance.name,
- param.tinfo.ctype, name);
- make_wrapper(funcname, funcdef, {param, instance});
- }
- // not safe to assume any particular ownership of some struct field
- // so let's not meddle with it other than the very basic cases
- if (writable && (tinfo.flags & TYPE_VALUE)) {
- param.direction = DIR_IN;
- param.tinfo.ctype = make_ctype(param.tinfo, param.direction, false);
- instance.tinfo.ctype = instance.tinfo.dtype + "*";
- // define helper function
- auto funcname = "_field_" + name + "_set";
- auto funcdef = fmt::format("static void {} ({} {}, {} {}) {{ "
- "obj->{} = (decltype(obj->{})) {}; }}",
- funcname, instance.tinfo.ctype, instance.name, param.tinfo.ctype,
- param.name, name, name, param.name);
- // void return
- Parameter vparam;
- parse_typeinfo(GIR_VOID, vparam.tinfo);
- vparam.direction = DIR_RETURN;
- make_wrapper(funcname, funcdef, {vparam, param, instance});
- }
- }
- FunctionDefinition process_element_function(
- const pt::ptree::value_type &entry, std::ostream &out, std::ostream &impl,
- const std::string &klass, const std::string &klasstype,
- std::set<std::string> &deps) const
- {
- return ::process_element_function(
- ctx, ns, entry, out, impl, klass, klasstype, deps, allow_deprecated_);
- }
- // unqualify (current ns qualifed) type
- std::string unqualify(const std::string &name) const
- {
- return std::regex_replace(name, re_unqualify_, "");
- }
- static std::string get_record_filename(const std::string &rname, bool impl)
- {
- return tolower(rname) + (impl ? "_impl" : "") + ".hpp";
- }
- static std::string make_include(const std::string &hname, bool local)
- {
- if (hname.empty())
- return hname;
- char open = local ? '"' : '<';
- char close = local ? '"' : '>';
- std::ostringstream oss;
- oss << "#include " << open << hname << close;
- return oss.str();
- }
- // make include for a qualified dependency
- // (not in current namespace)
- std::string make_dep_include(const std::string &girname) const
- {
- auto lname = unqualify(girname);
- return lname.size() && !is_qualified(lname)
- ? make_include(get_record_filename(lname, false), true) + '\n'
- : EMPTY;
- }
- std::string make_dep_declare(const std::set<std::string> &deps) const
- {
- std::ostringstream oss;
- for (const auto &d : deps) {
- auto c = unqualify(d);
- if (!is_qualified(c))
- oss << "class " << c << ";" << std::endl;
- }
- return oss.str();
- }
- static std::string make_conditional_include(
- const std::string &hname, bool local)
- {
- if (hname.empty())
- return hname;
- auto templ = R"|(
- #if defined(__has_include)
- #if __has_include({}{}{})
- #include {}{}{}
- #endif
- #endif
- )|";
- char open = local ? '"' : '<';
- char close = local ? '"' : '>';
- return fmt::format(templ, open, hname, close, open, hname, close);
- }
- static constexpr const char *const CLASS_PLACEHOLDER{"CLASS_PLACEHOLDER"};
- // minor convencience helper type
- struct TypeClassInfo
- {
- // (optionally) gir qualified
- std::string parentgir;
- // info on type-struct
- TypeInfo ti;
- };
- TypeClassInfo collect_type_class_info(const std::string &girname) const
- {
- // class struct is skipped above,
- // but we do look for it here to find *Class/*Interface struct
- TypeClassInfo result;
- auto &repo = ctx.repo;
- auto &node = repo.tree(girname).second;
- result.parentgir = get_attribute(node, AT_PARENT, "");
- // NOTE parent might be in different ns
- if (result.parentgir.size())
- result.parentgir = repo.qualify(result.parentgir, girname);
- auto cpptype = get_attribute(node, AT_GLIB_TYPE_STRUCT, "");
- if (cpptype.empty())
- throw skip(girname + " missing type-struct info");
- cpptype = repo.qualify(cpptype, girname);
- parse_typeinfo(cpptype, result.ti);
- // more useful in this setting
- result.ti.cpptype = unqualify(result.ti.cpptype);
- if (result.ti.dtype.empty())
- throw skip(girname + " missing C type-struct info");
- return result;
- }
- typedef std::vector<std::pair<std::string, FunctionDefinition>>
- VirtualMethods;
- void process_element_record_class(const pt::ptree::value_type &entry,
- const std::vector<TypeInfo> &interfaces, const std::string &decl,
- const std::string &impl, const VirtualMethods &methods,
- std::ostream &out_decl, std::ostream &out_impl) const
- {
- auto &node = entry.second;
- auto name = get_name(node);
- bool interface = (entry.first == EL_INTERFACE);
- // run up parent hierarchy to check if all those can be properly
- // generated
- auto class_info = collect_type_class_info(name);
- TypeClassInfo parent_class_info;
- // no parent for interface
- if (class_info.parentgir.size()) {
- parent_class_info = collect_type_class_info(class_info.parentgir);
- auto rec_info = parent_class_info;
- while (rec_info.parentgir.size())
- rec_info = collect_type_class_info(rec_info.parentgir);
- }
- // collect ok interfaces
- std::vector<TypeInfo> itfs_info;
- for (auto &&itf : interfaces) {
- try {
- itfs_info.emplace_back(collect_type_class_info(itf.girname).ti);
- // base class needs to be declared
- out_decl << make_dep_include(itf.girname);
- } catch (...) {
- }
- }
- out_decl << std::endl;
- // put into inner namespace to avoid name clash
- // declaration part
- NamespaceGuard ns_decl(out_decl);
- ns_decl.push(ns);
- ns_decl.push(GI_NS_IMPL);
- ns_decl.push(GI_NS_INTERNAL);
- // class definition
- // explicitly specify a non-public non-virtual destructor
- auto def_templ = R"|(
- class {0}
- {{
- typedef {0} self;
- public:
- typedef {1} instance_type;
- typedef {2} {3}_type;
- {6}
- struct TypeInitData;
- protected:
- {5} ~{0}() = default;
- static {5} void {3}_init (gpointer {3}_struct, gpointer );
- {4}
- }};
- )|";
- std::string kind = interface ? "interface" : "class";
- const auto klassnamedef = class_info.ti.cpptype + "Def";
- // used for interfaces
- const std::string suffix_class_impl = "ClassImpl";
- const auto klassname =
- class_info.ti.cpptype + (interface ? suffix_class_impl : "");
- // conflict check and type init lists
- std::string conflict_check, type_init, type_init_calc;
- conflict_check.reserve(1024);
- type_init.reserve(1024);
- type_init_calc.reserve(1024);
- std::string calc_indent = indent + indent;
- for (auto &&method : methods) {
- auto &&n = method.first;
- conflict_check +=
- fmt::format("using GI_MEMBER_CHECK_CONFLICT({}) = self;\n", n);
- type_init +=
- fmt::format("{}GI_MEMBER_DEFINE({}, {})\n", indent, klassname, n);
- type_init_calc +=
- fmt::format("{}{}GI_MEMBER_HAS_DEFINITION(SubClass, DefData, {})",
- type_init_calc.empty() ? "" : ",\n", calc_indent, n);
- }
- // (note that e.g. xlib cases prefix might not help, or ns interference)
- // drop inline mark on pure virtual interface functions;
- // does not quite make sense and might otherwise lead to compiler
- // warnings
- static const std::regex re_inline(GI_INLINE + ' ', std::regex::optimize);
- out_decl << fmt::format(def_templ, klassnamedef, qualify(name, TYPE_OBJECT),
- class_info.ti.dtype, kind, std::regex_replace(decl, re_inline, ""),
- GI_INLINE, conflict_check);
- if (interface)
- out_decl << fmt::format("using {}Impl = detail::InterfaceImpl<{}>;", name,
- klassnamedef)
- << std::endl;
- // avoid ambiguous unqualified name lookup of friend class below
- // (as the scopes of parent classes are involved then as well)
- auto class_templ = R"|(
- class {0}: public {1}
- {{
- friend class internal::{4};
- typedef {0} self;
- typedef {1} super;
- protected:
- using super::super;
- {2}
- {3}
- }};
- )|";
- auto classextra = R"|(
- private:
- // make local helpers private
- using super::get_struct_;
- using super::gobj_;
- protected:
- // disambiguation helper types
- {}
- )|";
- // qualification helper; ensure impl::internal qualified
- // (also adds current ns if needed)
- auto implqualify = [&](const std::string &cpptype) {
- auto result = cpptype;
- auto pos = result.find(GI_SCOPE.c_str());
- auto insert = GI_SCOPE + GI_NS_IMPL + GI_SCOPE + GI_NS_INTERNAL;
- if (pos != result.npos) {
- result.insert(pos, insert);
- } else {
- insert = ns + insert + GI_SCOPE;
- result.insert(0, insert);
- }
- return result;
- };
- // collect interface types and helper types
- std::ostringstream oss_types;
- std::vector<std::string> itfs;
- for (auto &&itf : itfs_info) {
- auto kname = itf.cpptype + suffix_class_impl;
- kname = implqualify(kname);
- itfs.push_back(kname);
- // fall back to ctype as it needs to be fully qualified
- static const std::regex re_descope(GI_SCOPE, std::regex::optimize);
- auto tprefix = std::regex_replace(itf.dtype, re_descope, "");
- oss_types << fmt::format("typedef {} {}_type;\n", kname, tprefix);
- }
- auto extra = interface ? EMPTY : fmt::format(classextra, oss_types.str());
- // determine baseclasses
- // qualified klassnamedef to avoid ambiguous parent class lookup
- // (when used as a typedef within class)
- std::string superclass =
- interface ? fmt::format("detail::InterfaceClassImpl<{}>", name + "Impl")
- : fmt::format("detail::ClassTemplate<{}, {}{}{}>",
- implqualify(klassnamedef),
- implqualify(parent_class_info.ti.cpptype),
- itfs.size() ? ", " : "",
- boost::algorithm::join(itfs, ", "));
- static const std::regex re_virtual("virtual ", std::regex::optimize);
- static const std::regex re_pure("= 0", std::regex::optimize);
- auto idecl = std::regex_replace(decl, re_virtual, "");
- idecl = std::regex_replace(idecl, re_pure, "override");
- // add macro for optional warning suppression
- if (!interface)
- out_decl << GI_CLASS_IMPL_BEGIN << std::endl << std::endl;
- out_decl << fmt::format(
- class_templ, klassname, superclass, extra, idecl, klassnamedef);
- // type init
- // at this later stage as it contains template definitions
- // that refer to the above class
- auto type_init_templ = R"|(
- struct {0}::TypeInitData
- {{
- {1}
- template<typename SubClass>
- constexpr static TypeInitData factory()
- {{
- {3}using DefData = detail::DefinitionData<SubClass, TypeInitData>;
- return {{
- {2}
- }};
- }}
- }};
- )|";
- out_decl << fmt::format(type_init_templ, klassnamedef, type_init,
- type_init_calc, methods.empty() ? "// " : "");
- // end internal ns
- ns_decl.pop(1);
- if (!interface)
- out_decl << GI_CLASS_IMPL_END << std::endl
- << std::endl
- << fmt::format("using {}Impl = detail::ObjectImpl<{}, {}::{}>;",
- name, name, GI_NS_INTERNAL, klassname)
- << std::endl
- << std::endl;
- // implementation part
- NamespaceGuard ns_impl(out_impl);
- ns_impl.push(ns);
- ns_impl.push(GI_NS_IMPL);
- ns_impl.push(GI_NS_INTERNAL);
- auto &ctype = class_info.ti.dtype;
- out_impl << fmt::format(
- "void {}::{}_init (gpointer {}_struct, gpointer factory)\n{{\n",
- klassnamedef, kind, kind);
- out_impl << indent
- << fmt::format(
- "{} *methods = ({} *) {}_struct;\n", ctype, ctype, kind);
- // avoid warning if no methods
- out_impl << indent << "(void) methods;" << std::endl << std::endl;
- // init data from factory
- out_impl
- << indent
- << "auto init_data = GI_MEMBER_INIT_DATA(TypeInitData, factory);\n";
- out_impl << indent << "(void) init_data;" << std::endl << std::endl;
- for (auto &&method : methods) {
- auto &&n = method.first;
- auto &&def = method.second;
- std::vector<std::string> args, transfers;
- for (auto &&arg : def.cpp_decl)
- args.push_back(arg.second);
- auto cpp_return =
- def.cpp_outputs.size() ? def.cpp_outputs[0].type : CPP_VOID;
- auto sig = fmt::format(
- "{} (*) ({})", cpp_return, boost::algorithm::join(args, ", "));
- std::string transferargs, precheck;
- if (def.arg_traits.empty()) {
- // only arrange to call the raw methods
- // if new-style detection (or explicit specification) is in place
- precheck = " && factory";
- transferargs = "std::nullptr_t";
- } else {
- transferargs = make_arg_traits(def.arg_traits, def.c_sig);
- }
- // add a hard cast to deal with const differences (e.g. string vs
- // const char*)
- out_impl << indent
- << fmt::format("if (init_data.{0}{3}) methods->{0} = (decltype "
- "(methods->{0})) "
- "detail::method_wrapper<self, {1}, "
- "{2}>::wrapper<&self::{0}_>;",
- n, sig, transferargs, precheck)
- << std::endl;
- }
- out_impl << "}" << std::endl << std::endl;
- auto iimpl =
- std::regex_replace(impl, std::regex(CLASS_PLACEHOLDER), klassname);
- out_impl << iimpl;
- }
- std::vector<TypeInfo> record_collect_interfaces(
- const pt::ptree::value_type &entry, const TypeInfo ¤t,
- const TypeInfo &parent, std::set<std::string> &deps) const
- {
- auto &repo = ctx.repo;
- auto &node = entry.second;
- auto name = get_name(node);
- // listed interfaces also include parent's interfaces
- // so we should subtract those for good measure
- auto collect_interfaces = [&](const pt::ptree &node,
- const std::string &basename) {
- std::set<std::string> result;
- auto p = node.equal_range(EL_IMPLEMENTS);
- assert(basename.size());
- for (auto &q = p.first; q != p.second; ++q) {
- auto n = get_name(q->second, std::nothrow);
- if (n.size()) {
- result.insert(repo.qualify(n, basename));
- }
- }
- return result;
- };
- std::vector<std::string> itfs;
- // ensure all GIR names fully qualified
- auto itfs_local = collect_interfaces(node, current.girname);
- if (parent.girname.size()) {
- // qualify relative to parent
- auto itfs_parent =
- collect_interfaces(repo.tree(parent.girname).second, parent.girname);
- // only keep those not matching a parent's interface (both fully
- // qualified)
- for (auto &&n : itfs_local) {
- if (itfs_parent.find(n) == itfs_parent.end())
- itfs.push_back(n);
- }
- } else {
- std::copy(itfs_local.begin(), itfs_local.end(), std::back_inserter(itfs));
- }
- std::vector<TypeInfo> interfaces;
- for (auto &n : itfs) {
- TypeInfo itf;
- parse_typeinfo(n, itf);
- // should know about this interface by now
- // but let's continue anyway
- if (!itf.flags) {
- logger(Log::WARNING, "{} implements unknown interface {}", name, n);
- continue;
- }
- deps.insert(itf.cpptype);
- interfaces.push_back(itf);
- }
- return interfaces;
- }
- // returns (decl header name, impl header name)
- std::tuple<std::string, std::string> process_element_record(
- const pt::ptree::value_type &entry, bool onlyverify) const
- {
- auto &kind = entry.first;
- auto &node = entry.second;
- auto name = get_name(node);
- logger(Log::LOG, "checking {} {} {}", kind, name, onlyverify);
- TypeInfo current;
- parse_typeinfo(name, current);
- // only consider real type, but keep info around
- if (!current.flags)
- return std::make_tuple("", "");
- auto &ctype = current.dtype;
- // could happen for a GType only case (e.g. GstFraction)
- if (!ctype.size())
- throw skip("no c:type info");
- bool is_object_base = (current.girname == GIR_GOBJECT);
- // check parent
- TypeInfo parent;
- auto &repo = ctx.repo;
- // 1 special case here
- if (kind == EL_OBJECT && !is_object_base) {
- auto parentgir = get_attribute(node, AT_PARENT, "");
- if (parentgir.empty())
- throw skip("missing parent class");
- parse_typeinfo(parentgir, parent);
- if (!parent.flags)
- throw skip("unknown parent " + parentgir);
- // parent might be found, but might have problems of its own
- // so that needs to be checked recursively
- // (at least if within current ns)
- if (onlyverify && !is_qualified(parentgir)) {
- try {
- process_element_record(repo.tree(parentgir), onlyverify);
- } catch (const skip &ex) {
- throw skip(std::string("parent problem; ") + ex.what());
- }
- }
- }
- if (onlyverify || is_object_base)
- return std::make_tuple("", "");
- // no throwing or giving up after this point
- // we have now committed to coming up with a class
- // declaration/definition (even if it is a pretty empty one)
- logger(Log::LOG, "processing {} {}", kind, name);
- // generate classes in subnamespace
- const std::string nsbase("base");
- auto namebase = name + "Base";
- // avoid qualification later on with standard ns (e.g. Gst)
- auto qnamebase = nsbase + "::" + namebase;
- // collect output of members and their dependencies
- std::ostringstream oss_decl, oss_class_decl;
- std::ostringstream oss_impl, oss_class_impl;
- VirtualMethods vmethods;
- std::set<std::string> deps;
- for (const auto &n : node) {
- auto el = n.first;
- int introspectable = get_attribute<int>(n.second, AT_INTROSPECTABLE, 1);
- try {
- // try even if marked introspectable
- // some are useful in non-runtime setting
- if (el == EL_FUNCTION || el == EL_CONSTRUCTOR || el == EL_METHOD ||
- el == EL_SIGNAL) {
- process_element_function(
- n, oss_decl, oss_impl, qnamebase, name, deps);
- } else if (el == EL_VIRTUAL_METHOD && introspectable &&
- ctx.options.classimpl) {
- // placeholder replaced suitably later on
- auto def = process_element_function(
- n, oss_class_decl, oss_class_impl, CLASS_PLACEHOLDER, name, deps);
- if (def.name.size())
- vmethods.push_back({def.name, def});
- } else if (el == EL_FIELD && introspectable) {
- process_element_field(n, oss_decl, oss_impl, qnamebase, name, deps);
- } else if (el == EL_PROPERTY && introspectable) {
- process_element_property(n, oss_decl, oss_impl, qnamebase, deps);
- }
- } catch (std::runtime_error &ex) {
- handle_exception(n, ex);
- }
- }
- // actual output to file
- auto fname_decl = get_record_filename(name, false);
- File out_decl(ns, fname_decl, false);
- auto fname_impl = get_record_filename(name, true);
- File out_impl(ns, fname_impl, false);
- // a superclass needs full declaration (not only forward declaration)
- out_decl << make_dep_include(parent.girname);
- out_decl << std::endl;
- // implemented interfaces are also dependency
- std::vector<TypeInfo> interfaces =
- record_collect_interfaces(entry, current, parent, deps);
- // namespace in decl before forward class decl
- NamespaceGuard ns_decl(out_decl);
- ns_decl.push(ns);
- // all declarations are included prior to implementation
- // forward class declarations in declaration
- deps.erase(current.cpptype);
- out_decl << make_dep_declare(deps);
- out_decl << std::endl;
- // also forward declare 'oneself' since only base is defined below
- out_decl << "class " << name << ";" << std::endl;
- out_decl << std::endl;
- // namespace in impl following includes
- NamespaceGuard ns_impl(out_impl);
- ns_impl.push(ns);
- // base class subnamespace
- ns_decl.push(nsbase);
- ns_impl.push(nsbase);
- // class definition
- auto obj_templ = R"|(
- #define {3} {4}::{0}
- class {0} : public {2}
- {{
- typedef {2} super_type;
- public:
- typedef {1} BaseObjectType;
- {0} (std::nullptr_t = nullptr) : super_type() {{}}
- BaseObjectType *gobj_() {{ return (BaseObjectType*) super_type::gobj_(); }}
- const BaseObjectType *gobj_() const {{ return (const BaseObjectType*) super_type::gobj_(); }}
- BaseObjectType *gobj_copy_() const {{ return (BaseObjectType*) super_type::gobj_copy_(); }}
- )|";
- auto boxed_templ = R"|(
- #define {2} {3}::{0}
- class {0} : public {1}
- {{
- typedef {1} super_type;
- public:
- {0} (std::nullptr_t = nullptr) : super_type() {{}}
- )|";
- auto gtype = get_attribute(node, AT_GLIB_GET_TYPE, "");
- auto basedef = toupper(fmt::format("GI_{}_{}_BASE", ns, name));
- // class definition
- bool is_variant = false;
- if (kind == EL_OBJECT) {
- out_decl << fmt::format(
- obj_templ, namebase, ctype, parent.cpptype, basedef, nsbase);
- } else if (kind == EL_INTERFACE) {
- out_decl << fmt::format(
- obj_templ, namebase, ctype, "gi::InterfaceBase", basedef, nsbase);
- } else {
- auto tmpl = GI_NS_DETAIL_SCOPED +
- (gtype.size() ? "GBoxedWrapperBase" : "CBoxedWrapperBase");
- auto parent = fmt::format("{}<{}, {}>", tmpl, namebase, ctype);
- // override if special fundamental boxed-like case
- if ((is_variant = (current.girname == GIR_GVARIANT))) {
- parent = "detail::VariantWrapper";
- gtype.clear();
- }
- out_decl << fmt::format(boxed_templ, namebase, parent, basedef, nsbase);
- }
- if (gtype.size()) {
- out_decl << fmt::format(
- "static GType get_type_ () G_GNUC_CONST {{ return {}(); }} ", gtype);
- out_decl << std::endl << std::endl;
- }
- // add some helpers to obtain known implemented interfaces
- for (auto &&_itf : interfaces) {
- // temp extend for wrapping below
- ArgInfo itf;
- (TypeInfo &)itf = _itf;
- auto decl_fmt = "{0} {1}interface_ (gi::interface_tag<{0}>)";
- auto impl_fmt = decl_fmt;
- auto decl = fmt::format(decl_fmt, itf.cpptype, "");
- out_decl << GI_INLINE << ' ' << decl << ";" << std::endl << std::endl;
- // wrap a properly casted extra reference (ensure no sink'ing)
- auto impl = fmt::format(impl_fmt, itf.cpptype, (namebase + "::"));
- out_impl << impl << std::endl;
- auto towrap =
- fmt::format("({}::BaseObjectType*) gobj_copy_()", itf.cpptype);
- auto w =
- fmt::format(make_wrap_format(ArgInfo{itf}, TRANSFER_FULL), towrap);
- out_impl << fmt::format("{{ return {}; }}", w) << std::endl << std::endl;
- // conversion operator
- auto op_decl = fmt::format("operator {} ()", itf.cpptype);
- auto op_impl = fmt::format(
- "{}::{}\n{{ return interface_ (gi::interface_tag<{}>()); }}",
- namebase, op_decl, itf.cpptype);
- out_decl << GI_INLINE << ' ' << op_decl << ';' << std::endl << std::endl;
- out_impl << op_impl << std::endl << std::endl;
- }
- out_decl << oss_decl.str();
- out_decl << "}; // class" << std::endl;
- out_decl << std::endl;
- ns_decl.pop();
- out_impl << oss_impl.str();
- out_impl << std::endl;
- ns_impl.pop();
- // optionally include supplements/overrides for this class
- auto include_extra = [&](File &out, bool impl) {
- for (auto &&suffix : {"_extra_def", "_extra"}) {
- auto header =
- (fs::path(tolower(ns)) / get_record_filename(name + suffix, impl))
- .native();
- out << make_conditional_include(header, false) << std::endl;
- }
- };
- include_extra(out_decl, false);
- include_extra(out_impl, true);
- ns_decl.push(GI_REPOSITORY_NS);
- // make fragment to define final class
- {
- NamespaceGuard nst(out_decl);
- nst.push(ns, false);
- auto supertype = basedef;
- auto reftype = name + GI_SUFFIX_REF;
- if (kind != EL_OBJECT && kind != EL_INTERFACE && !is_variant) {
- // boxed base templates define a few members with CppType return type
- // so in the CppTypeBase defined above that is still CppTypeBase
- // again add the template as subclass but now with final CppType
- auto tmpl = GI_NS_DETAIL_SCOPED +
- (gtype.size() ? "GBoxedWrapper" : "CBoxedWrapper");
- supertype = fmt::format(
- "{}<{}, {}, {}, {}>", tmpl, name, ctype, basedef, reftype);
- // forward declaration of corresponding ref type
- out_decl << fmt::format("class {};", reftype) << std::endl << std::endl;
- }
- auto fmtclass =
- "class {0} : public {1}\n"
- "{{ typedef {1} super_type; using super_type::super_type; }};\n";
- out_decl << fmt::format(fmtclass, name, supertype) << std::endl;
- // in case of boxed, also define ref type
- if (kind != EL_OBJECT && kind != EL_INTERFACE && !is_variant) {
- auto tmpl = GI_NS_DETAIL_SCOPED +
- (gtype.size() ? "GBoxedRefWrapper" : "CBoxedRefWrapper");
- supertype = fmt::format("{}<{}, {}, {}>", tmpl, name, ctype, basedef);
- out_decl << std::endl
- << fmt::format(fmtclass, reftype, supertype) << std::endl;
- }
- }
- // GInitiallyUnowned is typedef'ed to GObject
- // so we have to avoid duplicate definition
- // FIXME generally no way to check for that using Gir, provide some
- // override here ??
- if (current.girname != GIR_GINITIALLYUNOWNED) {
- // declare type info
- out_decl << make_declare(false, ns + "::" + name, ctype) << std::endl;
- out_decl << std::endl;
- }
- ns_decl.pop();
- // now process the virtual method class parts
- if (ctx.options.classimpl && (kind == EL_OBJECT || kind == EL_INTERFACE)) {
- try {
- process_element_record_class(entry, interfaces, oss_class_decl.str(),
- oss_class_impl.str(), vmethods, out_decl, out_impl);
- } catch (const skip &ex) {
- logger(Log::WARNING, "skipping class generation for {}; {}", name,
- ex.what());
- }
- }
- return std::make_tuple(fname_decl, fname_impl);
- }
- std::string handle_exception(
- const pt::ptree::value_type &n, const std::runtime_error &ex) const
- {
- auto &el = n.first;
- const auto &name = get_name(n.second, std::nothrow);
- auto ex_skip = dynamic_cast<const skip *>(&ex);
- if (!ex_skip || ex_skip->cause == skip::INVALID) {
- auto msg = fmt::format("{} {} {}; {}",
- (ex_skip ? "skipping" : "EXCEPTION processing"), el, name, ex.what());
- Log level = ex_skip ? Log::WARNING : Log::ERROR;
- if (check_suppression(ns, el, name))
- level = Log::DEBUG;
- logger(level, msg);
- } else {
- logger(Log::DEBUG, "discarding {} {}; {}", el, name, ex.what());
- }
- return name;
- }
- typedef std::function<void(const pt::ptree::value_type &)> entry_processor;
- void process_entries(const pt::ptree &node, const entry_processor &proc)
- {
- for (auto it = node.begin(); it != node.end(); ++it) {
- auto &&n = *it;
- if (n.first == PT_ATTR)
- continue;
- try {
- proc(n);
- } catch (std::runtime_error &ex) {
- auto name = handle_exception(n, ex);
- // in any case give up on this name
- ctx.repo.discard(name);
- }
- }
- }
- bool visit_ok(const pt::ptree::value_type &n) const
- {
- const auto &girname = get_name(n.second, std::nothrow);
- auto ninfo = ctx.repo.lookup(girname);
- if (ninfo && !(ninfo->info && (ninfo->info->flags & TYPE_PREDEFINED))) {
- logger(Log::LOG, "visiting {} {}", n.first, girname);
- return true;
- } else {
- return false;
- }
- }
- const char *process_libs()
- {
- // also find needed shared libraries in case of dlopen
- auto sl = get_attribute(tree_, AT_SHARED_LIBRARY, "");
- std::vector<std::string> shlibs, qshlibs;
- boost::split(shlibs, sl, boost::is_any_of(","));
- for (auto &l : shlibs)
- if (l.size())
- qshlibs.push_back(fmt::format("\"{}\"", l));
- auto h_libs = "_libs.hpp";
- File libs(ns, h_libs);
- auto libs_templ = R"|(
- namespace internal {{
- GI_INLINE_DECL std::vector<const char*> _libs()
- {{ return {{{0}}}; }}
- }} // internal
- )|";
- libs << fmt::format(libs_templ, boost::join(qshlibs, ","));
- return h_libs;
- }
- public:
- std::string process_tree(const std::vector<std::string> &dep_headers)
- {
- logger(Log::INFO, "processing namespace {} {}", ns, version_);
- // set state for ns processing
- ctx.repo.set_ns(ns);
- File::set_root(ctx.options.rootdir);
- // check if deprecated should pass for this ns
- allow_deprecated_ = ctx.match_ignore.matches("deprecated", ns, {version_});
- // optionally process libs
- auto h_libs = ctx.options.dl ? process_libs() : "";
- auto h_types = "_types.hpp";
- File types(ns, h_types);
- // index run
- // collect info by name
- // also gather alias/type info
- entry_processor proc_index = [&](const pt::ptree::value_type &n) {
- const auto &name = get_name(n.second, std::nothrow);
- auto deprecated = get_attribute<int>(n.second, AT_DEPRECATED, 0);
- if (deprecated && !allow_deprecated_)
- return;
- auto &el = n.first;
- // redirect to oblivion
- // empty name might originate from a glib:boxed with glib:name attribute
- // discard those as well, as that is a glib type without C-type
- // (which are not really useful in our situation)
- if (name.empty() || ctx.match_ignore.matches(ns, el, {name})) {
- logger(Log::INFO, "ignoring {} {}", el, name);
- } else {
- ctx.repo.add(name, n);
- if (n.first == EL_ALIAS && visit_ok(n)) {
- process_element_alias(n.second, types);
- }
- }
- };
- process_entries(tree_, proc_index);
- // process basic types and callbacks
- auto h_enums = "_enums.hpp";
- auto h_flags = "_flags.hpp";
- auto h_constants = "_constants.hpp";
- auto h_callbacks = "_callbacks.hpp";
- auto h_callbacks_impl = "_callbacks_impl.hpp";
- auto h_functions = "_functions.hpp";
- auto h_functions_impl = "_functions_impl.hpp";
- File enums(ns, h_enums, false);
- File flags(ns, h_flags, false);
- File constants(ns, h_constants);
- File callbacks(ns, h_callbacks);
- File callbacks_impl(ns, h_callbacks_impl);
- File functions(ns, h_functions);
- File functions_impl(ns, h_functions_impl);
- entry_processor proc_pass_1 = [&](const pt::ptree::value_type &n) {
- auto &el = n.first;
- if (el == EL_ENUM && visit_ok(n)) {
- process_element_enum(n, enums, nullptr);
- } else if (el == EL_FLAGS && visit_ok(n)) {
- process_element_enum(n, flags, nullptr);
- } else if (el == EL_CONST && visit_ok(n)) {
- process_element_const(n.second, constants);
- }
- };
- process_entries(tree_, proc_pass_1);
- // check class types we can handle and will provide a definition for
- entry_processor proc_pass_class = [&](const pt::ptree::value_type &n) {
- auto &el = n.first;
- if ((el == EL_RECORD || el == EL_OBJECT || el == EL_INTERFACE) &&
- visit_ok(n)) {
- process_element_record(n, true);
- }
- };
- process_entries(tree_, proc_pass_class);
- // so the known classes will be declared/defined
- // check what callback typedefs that allows for
- // check class types we can handle and will provide a definition for
- std::set<std::string> cb_deps;
- std::ostringstream cb_decl;
- entry_processor proc_pass_callbacks = [&](const pt::ptree::value_type &n) {
- auto &el = n.first;
- if (el == EL_CALLBACK && visit_ok(n)) {
- std::ostringstream null;
- process_element_function(n, cb_decl, callbacks_impl, "", "", cb_deps);
- }
- };
- process_entries(tree_, proc_pass_callbacks);
- // write callbacks
- // need to declare deps first
- callbacks << make_dep_declare(cb_deps);
- callbacks << std::endl;
- callbacks << cb_decl.str();
- // now we know all supported types and supported callbacks
- // pass over class types again and fill in
- std::set<std::pair<std::string, std::string>> includes;
- std::set<std::string> dummy;
- entry_processor proc_pass_2 = [&](const pt::ptree::value_type &n) {
- auto &el = n.first;
- if ((el == EL_RECORD || el == EL_OBJECT || el == EL_INTERFACE) &&
- visit_ok(n)) {
- auto &&res = process_element_record(n, false);
- includes.insert({std::get<0>(res), std::get<1>(res)});
- } else if (el == EL_FUNCTION && visit_ok(n)) {
- process_element_function(n, functions, functions_impl, "", "", dummy);
- } else if ((el == EL_ENUM || el == EL_FLAGS) && visit_ok(n)) {
- process_element_enum(n, functions, &functions_impl);
- }
- };
- process_entries(tree_, proc_pass_2);
- auto add_stub_include = [this](const std::string &suffix) {
- auto fpath = (fs::path(tolower(ns)) / (tolower(ns) + suffix)).native();
- return make_conditional_include(fpath, false);
- };
- auto add_stub_define = [this](bool impl, bool begin) {
- auto nsu = toupper(ns);
- auto infix = impl ? "IMPL_" : "";
- auto suffix = begin ? "BEGIN" : "END";
- auto macro = fmt::format("GI_INCLUDE_{}{}_{}", infix, nsu, suffix);
- // avoid deprecated warning floods
- auto guard = begin ? GI_DISABLE_DEPRECATED_WARN_BEGIN
- : GI_DISABLE_DEPRECATED_WARN_END;
- // also allow for custom/override tweak
- return fmt::format("{}\n\n#ifdef {}\n{}\n#endif", guard, macro, macro);
- };
- auto h_ns = tolower(ns) + ".hpp";
- auto h_ns_impl = tolower(ns) + "_impl.hpp";
- // generate overall includes
- File nsh(ns, h_ns, false);
- // enable dl load coding
- if (h_libs && *h_libs) {
- nsh << "#ifndef GI_DL" << std::endl;
- nsh << "#define GI_DL 1" << std::endl;
- nsh << "#endif" << std::endl;
- }
- if (ctx.options.expected) {
- nsh << "#ifndef GI_EXPECTED" << std::endl;
- nsh << "#define GI_EXPECTED 1" << std::endl;
- nsh << "#endif" << std::endl;
- }
- if (ctx.options.const_method) {
- nsh << "#ifndef GI_CONST_METHOD" << std::endl;
- nsh << "#define GI_CONST_METHOD 1" << std::endl;
- nsh << "#endif" << std::endl;
- }
- nsh << make_include("gi/gi.hpp", false) << std::endl;
- nsh << std::endl;
- // include gi deps
- for (auto &&d : dep_headers)
- nsh << make_include(d, false) << std::endl;
- nsh << std::endl;
- // package includes
- // in some cases, these are totally missing
- // so the headers will have to be supplied by extra header override
- // repo supplied
- nsh << add_stub_include("_setup_pre_def.hpp") << std::endl;
- // user supplied
- nsh << add_stub_include("_setup_pre.hpp") << std::endl;
- // include the above before the includes
- // (so as to allow tweaking some defines in the overrides)
- auto node = root_.get_child(EL_REPOSITORY);
- auto p = node.equal_range(EL_CINCLUDE);
- for (auto &q = p.first; q != p.second; ++q) {
- // in some buggy cases, additional headers may have a full path
- // let's only keep the last part
- auto name = get_name(q->second);
- if (name.size() && name[0] == '/') {
- auto pos = name.rfind('/');
- pos = (pos != name.npos && pos) ? name.rfind('/', pos - 1) : pos;
- if (pos != name.npos)
- name = name.substr(pos + 1);
- }
- nsh << make_include(name, false) << std::endl;
- }
- // we may also need to tweak or fix things after usual includes
- // repo supplied
- nsh << add_stub_include("_setup_post_def.hpp") << std::endl;
- // user supplied
- nsh << add_stub_include("_setup_post.hpp") << std::endl;
- nsh << std::endl;
- // guard begin
- nsh << add_stub_define(false, true) << std::endl;
- nsh << std::endl;
- // various basic declaration parts
- for (auto &h : {h_types, h_enums, h_flags, h_constants, h_callbacks})
- nsh << make_include(h, true) << std::endl;
- nsh << std::endl;
- // declarations
- for (auto &h : includes)
- nsh << make_include(h.first, true) << std::endl;
- nsh << std::endl;
- // global functions when we have seen all else
- nsh << make_include(h_functions, true) << std::endl;
- nsh << std::endl;
- // allow for override/supplements
- // repo supplied
- nsh << add_stub_include("_extra_def.hpp") << std::endl;
- // user supplied
- nsh << add_stub_include("_extra.hpp") << std::endl;
- // guard end
- nsh << add_stub_define(false, false) << std::endl;
- nsh << std::endl;
- // include implementation header in inline case
- nsh << "#if defined(GI_INLINE) || defined(GI_INCLUDE_IMPL)" << std::endl;
- nsh << make_include(h_ns_impl, true) << std::endl;
- nsh << "#endif" << std::endl;
- nsh << std::endl;
- File nsh_impl(ns, h_ns_impl, false);
- // include declaration
- nsh_impl << make_include(h_ns, true) << std::endl;
- nsh_impl << std::endl;
- // guard begin
- nsh_impl << add_stub_define(true, true) << std::endl;
- nsh_impl << std::endl;
- // lib helper for symbol load
- nsh_impl << make_include(h_libs, true) << std::endl;
- nsh_impl << std::endl;
- // implementations
- for (auto &h : includes)
- nsh_impl << make_include(h.second, true) << std::endl;
- nsh_impl << std::endl;
- nsh_impl << make_include(h_callbacks_impl, true) << std::endl;
- nsh_impl << make_include(h_functions_impl, true) << std::endl;
- nsh_impl << std::endl;
- // likewise for override/supplement implementation
- // repo supplied
- nsh_impl << add_stub_include("_extra_def_impl.hpp") << std::endl;
- // user supplied
- nsh_impl << add_stub_include("_extra_impl.hpp") << std::endl;
- nsh_impl << std::endl;
- // guard end
- nsh_impl << add_stub_define(false, true) << std::endl;
- nsh_impl << std::endl;
- // a convenience cpp for non-inline
- auto cpp_ns = tolower(ns) + ".cpp";
- File cpp(ns, cpp_ns, false, false);
- cpp << make_include(h_ns_impl, true) << std::endl;
- return nsh.get_rel_path();
- }
- };
- } // namespace
- std::shared_ptr<NamespaceGenerator>
- NamespaceGenerator::new_(GeneratorContext &ctx, const std::string &filename)
- {
- return std::make_shared<NamespaceGeneratorImpl>(ctx, filename);
- }
|