#include "genbase.hpp" #include static std::string qualify(const std::string &ns, const std::string &cpptype, int flags) { if (cpptype.find(':') == cpptype.npos && !cpptype.empty() && !(flags & TYPE_BASIC)) return ns + "::" + cpptype; return cpptype; } GeneratorBase::GeneratorBase(GeneratorContext &_ctx, const std::string _ns) : ctx(_ctx), ns(_ns) {} std::string GeneratorBase::qualify(const std::string &cpptype, int flags) const { return ::qualify(ns, cpptype, flags); } void GeneratorBase::parse_typeinfo( const std::string &girname, TypeInfo &result) const { auto ti = ctx.repo.lookup(girname); if (ti && ti->info) result = *ti->info; } GeneratorBase::ArgInfo GeneratorBase::parse_arginfo(const pt::ptree &node, ArgInfo *routput) const { ArgInfo lresult; ArgInfo &result = routput ? *routput : lresult; const pt::ptree *pntype{}; std::string kind; // try most likely in turn for (auto &&v : {EL_TYPE, EL_ARRAY, EL_VARARGS}) { const auto &ntypeo = node.get_child_optional(v); if (ntypeo.is_initialized()) { kind = v; pntype = &ntypeo.get(); } } if (kind.empty() || !pntype) throw skip("type info not found"); if (kind == EL_VARARGS) throw skip("varargs not supported", skip::OK); auto &ntype = *pntype; bool discard_element = false; // no name for array if (kind == EL_ARRAY) { result.flags = TYPE_ARRAY; result.length = get_attribute(ntype, AT_LENGTH, -10); result.zeroterminated = get_attribute(ntype, AT_ZERO_TERMINATED, 1); result.fixedsize = get_attribute(ntype, AT_FIXED_SIZE, 0); if (result.length < 0 && !result.zeroterminated && !result.fixedsize) throw skip("inconsistent array info"); auto name = get_name(ntype, std::nothrow); // could be GBytes, etc if (name.size()) { // transform to plain argument if not ignored // FIXME maybe custom wrappers might be more convenient ?? if (ctx.match_ignore.matches(ns, EL_RECORD, {name})) throw skip(name + " array not supported", skip::OK); // fresh state and discard element info processing result = ArgInfo{}; discard_element = true; parse_typeinfo(name, result); } } else { parse_typeinfo(get_name(ntype), result); } // should be present (but not so for signal) // though const info seems to be missing in array case result.ctype = get_attribute(ntype, AT_CTYPE, ""); // special case where const is preserved for the cpp type if (result.girname == "gpointer" && is_const(result.ctype)) parse_typeinfo("gconstpointer", result); // handle element type info int i = 0; for (const auto &n : ntype) { if (n.first == "type" && !discard_element) { ArgTypeInfo &ti = i == 0 ? result.first : result.second; auto elname = get_name(n.second); parse_typeinfo(elname, ti); if (!ti.flags) throw skip("container element not supported", skip::OK); // probably not, but give it a shot ti.ctype = get_attribute(n.second, AT_CTYPE, ""); // avoid specialized bool vector if (ti.cpptype == "bool") ti.cpptype = "gboolean"; // some char arrays (e.g. g_regex* parameters) specify utf8 element type // along with ctype char rather than char* // so discard utf8 in that case and go for a plain array if (kind == EL_ARRAY && elname == "utf8" && ti.ctype == "gchar") parse_typeinfo(ti.ctype, ti); ++i; } } // sanity checks if ((result.flags & (TYPE_ARRAY | TYPE_LIST))) { if (i != 1) throw skip("inconsistent list element info"); if (result.flags & TYPE_ARRAY) { // NOTE apparently both are possible, see e.g. g_shell_parse_arg* // in that case it is handled zeroterminated // and the size param becomes a regular one if (result.zeroterminated) { result.length = -1; result.fixedsize = 0; } // 1 pointer level more than the basic type result.pdepth = ((result.first.flags & TYPE_CLASS) ? 1 : 0) + 1; } } else if (result.flags & TYPE_MAP) { if (i != 2) throw skip("inconsistent map element info"); } else { if (i != 0) throw skip("inconsistent type info"); } return result; } std::string GeneratorBase::make_ctype( const ArgInfo &info, const std::string &direction, bool callerallocates) { std::string result; bool out = !(direction == DIR_IN || direction == DIR_RETURN); if (info.flags & TYPE_ARRAY) { auto ret = info.first.argtype + GI_PTR; if (out && !callerallocates) ret += GI_PTR; result = ret; } else if (info.flags & TYPE_CALLBACK) { result = info.cpptype + "::cfunction_type"; } else { if (!out || callerallocates) { result = info.argtype; } else { result = info.argtype + GI_PTR; } } return (is_const(info.ctype) ? std::string("const ") : EMPTY) + result; } void GeneratorBase::track_dependency( std::set &deps, const ArgInfo &info) const { auto track = [&](const std::string &cpptype, int flags) { // other items (e.g. enums) are included before anyway if (flags & TYPE_CLASS && !(flags & (TYPE_BASIC | TYPE_TYPEDEF))) { // always track scoped assert(is_qualified(cpptype)); deps.insert(cpptype); if (flags & TYPE_BOXED) deps.insert(cpptype + GI_SUFFIX_REF); } }; if (!(info.flags & TYPE_CONTAINER)) { track(info.cpptype, info.flags); } else { track(info.first.cpptype, info.first.flags); if (info.flags & TYPE_MAP) track(info.second.cpptype, info.second.flags); } } bool GeneratorBase::check_suppression(const std::string &ns, const std::string &kind, const std::string &name) const { // always generate ctx.suppressions.insert(ctx.match_sup.format(ns, kind, name)); return ctx.match_sup.matches(ns, kind, {name}); } std::string GeneratorBase::make_wrap_format(const ArgInfo &info, const std::string &transfer, const std::string &outtype) { std::string fmts; auto format = "{}"; if (info.flags & TYPE_CLASS) { fmts = fmt::format(GI_NS_SCOPED + "wrap ({}, {})", format, get_transfer_parameter(transfer)); } else if (info.flags & TYPE_ENUM) { fmts = GI_NS_SCOPED + "wrap ({})"; } else if (info.flags & (TYPE_LIST | TYPE_ARRAY | TYPE_MAP)) { assert(!outtype.empty()); fmts = fmt::format(GI_NS_SCOPED + "wrap_to<{}>({}, {})", outtype, format, get_transfer_parameter(transfer)); } else { fmts = format; } return fmts; } std::string GeneratorBase::get_transfer_parameter(const std::string &transfer, bool _type) { std::map m{ {TRANSFER_NOTHING, "transfer_none"}, {TRANSFER_FULL, "transfer_full"}, {TRANSFER_CONTAINER, "transfer_container"}, }; auto it = m.find(transfer); if (it == m.end()) throw std::runtime_error("invalid transfer " + transfer); return GI_NS_SCOPED + it->second + (_type ? "_t" : ""); }