init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
allhaileris
2026-02-16 15:50:16 +03:00
commit afb81b8278
13816 changed files with 3689732 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
#ifndef COMMON_HPP
#define COMMON_HPP
#include <iostream>
#include <string>
#include "format.hpp"
namespace
{
const char GI_PTR = '*';
const std::string GI_SUFFIX_REF = "_Ref";
const std::string GI_SUFFIX_CF_CTYPE = "_CF_CType";
const std::string GI_SUFFIX_CB_TRAIT = "_CB_Trait";
const std::string GIR_GOBJECT("GObject.Object");
const std::string GIR_GINITIALLYUNOWNED("GObject.InitiallyUnowned");
const std::string GIR_GVARIANT("GLib.Variant");
const std::string GIR_VOID("none");
const std::string GIR_GDESTROYNOTIFY("GLib.DestroyNotify");
const std::string GDESTROYNOTIFY("GDestroyNotify");
const std::string CPP_VOID("void");
const std::string GIR_SUFFIX(".gir");
const std::string PT_ATTR("<xmlattr>");
const std::string GI_NS("gi");
const std::string GI_NS_INTERNAL("internal");
const std::string GI_NS_IMPL("impl");
const std::string GI_NS_ARGS("callargs");
const std::string GI_SCOPE("::");
const std::string GI_NS_SCOPED("gi::");
const std::string GI_NS_DETAIL_SCOPED("gi::detail::");
const std::string GI_REPOSITORY_NS("repository");
const std::string GI_INLINE("GI_INLINE_DECL");
const std::string GI_CLASS_IMPL_BEGIN("GI_CLASS_IMPL_BEGIN");
const std::string GI_CLASS_IMPL_END("GI_CLASS_IMPL_END");
const std::string GI_DISABLE_DEPRECATED_WARN_BEGIN(
"GI_DISABLE_DEPRECATED_WARN_BEGIN");
const std::string GI_DISABLE_DEPRECATED_WARN_END(
"GI_DISABLE_DEPRECATED_WARN_END");
const std::string EMPTY;
const std::string EL_REPOSITORY("repository");
const std::string EL_CINCLUDE("c:include");
const std::string EL_ALIAS("alias");
const std::string EL_ENUM("enumeration");
const std::string EL_FLAGS("bitfield");
const std::string EL_MEMBER("member");
const std::string EL_CONST("constant");
const std::string EL_OBJECT("class");
const std::string EL_INTERFACE("interface");
const std::string EL_RECORD("record");
const std::string EL_CALLBACK("callback");
const std::string EL_FUNCTION("function");
const std::string EL_CONSTRUCTOR("constructor");
const std::string EL_METHOD("method");
const std::string EL_VIRTUAL_METHOD("virtual-method");
const std::string EL_FIELD("field");
const std::string EL_PROPERTY("property");
const std::string EL_SIGNAL("glib:signal");
const std::string EL_RETURN("return-value");
const std::string EL_PARAMETERS("parameters");
const std::string EL_INSTANCE_PARAMETER("instance-parameter");
const std::string EL_PARAMETER("parameter");
const std::string EL_TYPE("type");
const std::string EL_ARRAY("array");
const std::string EL_VARARGS("varargs");
const std::string EL_IMPLEMENTS("implements");
const std::string AT_SHARED_LIBRARY("shared-library");
const std::string AT_DEPRECATED("deprecated");
const std::string AT_INTROSPECTABLE("introspectable");
const std::string AT_FOREIGN("foreign");
const std::string AT_SHADOWS("shadows");
const std::string AT_SHADOWED_BY("shadowed-by");
const std::string AT_DISGUISED("disguised");
const std::string AT_MOVED_TO("moved-to");
const std::string AT_VERSION("version");
const std::string AT_NAME("name");
const std::string AT_TRANSFER("transfer-ownership");
const std::string AT_DIRECTION("direction");
const std::string AT_CLOSURE("closure");
const std::string AT_DESTROY("destroy");
const std::string AT_SCOPE("scope");
const std::string AT_NULLABLE("nullable");
const std::string AT_OPTIONAL("optional");
const std::string AT_ALLOW_NONE("allow-none");
const std::string AT_CALLER_ALLOCATES("caller-allocates");
const std::string AT_GLIB_GET_TYPE("glib:get-type");
const std::string AT_GLIB_FUNDAMENTAL("glib:fundamental");
const std::string AT_GLIB_TYPE_STRUCT("glib:type-struct");
const std::string AT_GLIB_IS_TYPE_STRUCT_FOR("glib:is-gtype-struct-for");
const std::string AT_PARENT("parent");
const std::string AT_LENGTH("length");
const std::string AT_ZERO_TERMINATED("zero-terminated");
const std::string AT_FIXED_SIZE("fixed-size");
const std::string AT_THROWS("throws");
const std::string AT_CTYPE("c:type");
const std::string AT_CIDENTIFIER("c:identifier");
const std::string TRANSFER_NOTHING("none");
const std::string TRANSFER_FULL("full");
const std::string TRANSFER_CONTAINER("container");
const std::string SCOPE_NOTIFIED("notified");
const std::string SCOPE_ASYNC("async");
const std::string SCOPE_CALL("call");
const std::string DIR_IN("in");
const std::string DIR_OUT("out");
const std::string DIR_INOUT("inout");
const std::string DIR_RETURN("return");
const std::string AT_READABLE("readable");
const std::string AT_WRITABLE("writable");
const std::string AT_PRIVATE("private");
} // namespace
enum class Log { NONE, ERROR, WARNING, INFO, DEBUG, LOG };
extern Log _loglevel;
template<typename T>
void
logger(Log level, const T &m)
{
static std::string lvl[] = {"NONE", "ERROR", "WARN", "INFO", "DEBUG", "LOG"};
if (level <= _loglevel)
std::cerr << lvl[(int)level] << " " << m << std::endl;
}
template<typename T, typename ARG, typename... ARGS>
void
logger(Log level, const T &m, ARG &&arg, ARGS &&...args)
{
logger(level,
fmt::format(m, std::forward<ARG>(arg), std::forward<ARGS>(args)...));
}
inline bool
is_qualified(const std::string &name)
{
return (name.find(':') != name.npos) || (name.find('.') != name.npos);
}
inline std::string
toupper(const std::string &s)
{
auto c = s;
for (auto &ch : c)
ch = std::toupper(ch);
return c;
}
inline std::string
tolower(const std::string &s)
{
auto c = s;
for (auto &ch : c)
ch = std::tolower(ch);
return c;
}
#endif // COMMON_HPP

View File

@@ -0,0 +1,514 @@
#include "common.hpp"
#include "fs.hpp"
#include "genbase.hpp"
#include "genns.hpp"
#include "repository.hpp"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <map>
#include <set>
#include <vector>
// thanks go to glib
#define GI_STRINGIFY(macro_or_string) GI_STRINGIFY_ARG(macro_or_string)
#define GI_STRINGIFY_ARG(contents) #contents
#ifndef DEFAULT_IGNORE_FILE
// embed ignore data
#include "ignore.hpp"
static std::string GI_DEFAULT_IGNORE;
#else
static const char *GI_DATA_IGNORE = "";
static std::string GI_DEFAULT_IGNORE{GI_STRINGIFY(DEFAULT_IGNORE_FILE)};
#endif
static const char PATH_SEP = '/';
static const std::string GIR_SUBDIR{"gir-1.0"};
Log _loglevel = Log::WARNING;
class Generator
{
GeneratorContext &ctx_;
std::vector<std::string> girdirs_;
// processes ns (ns: ns header)
std::map<std::string, std::string> processed_;
public:
Generator(GeneratorContext &_ctx, const std::vector<std::string> &girdirs)
: ctx_(_ctx), girdirs_(girdirs)
{}
std::string find_in_dir(const fs::path p, const std::string &ns) const
{
std::string result;
fs::error_code ec;
// check if in this directory
if (!fs::is_directory(p, ec))
return "";
auto f = p / (ns + GIR_SUFFIX);
std::vector<fs::directory_entry> dirs;
for (auto &&entry : fs::directory_iterator(p, ec)) {
if (fs::is_directory(entry, ec)) {
dirs.emplace_back(entry);
} else if (f == entry) {
// exact match
result = f.native();
} else {
// non-version match
auto ename = entry.path().filename().native();
auto s = ns.size();
if (ename.substr(0, s) == ns && ename.size() > s && ename[s] == '-')
result = entry.path().native();
}
}
// check dirs
while (!dirs.empty() && result.empty()) {
result = find_in_dir(dirs.back(), ns);
dirs.pop_back();
}
return result;
}
std::string find(const std::string &ns) const
{
for (auto &&d : girdirs_) {
auto res = find_in_dir(fs::path(d), ns);
if (!res.empty())
return res;
}
return "";
}
// gir might be:
// + a GIR filename
// + or a GIR namespace (ns-version)
// + or a GIR namespace (no version appended)
void generate(const std::string &gir, bool recurse)
{
fs::path f(gir);
fs::error_code ec;
auto path = fs::exists(f, ec) ? gir : find(gir);
if (path.empty())
throw std::runtime_error("could not find GIR for " + gir);
// avoid reading if possible
if (processed_.count(gir))
return;
auto genp = NamespaceGenerator::new_(ctx_, path);
auto &gen = *genp;
// normalize namespace
auto &&ns = gen.get_ns();
// prevent duplicate processing
if (processed_.count(ns))
return;
// generate deps
auto &&deps = gen.get_dependencies();
std::vector<std::string> headers;
if (recurse) {
for (auto &&d : deps) {
generate(d, recurse);
// should be available now
headers.emplace_back(processed_[d]);
}
}
// now generate this one
// also mark processed and retain ns header file
processed_[ns] = gen.process_tree(headers);
}
};
static std::string
wrap(const char *s)
{
std::string res;
if (s)
res = s;
return res;
}
static int
die(const std::string &desc, const std::string &msg = "")
{
std::cout << msg << std::endl << std::endl;
std::cout << desc << std::endl;
return 1;
}
static std::string
make_subdir(const std::string &dir, const std::string &subdir)
{
auto result = dir;
if (subdir.size()) {
assert(subdir.front() != PATH_SEP);
if (result.back() != PATH_SEP)
result += PATH_SEP;
result += subdir;
}
return result;
}
static void
addsplit(std::vector<std::string> &target, const std::string &src,
const std::string &suffix = "", const std::string &seps = ":")
{
std::vector<std::string> tmp;
boost::split(tmp, src, boost::is_any_of(seps));
for (auto &&d : tmp) {
if (d.size()) {
target.emplace_back(make_subdir(d, suffix));
}
}
}
namespace options
{
using OptionParseFunc = std::function<bool(const char *)>;
struct Option
{
std::string arg;
OptionParseFunc func;
};
Option
make_parser(bool *val)
{
auto h = [val](const char *v) {
// no arg for command-line option, set to true in that case
*val = v ? atoi(v) : true;
return true;
};
return {"", h};
}
Option
make_parser(int *val)
{
auto h = [val](const char *nextarg) {
try {
*val = std::stoi(nextarg);
return true;
} catch (const std::exception &exc) {
return false;
}
};
return {"number", h};
}
Option
make_parser(std::string *val)
{
auto h = [val](const char *nextarg) {
*val = nextarg;
return true;
};
return {"arg", h};
}
} // namespace options
int
main(int argc, char *argv[])
{
(void)argc;
(void)argv;
using namespace options;
// env/options targets
int debug_level{};
std::string fpath_ignore;
std::string fpath_suppress{};
std::string fpath_gen_suppress;
std::string output_dir;
bool doclass{};
bool dofullclass{};
bool use_dl{};
bool use_expected{};
bool const_method{};
bool output_top{};
int call_args{-1};
bool basic_collection{};
bool dump_ignore{};
std::string gir_path;
std::string helpdesc;
struct OptionData
{
std::string opt;
std::string var;
const char *desc;
Option option{};
};
std::vector<OptionData> descs = {
{"help", "", "produce help message"},
{"debug", "GI_DEBUG", "debug level", make_parser(&debug_level)},
{"ignore", "GI_IGNORE", "colon separated ignore files",
make_parser(&fpath_ignore)},
{"suppression", "GI_SUPPRESSION", "colon separated suppression files",
make_parser(&fpath_suppress)},
{"gen-suppression", "G_GEN_SUPPRESSION", "generate suppression file",
make_parser(&fpath_gen_suppress)},
{"output", "GI_OUTPUT", "output directory", make_parser(&output_dir)},
{"gir-path", "GI_GIR_PATH", "colon separated GIR search path",
make_parser(&gir_path)},
{"class", "GI_CLASS", "generate class implementation",
make_parser(&doclass)},
{"class-full", "GI_CLASS_FULL", "generate fallback class methods",
make_parser(&dofullclass)},
{"dl", "GI_DL", "use dynamic dlopen/dlsym rather than static link",
make_parser(&use_dl)},
{"expected", "GI_EXPECTED", "use expected<> return rather than exception",
make_parser(&use_expected)},
{"const-method", "GI_CONST_METHOD", "generate const methods",
make_parser(&const_method)},
{"output-top", "GI_OUTPUT_TOP",
"generate convenience wrappers in output dir",
make_parser(&output_top)},
{"call-args", "GI_CALL_ARGS",
"(if >= 0) min #optional arguments to enable a CallArgs variant",
make_parser(&call_args)},
{"basic-collection", "GI_BASIC_COLLECTION",
"also generate collection for input collection of basic type",
make_parser(&basic_collection)},
};
// optionally dump embedded ignore
if (*GI_DATA_IGNORE) {
descs.push_back({"dump-ignore", "", "dump embedded ignore data",
make_parser(&dump_ignore)});
}
std::map<std::string, OptionData *> descs_index;
// first collect settings from environment
for (auto &e : descs) {
// build index
descs_index[e.opt] = &e;
// check env var
if (!e.var.empty() && e.option.func) {
if (auto v = getenv(e.var.c_str())) {
e.option.func(v);
}
}
}
// non-option argument analogue
auto gir_top = wrap(getenv("GI_GIR"));
// into settings
std::vector<std::string> girs, ignore_files, suppress_files;
std::vector<std::string> girdirs;
// collect ignore files
auto fpath_ignore_env = std::move(fpath_ignore);
// suppress files
auto fpath_suppress_env = std::move(fpath_suppress);
// GIR path
auto gir_path_env = std::move(gir_path);
{ // basic command line processing
// assemble help description
auto tmpl = (R"|(
{} [options] girs...
Supported options and environment variables
(specify 0 or 1 as environment variable value for a boolean switch):
)|");
helpdesc = fmt::format(tmpl, argv[0]);
for (auto &entry : std::as_const(descs)) {
std::string var = entry.var;
var = !var.empty() ? fmt::format("[{}] ", var) : var;
helpdesc += fmt::format(" --{:<25}{}{}\n",
entry.opt + ' ' + entry.option.arg, var, entry.desc);
}
if (!GI_DEFAULT_IGNORE.empty()) {
helpdesc +=
fmt::format("\nDefault ignore files:\n{}\n", GI_DEFAULT_IGNORE);
}
// simple command line processing
for (int i = 1; i < argc; ++i) {
std::string opt = argv[i];
if (opt == "-h" || opt == "--help")
return die(helpdesc);
if (opt.size() > 2 && opt.find("--") == 0) {
auto oi = descs_index.find(opt.substr(2));
if (oi != descs_index.end()) {
assert(oi->second);
auto &option = *oi->second;
assert(option.option.func);
char *nextarg = nullptr;
if (!option.option.arg.empty()) {
if (i + 1 >= argc) {
return die(helpdesc, opt + "; missing argument");
} else {
nextarg = argv[i + 1];
++i;
}
}
logger(Log::LOG,
fmt::format("processing option {} {}", opt, wrap(nextarg)));
if (!option.option.func(nextarg))
return die(helpdesc, opt + "; invalid argument " + nextarg);
} else {
return die(helpdesc, "unknown option " + opt);
}
} else if (!opt.empty() && opt[0] == '-') {
return die(helpdesc, "unknown option " + opt);
} else {
girs.push_back(opt);
}
}
}
if (dump_ignore) {
std::cout << GI_DATA_IGNORE << std::endl;
return 0;
}
// collect some more files
addsplit(ignore_files, fpath_ignore);
addsplit(suppress_files, fpath_suppress);
addsplit(girdirs, gir_path);
// add env specified
addsplit(ignore_files, fpath_ignore_env);
addsplit(suppress_files, fpath_suppress_env);
addsplit(girdirs, gir_path_env);
// level
if (debug_level > 0)
_loglevel = (Log)debug_level;
// system default
{
// gobject-introspection considers XDG_DATA_HOME first
// (essentially g_get_user_data_dir)
auto xdg_data_home = wrap(getenv("XDG_DATA_HOME"));
if (xdg_data_home.empty()) {
xdg_data_home = wrap(getenv("HOME"));
if (!xdg_data_home.empty()) {
if (xdg_data_home.back() != PATH_SEP)
xdg_data_home += PATH_SEP;
xdg_data_home += fmt::format(".local{}share", PATH_SEP);
}
}
std::vector<std::string> default_gir_dirs;
if (!xdg_data_home.empty())
default_gir_dirs.push_back(xdg_data_home);
// gobject-introspection uses XDG_DATA_DIRS next
// (essentially g_get_system_data_dirs)
auto xdg_data_dirs = wrap(getenv("XDG_DATA_DIRS"));
addsplit(default_gir_dirs, xdg_data_dirs);
// g_get_system_data_dirs optionally falls back to fixed /usr[/local] now
// but that would then precede custom paths, which is a bit unfortunate
// so, instead, consider those only as the very last resort (below)
for (auto &d : default_gir_dirs) {
girdirs.push_back(make_subdir(d, GIR_SUBDIR));
}
}
// extra GIR paths that gobject-introspection considers
#ifdef GI_GIR_DIR
girdirs.push_back(GI_STRINGIFY(GI_GIR_DIR));
#endif
#ifdef GI_DATA_DIR
girdirs.push_back(GI_STRINGIFY(GI_DATA_DIR));
#endif
#ifdef DEFAULT_GIRPATH
// optional (hard) fallback
addsplit(girdirs, GI_STRINGIFY(DEFAULT_GIRPATH), GIR_SUBDIR);
#endif
for (auto &&d : girdirs)
logger(Log::DEBUG, "extending GIR path " + d);
// system default
if (!GI_DEFAULT_IGNORE.empty())
addsplit(ignore_files, GI_DEFAULT_IGNORE);
// collect girs to process
addsplit(girs, gir_top);
// sanity check
if (output_dir.empty())
return die(helpdesc, "missing output directory");
if (girs.empty())
return die(helpdesc, "nothing to process");
// check for now
if (girdirs.empty())
return die(helpdesc, "empty search path");
// at least the standard ignore file is required
// or things will go wrong
int cnt = 0;
fs::error_code ec;
for (auto &f : ignore_files)
cnt += fs::exists(f, ec);
if (cnt == 0 && !GI_DEFAULT_IGNORE.empty())
return die(helpdesc, "required default ignore file location not specified");
// HACKety hack; extract some other config from ignore files
// (avoids coming up with another separate config file for now)
std::map<std::string, std::string> custom_c_types;
auto custom = [&custom_c_types](const std::string &line) {
if (line.find("#!ctype:") == 0) {
std::vector<std::string> tmp;
addsplit(tmp, line);
if (tmp.size() == 3) {
custom_c_types[tmp[1]] = tmp[2];
}
}
};
auto match_ignore = Matcher(ignore_files, GI_DATA_IGNORE, custom);
auto match_suppress = Matcher(suppress_files);
// now let's start
GeneratorOptions options;
options.rootdir = output_dir;
options.classimpl = doclass;
options.classfull = dofullclass;
options.dl = use_dl;
options.expected = use_expected;
options.const_method = const_method;
options.output_top = output_top;
options.call_args = call_args;
options.basic_collection = basic_collection;
logger(Log::INFO, "generating to directory {}", options.rootdir);
auto repo = Repository::new_(custom_c_types);
std::set<std::string> suppressions;
GeneratorContext ctx{
options, *repo, match_ignore, match_suppress, suppressions};
Generator gen(ctx, girdirs);
try {
for (auto &&g : girs)
gen.generate(g, true);
// write suppression
if (fpath_gen_suppress.size()) {
std::vector<std::string> sup(suppressions.begin(), suppressions.end());
sort(sup.begin(), sup.end());
logger(Log::INFO, "writing {} suppressions to {}", sup.size(),
fpath_gen_suppress);
std::ofstream fsup(fpath_gen_suppress);
for (auto &&v : sup)
fsup << v << std::endl;
}
} catch (std::runtime_error &ex) {
logger(Log::ERROR, ex.what());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,25 @@
#ifndef FORMAT_HPP
#if __cplusplus >= 202002L
// available as of gcc-13
// also requires/checks the above guard on C++20
#include <format>
#include <string_view>
namespace fmt
{
template<typename... Args>
inline std::string
format(std::string_view format, Args &&...args)
{
return std::vformat(format, std::make_format_args(args...));
}
} // namespace fmt
#else
// use original fmtlib
#include <fmt/format.h>
#endif
#endif // FORMAT_HPP

13
cmake/external/glib/cppgir/tools/fs.hpp vendored Normal file
View File

@@ -0,0 +1,13 @@
#ifndef FS_HPP
#define FS_HPP
#include <filesystem>
#include <system_error>
namespace fs
{
using namespace std::filesystem;
using error_code = std::error_code;
} // namespace fs
#endif // FS_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
#ifndef FUNCTION_HPP
#define FUNCTION_HPP
#include "genbase.hpp"
#include <iosfwd>
#include <map>
#include <set>
#include <string>
#include <vector>
struct ElementFunction
{
// type of function; method, etc
std::string kind;
// GIR name
std::string name;
// otherwise descriptive name (e.g. original C name)
std::string c_id;
// expression defining function
std::string functionexp;
bool throws{};
// represents symbol in lib
// (which can be linked to or dlsym'ed)
bool lib_symbol{};
std::string shadows;
};
// parameter data
constexpr static const int INDEX_DEFAULT = -10;
struct FunctionParameter
{
std::string name;
GeneratorBase::ArgInfo tinfo{};
// deduced C type
std::string ptype;
bool instance{};
std::string direction;
std::string transfer{TRANSFER_NOTHING};
// index
int closure{INDEX_DEFAULT}, destroy{INDEX_DEFAULT};
int callerallocates{};
std::string scope;
bool optional{};
bool nullable{};
};
using Parameter = FunctionParameter;
// info defining function to construct declaration/definition
// (some parts not relevant for signal/callback)
struct FunctionDefinition
{
// return (and optionally additional outputs)
struct Output
{
// cpp type of output
std::string type;
// expression (value of output)
std::string value;
};
struct ArgTrait
{
// transfer (original attribute)
std::string transfer;
// inout parameter
bool inout{};
// applicable/relevant arguments (indexed as usual; see below)
// usually only 1 (to 1)
// for callback; function, userdata[, destroy]
// for sized arrays; data, size
// (other containers only need 1 and handled as usual)
std::vector<int> args;
// custom type trait
std::string custom{};
};
// GIR name (empty if not valid)
std::string name;
// statements preceding wrapped call (excluding final ;)
std::vector<std::string> pre_call;
// idem, post call
std::vector<std::string> post_call;
// assembled outputs (first one is return value, if any, could be empty)
std::vector<Output> cpp_outputs;
// parts that make up the C call (...)
// indexed by param number (instance = -1)
std::map<int, std::string> c_call;
// parts that make up the ( ... ) decl/def
// (similarly indexed/sorted)
std::map<int, std::string> cpp_decl;
// callee; trait info of (callback) parameters
// (lowest one is for return; always present)
// (except if fallback virtual method)
std::map<int, ArgTrait> arg_traits;
// extra parameters in a callee (= cb) declaration not present in callforward
// (to deal with cb sized array output0
std::map<int, std::string> cpp_decl_extra;
// function (expression) to call
std::string c_callee;
// format with single placeholder for call (constructed based on all above)
std::string ret_format;
// parts used for callforward generation of callback type
// (callforward = C++ signature which then calls a C function)
// (callback = C signature which then calls a C++ function)
// typedef of C function to call
std::string cf_ctype;
// (derived) C signature (without instance parameter if virtual method)
std::string c_sig;
};
std::string make_arg_traits(
const std::map<int, FunctionDefinition::ArgTrait> &traits,
const std::string &c_sig);
FunctionDefinition process_element_function(GeneratorContext &_ctx,
const std::string _ns, const pt::ptree::value_type &entry,
std::ostream &out, std::ostream &impl, const std::string &klass,
const std::string &klasstype, GeneratorBase::DepsSet &deps,
std::ostream *call_args, bool allow_deprecated);
FunctionDefinition process_element_function(GeneratorContext &_ctx,
const std::string _ns, const ElementFunction &func,
const std::vector<Parameter> &params, std::ostream &out, std::ostream &impl,
const std::string &klass, const std::string &klasstype,
GeneratorBase::DepsSet &deps);
#endif // FUNCTION_HPP

View File

@@ -0,0 +1,222 @@
#include "genbase.hpp"
#include <map>
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<int>(ntype, AT_LENGTH, -10);
result.zeroterminated = get_attribute<int>(ntype, AT_ZERO_TERMINATED, 1);
result.fixedsize = get_attribute<int>(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(DepsSet &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<std::string, std::string> 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" : "");
}

View File

@@ -0,0 +1,115 @@
#ifndef GENBASE_HPP
#define GENBASE_HPP
#include "common.hpp"
#include "genutils.hpp"
#include "repository.hpp"
#include <set>
struct GeneratorOptions
{
// dir in which to generate
std::string rootdir;
// generate implementation classes
bool classimpl;
// generate fallback methods (for class implementation)
bool classfull;
// use dlopen/dlsym for generated call
bool dl;
// use expected<> return iso exception throwing
bool expected;
// generate const methods
bool const_method;
// generate top-level helpers in rootdir
bool output_top;
// min number of non-required function arguments
// that triggers generation a CallArgs variant
int call_args;
// also generate collection signature for input collection of basic type
bool basic_collection;
};
struct GeneratorContext
{
GeneratorOptions &options;
Repository &repo;
const Matcher &match_ignore;
const Matcher &match_sup;
// generated during processing
std::set<std::string> &suppressions;
};
class GeneratorBase
{
protected:
GeneratorContext &ctx;
std::string ns;
const std::string indent = " ";
public:
GeneratorBase(GeneratorContext &_ctx, const std::string _ns);
struct ArgTypeInfo : public TypeInfo
{
// c:type as parsed from type element
// (no const info for array or filename arg)
// (empty for void, and possibly property or signal)
std::string ctype;
// return CppType or reference CppType (if non-owning boxed transfer)
std::string cppreftype(const std::string transfer) const
{
if ((flags & TYPE_BOXED) && transfer != TRANSFER_FULL)
return cpptype + GI_SUFFIX_REF;
// string case
if ((flags & TYPE_CLASS) && (flags & TYPE_BASIC) &&
transfer != TRANSFER_FULL)
return cpptype + "_v";
return cpptype;
}
};
// the above along with info on contained element
// (as provided typically within <type> element in parameter or so)
// * first only relevant for array, lists and maps
// * second only for maps
// * first and second do not have ctype info
// * in case of array, no cpptype info on primary
// * in case of (GS)List etc, primary specifies the particular type
struct ArgInfo : public ArgTypeInfo
{
// container element types
ArgTypeInfo first, second;
// array
int length = -1;
bool zeroterminated = false;
int fixedsize = 0;
};
std::string qualify(const std::string &cpptype, int flags) const;
void parse_typeinfo(const std::string &girname, TypeInfo &result) const;
// if routput != nullptr, also place result in output
// (possibly a partial one if exception is thrown)
ArgInfo parse_arginfo(
const pt::ptree &node, ArgInfo *routput = nullptr) const;
static std::string make_ctype(
const ArgInfo &info, const std::string &direction, bool callerallocates);
// set of (ns, dep), where dep = [struct|class ]type
using DepsSet = std::set<std::pair<std::string, std::string>>;
void track_dependency(DepsSet &deps, const ArgInfo &info) const;
static std::string make_wrap_format(const ArgInfo &info,
const std::string &transfer, const std::string &outtype = {});
static std::string get_transfer_parameter(
const std::string &transfer, bool _type = false);
bool check_suppression(const std::string &ns, const std::string &kind,
const std::string &name) const;
};
#endif // GENBASE_HPP

1741
cmake/external/glib/cppgir/tools/genns.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
#ifndef GENNS_HPP
#define GENNS_HPP
struct GeneratorContext;
#include <memory>
#include <vector>
class NamespaceGenerator
{
public:
static std::shared_ptr<NamespaceGenerator> new_(
GeneratorContext &ctx, const std::string &filename);
virtual ~NamespaceGenerator() {}
virtual std::string get_ns() const = 0;
virtual std::vector<std::string> get_dependencies() const = 0;
virtual std::string process_tree(
const std::vector<std::string> &dep_headers) = 0;
};
#endif // GENNS_HPP

View File

@@ -0,0 +1,31 @@
#include "genutils.hpp"
#include <set>
#include <string>
static std::set<std::string> reserved{"alignas", "alignof", "and", "and_eq",
"asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "auto",
"bitand", "bitor", "bool", "break", "case", "catch", "char", "char16_t",
"char32_t", "class", "compl", "concept", "const", "constexpr", "const_cast",
"continue", "co_await", "co_return", "co_yield", "decltype", "default",
"delete", "do", "double", "dynamic_cast", "else", "enum", "explicit",
"export", "extern", "false", "float", "for", "friend", "goto", "if",
"import", "inline", "int", "long", "module", "mutable", "namespace", "new",
"noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq",
"private", "protected", "public", "register", "reflexpr",
"reinterpret_cast", "requires", "return", "short", "signed", "sizeof",
"static", "static_assert", "static_cast", "struct", "switch",
"synchronized", "template", "this", "thread_local", "throw", "true", "try",
"typedef", "typeid", "typename", "union", "unsigned", "using", "virtual",
"void", "volatile", "wchar_t", "while", "xor", "xor_eq"};
std::string
unreserve(const std::string &s, bool force)
{
auto res(s);
if (!s.empty() && isdigit(s[0]))
res.insert(res.begin(), '_');
else if (reserved.find(s) != reserved.end() || toupper(s) == s || force)
res += "_";
return res;
}

View File

@@ -0,0 +1,192 @@
#ifndef GENUTILS_HPP
#define GENUTILS_HPP
#include "common.hpp"
#include <fstream>
#include <regex>
#include <set>
#include <sstream>
#include <string>
class skip : public std::runtime_error
{
public:
static const int TODO = 0;
static const int OK = 0;
static const int INVALID = 1;
static const int IGNORE = 2;
int cause;
skip(const std::string &reason, int _cause = INVALID)
: std::runtime_error(reason), cause(_cause)
{}
};
class ScopeGuard
{
private:
std::function<void()> cleanup_;
public:
ScopeGuard(std::function<void()> &&cleanup) : cleanup_(std::move(cleanup)) {}
~ScopeGuard() noexcept(false)
{
#if __cplusplus >= 201703L
auto pending = std::uncaught_exceptions();
#else
auto pending = std::uncaught_exception();
#endif
try {
cleanup_();
} catch (...) {
if (!pending)
throw;
}
}
};
class NamespaceGuard
{
typedef std::vector<std::string> ns_list;
private:
ns_list ns_;
std::ostream &out_;
void push(const ns_list &ns)
{
for (auto &&v : ns) {
out_ << "namespace " << v << " {" << std::endl << std::endl;
ns_.push_back(v);
}
}
public:
NamespaceGuard(std::ostream &_out) : out_(_out) {}
void push(const std::string &ns, bool autodetect = true)
{
if (ns_.empty() && autodetect) {
ns_list l{GI_NS, GI_REPOSITORY_NS};
if (ns == GI_NS)
l.pop_back();
else if (ns != GI_REPOSITORY_NS)
l.push_back(ns);
push(l);
} else {
push(ns_list{ns});
}
}
void pop(int count = -1)
{
while (!ns_.empty() && (count > 0 || count < 0)) {
out_ << "} // namespace";
if (ns_.back().size())
out_ << ' ' << ns_.back();
out_ << std::endl << std::endl;
ns_.pop_back();
--count;
}
}
~NamespaceGuard() { pop(); }
};
class Matcher
{
std::regex pattern_;
static std::vector<std::string> readfile(std::istream &input)
{
std::vector<std::string> result;
for (std::string line; std::getline(input, line);) {
result.emplace_back(line);
}
return result;
}
public:
Matcher(const std::vector<std::string> &paths,
const std::string &patterns = {},
const std::function<void(const std::string &)> &custom = {})
{
std::set<std::string> lines;
for (auto &fpath : paths) {
if (fpath.size()) {
std::ifstream input(fpath);
auto flines = readfile(input);
logger(Log::DEBUG, "read {} lines from {}", flines.size(), fpath);
std::copy(
flines.begin(), flines.end(), std::inserter(lines, lines.begin()));
}
}
if (patterns.size()) {
std::istringstream iss(patterns);
auto flines = readfile(iss);
logger(Log::DEBUG, "read {} data lines", flines.size());
std::copy(
flines.begin(), flines.end(), std::inserter(lines, lines.begin()));
}
std::string lpatterns;
for (auto &&l : lines) {
if (!l.empty() && l[0] != '#') {
// combine all expressions into 1 expression
// so we only need to call once to check for matching
lpatterns += (lpatterns.size() ? "|" : "") + l;
} else if (custom && l.size() > 2 && l[0] == '#' && l[1] == '!') {
custom(l);
}
}
if (lpatterns.size())
pattern_ = std::regex(lpatterns, std::regex::optimize);
}
static std::string format(const std::string &first, const std::string &second,
const std::string name)
{
return first + ":" + second + ":" + name;
}
bool matches(const std::string &first, const std::string &second,
const std::vector<std::string> &options) const
{
for (auto &&c : options) {
// assemble string to match
auto find = format(first, second, c);
if (std::regex_match(find, pattern_))
return true;
}
return false;
}
};
inline bool
is_const(const std::string &ctype)
{
return (ctype.find("const ") != ctype.npos) || ctype == "gconstpointer";
}
inline bool
is_volatile(const std::string &ctype)
{
return ctype.find("volatile ") != ctype.npos;
}
inline int
get_pointer_depth(const std::string &ctype)
{
int pointer = ctype.find("gpointer") != ctype.npos ||
ctype.find("gconstpointer") != ctype.npos;
// note that things like const gchar* const* are also possible
return std::count(ctype.begin(), ctype.end(), GI_PTR) + !!pointer;
}
// check if @s is somehow special
// and mangle it a bit if so
std::string unreserve(const std::string &s, bool force = false);
#endif // GENUTILS_HPP

View File

@@ -0,0 +1,10 @@
#ifndef IGNORE_HPP
#define IGNORE_HPP
static const char *GI_DATA_IGNORE = R"|(
@CPPGIR_IGNORE@
@CPPGIR_UNIX_IGNORE@
@CPPGIR_WIN_IGNORE@
)|";
#endif // IGNORE_HPP

View File

@@ -0,0 +1,341 @@
#include "repository.hpp"
#include "genutils.hpp"
#include <regex>
#include <set>
#include <unordered_map>
static std::set<std::string> basic_types{"gchar", "guchar", "gshort", "gushort",
"gint", "guint", "glong", "gulong", "gssize", "gsize", "gintptr",
"guintptr", "gpointer", "gconstpointer", "gboolean", "gint8", "gint16",
"guint8", "guint16", "gint32", "guint32", "gint64", "guint64", "gfloat",
"gdouble", "GType", "utf8", "filename", "gi::cstring", "gunichar",
"dev_t", "gid_t", "pid_t", "socklen_t", "uid_t"};
class RepositoryPriv : public Repository
{
public:
// holds info collected from GIRs indexed by entry's (qualified) name
// (which should be unique within a namespace)
std::unordered_map<key_type, mapped_type> index;
// active ns
std::string ns;
// ODR check
mutable std::unordered_map<std::string, std::string> type_index;
// substitutes for missing c:type
subst_c_types c_types;
RepositoryPriv(subst_c_types m) : c_types(std::move(m))
{
// make basic types known
for (auto &&girname : basic_types) {
auto cpptype = girname;
auto ctype = girname;
int flags = 0;
flags |= TYPE_BASIC;
if (girname == "utf8" || girname == "filename" ||
girname == "gi::cstring") {
flags |= TYPE_CLASS;
ctype = "char*";
cpptype = "gi::cstring";
} else {
flags |= TYPE_VALUE;
if (girname == "gboolean")
cpptype = "bool";
}
auto argtype =
(girname.find("pointer") != girname.npos) ? "void*" : ctype;
index.emplace(
girname, mapped_type{nullptr, std::make_unique<TypeInfo>(girname,
cpptype, ctype, argtype, flags)});
}
// void case
index.emplace(GIR_VOID,
mapped_type{nullptr, std::make_unique<TypeInfo>(GIR_VOID, CPP_VOID,
EMPTY, CPP_VOID, TYPE_BASIC)});
// other special and pre-defined cases
std::vector<std::tuple<std::string, std::string, std::string, int>> pre{
{std::make_tuple("GLib.List", "GList", "GList", TYPE_LIST)},
{std::make_tuple("GLib.SList", "GSList", "GSList", TYPE_LIST)},
{std::make_tuple(
"GLib.HashTable", "GHashTable", "GHashTable", TYPE_MAP)},
{std::make_tuple("GObject.Value", "GObject::Value", "GValue",
TYPE_CLASS | TYPE_BOXED)},
{std::make_tuple(
"GLib.Error", "GLib::Error", "GError", TYPE_CLASS | TYPE_BOXED)},
// avoid namespace mishap
{std::make_tuple("GObject.Object", "GObject::Object", "GObject",
TYPE_CLASS | TYPE_OBJECT)},
// pretend is like object
{std::make_tuple("GObject.ParamSpec", "GObject::ParamSpec",
"GParamSpec", TYPE_CLASS)}};
for (auto &&e : pre) {
// qualify argtype to avoid name conflicts
auto ti = std::make_unique<TypeInfo>(std::get<0>(e), std::get<1>(e),
std::get<2>(e), GI_SCOPE + std::get<2>(e) + "*",
std::get<3>(e) | TYPE_PREDEFINED);
auto girname = ti->girname;
index.emplace(girname, mapped_type{nullptr, std::move(ti)});
}
auto ti =
std::make_unique<TypeInfo>("GLib.DestroyNotify", "GLib::DestroyNotify",
GDESTROYNOTIFY, GDESTROYNOTIFY, TYPE_CALLBACK | TYPE_PREDEFINED);
auto girname = ti->girname;
index.emplace(girname, mapped_type{nullptr, std::move(ti)});
}
};
// C++ on the outside, C trick on the inside
static const RepositoryPriv &
get_self(const Repository *t)
{
return *static_cast<const RepositoryPriv *>(t);
}
static RepositoryPriv &
get_self(Repository *t)
{
return *static_cast<RepositoryPriv *>(t);
}
// set namespace used for unqualified girname
void
Repository::set_ns(const std::string _ns)
{
auto &self = get_self(this);
self.ns = _ns;
}
// qualify girname, optionally wrt relative base
std::string
Repository::qualify(const std::string &girname, const std::string &base) const
{
auto &&self = get_self(this);
if (girname.find('.') == girname.npos) {
auto bns = self.ns;
if (base.size()) {
auto pos = base.find('.');
if (pos == base.npos)
return girname;
bns = base.substr(0, pos);
}
return bns + '.' + girname;
}
return girname;
}
void
Repository::add(const key_type &girname, const mapped_type::tree_type &n)
{
auto &&self = get_self(this);
auto qualified = qualify(girname);
auto it = self.index.find(qualified);
bool registered = false;
if (girname.empty()) {
// should not make it here
assert(false);
} else if (it != self.index.end()) {
auto &e = it->second;
// merge in tree data for predefined
if (e.info && (e.info->flags & TYPE_PREDEFINED)) {
e.tree = std::make_unique<mapped_type::tree_type>(n);
} else {
throw std::runtime_error(
fmt::format("duplicate name {} [{}]", girname, qualified));
}
} else {
// examine node/type
// NOTE not every entry/node represents a type (and therefore has !=
// flags) consider e.g. a constant or function
int flags = 0;
auto &el = n.first;
auto &node = n.second;
auto ctype = get_attribute(node, AT_CTYPE, "");
// base identification
if (el == EL_RECORD) {
flags |= TYPE_CLASS | TYPE_BOXED;
} else if (el == EL_OBJECT || el == EL_INTERFACE) {
flags |= TYPE_CLASS | TYPE_OBJECT;
} else if (el == EL_ENUM || el == EL_FLAGS) {
flags |= TYPE_ENUM | TYPE_VALUE;
} else if (el == EL_ALIAS) {
// adopt flags of typedef'ed one
// need not be a primitive one (e.g. GtkAllocation = GdkRectangle)
auto btype = node.get(EL_TYPE + '.' + PT_ATTR + '.' + AT_NAME, "");
auto ti = lookup(btype);
flags = ti && ti->info ? ti->info->flags : 0;
// also consider as sort-of predefined (at least if we know about
// it)
if (flags)
flags |= TYPE_TYPEDEF;
} else if (el == EL_CALLBACK) {
flags |= TYPE_CALLBACK;
}
if (flags) {
// check if registered
auto gettype = get_attribute(n.second, AT_GLIB_GET_TYPE, "");
if (gettype.size())
registered = true;
}
bool keep_node = false;
bool keep_info = flags != 0;
if (flags & TYPE_CLASS) {
keep_node = true;
// additional checks
auto class_struct = get_attribute(node, AT_GLIB_IS_TYPE_STRUCT_FOR, "");
if (class_struct.size()) {
// is not a valid type, but keep some info around for later
// lookup
flags = 0;
keep_info = true;
} else if (flags & TYPE_BOXED) {
keep_node = false;
}
// e.g. GParamSpec (several), variant
auto fundamental = get_attribute<int>(node, AT_GLIB_FUNDAMENTAL, 0);
// GVariant is marked this way
auto gtype = get_attribute(node, AT_GLIB_GET_TYPE, "");
if (fundamental || gtype == "intern") {
flags = -1;
}
}
{
// filter some more
auto disguised = get_attribute<int>(node, AT_DISGUISED, 0);
// also never mind if it is designed opaque/disguised
// lots of private stuff, but also possibly type structs
// (some at least, in case of opaque struct)
if (disguised)
flags = -1;
// cairo is notable foreign example
// structs are typically opaque, with custom _create, _copy,
// _destroy
// TODO for now filter all, perhaps not so later
// (and then only filter by ignore and allow custom declaration of
// above functions in the boxed system)
auto foreign = get_attribute<int>(node, AT_FOREIGN, 0);
if (foreign && !registered)
flags = -1;
}
// special fundamental case
// partly prepared, mostly generated
if (qualified == GIR_GVARIANT) {
keep_node = false;
keep_info = true;
flags = TYPE_CLASS;
}
// always check
auto movedto = get_attribute(node, AT_MOVED_TO, "");
if (!movedto.empty())
flags = -1;
if (flags == -1) {
logger(Log::DEBUG, "ignoring GIR {} {}", el, qualified);
} else {
logger(Log::LOG, "registering GIR {} {} {}", el, qualified, flags);
// convert GIR qualification to namespace qualification
static const std::regex re_qualify("\\.", std::regex::optimize);
// enum cpptype is unreserve'd in type definition
auto cpptype = std::regex_replace(
(flags & TYPE_ENUM) ? qualify(unreserve(girname)) : qualified,
re_qualify, "::");
assert(!(flags & TYPE_CLASS) || is_qualified(cpptype));
// in rare cases c:type is missing for a record/class
// (e.g. GtkSnapshot = alias of Gdk type)
// use an override substitute type
if ((flags & TYPE_CLASS) && ctype.empty()) {
auto sit = self.c_types.find(qualified);
if (sit != self.c_types.end()) {
ctype = sit->second;
logger(Log::INFO, "{} using substitute c:type {}", qualified, ctype);
}
}
// always top-level qualify ctype to avoid ns ambiguity
if (ctype.size())
ctype = GI_SCOPE + ctype;
auto argtype = ctype;
if ((flags & TYPE_CLASS) && argtype.size())
argtype += GI_PTR;
std::unique_ptr<mapped_type::tree_type> xmlinfo;
// node only needed for class type
// (only a small part of node is needed later on,
// but let's simply keep all of it)
if (keep_node)
xmlinfo = std::make_unique<mapped_type::tree_type>(n);
mapped_type entry = {
std::move(xmlinfo), keep_info ? std::make_unique<TypeInfo>(qualified,
cpptype, ctype, argtype, flags)
: nullptr};
it = std::get<0>(
self.index.emplace(std::move(qualified), std::move(entry)));
}
}
}
void
Repository::discard(const key_type &girname)
{
auto &&self = get_self(this);
auto qualified = qualify(girname);
logger(Log::LOG, "discarding girname {}", qualified);
if (!self.index.erase(qualified))
logger(Log::WARNING, "discarded unknown girname {}", qualified);
}
const Repository::mapped_type::tree_type &
Repository::tree(const std::string &girname) const
{
auto &&self = get_self(this);
auto &index = self.index;
// only used by class types
auto it = index.find(qualify(girname));
if (it == index.end() || !it->second.tree)
throw std::runtime_error("no node info for " + girname);
return *it->second.tree;
}
const Repository::mapped_type *
Repository::lookup(const std::string &girname) const
{
auto &&self = get_self(this);
auto &index = self.index;
// also consider non-qualified for basic types
auto it = index.find(girname);
if (it == index.end())
it = index.find(qualify(girname));
if (it != index.end())
return &it->second;
return nullptr;
}
std::string
Repository::check_odr(const std::string &cpptype, const std::string &ctype)
{
if (!ctype.empty()) {
auto &&self = get_self(this);
auto ret = self.type_index.insert({ctype, cpptype});
if (!ret.second && ret.first->second != cpptype) {
return ret.first->second;
}
}
return {};
}
std::shared_ptr<Repository>
Repository::new_(subst_c_types m)
{
return std::make_shared<RepositoryPriv>(std::move(m));
}

View File

@@ -0,0 +1,126 @@
#ifndef REPOSITORY_HPP
#define REPOSITORY_HPP
#include "common.hpp"
#include <map>
#include <memory>
#include <string>
#include <boost/property_tree/ptree.hpp>
namespace pt = boost::property_tree;
// ptree helpers
template<typename T = std::string, typename... Args>
static T
get_attribute(const pt::ptree &node, const std::string &attr, Args... args)
{
static auto prefix = PT_ATTR + '.';
return node.get<T>(prefix + attr, args...);
}
inline std::string
get_name(const pt::ptree &node)
{
return get_attribute(node, AT_NAME);
}
inline std::string
get_name(const pt::ptree &node, std::nothrow_t)
{
return get_attribute(node, AT_NAME, "");
}
enum TYPE_TRAITS {
// basic/fundamental glib defined type
TYPE_BASIC = 1 << 0,
// type passed by value (integral, enum, etc)
TYPE_VALUE = 1 << 1,
// enum, bitfield type
TYPE_ENUM = 1 << 2,
// class type (string, object, record)
TYPE_CLASS = 1 << 3,
// gobject
TYPE_OBJECT = 1 << 4,
// boxed
TYPE_BOXED = 1 << 5,
// callback
TYPE_CALLBACK = 1 << 6,
// containers
TYPE_ARRAY = 1 << 7,
TYPE_LIST = 1 << 8,
TYPE_MAP = 1 << 9,
TYPE_CONTAINER = TYPE_ARRAY | TYPE_LIST | TYPE_MAP,
TYPE_VARARGS = 1 << 10,
// predefined
TYPE_PREDEFINED = 1 << 11,
// typedef (no forward class declare)
TYPE_TYPEDEF = 1 << 12
};
// some info on (argument) type
struct TypeInfo
{
TypeInfo() = default;
TypeInfo(const std::string &_gir, const std::string &_cpp,
const std::string &_c, const std::string &_argtype, int _flags)
: girname(_gir), cpptype(_cpp), dtype(_c), argtype(_argtype),
flags(_flags), pdepth(flags & (TYPE_CLASS | TYPE_CONTAINER) ? 1 : 0)
{}
// always qualified (if not predefined glib type)
std::string girname;
// always qualified (see below) as it might be used in class context
// (so to avoid lookup conflicts with parent classes in other ns,
// e.g. injected-class-name)
std::string cpptype;
// c:type taken from class/record definition
std::string dtype;
// c type as used in argument (no cv qualification)
std::string argtype;
// combination of flags above
int flags = 0;
// number of pointer indirections
int pdepth = 0;
};
class Repository
{
Repository() = default;
friend class RepositoryPriv;
Repository(const Repository &other) = delete;
Repository &operator=(const Repository &other) = delete;
public:
typedef std::map<std::string, std::string> subst_c_types;
typedef std::string key_type;
struct mapped_type
{
typedef pt::ptree::value_type tree_type;
std::unique_ptr<tree_type> tree;
std::unique_ptr<TypeInfo> info;
};
static std::shared_ptr<Repository> new_(subst_c_types m);
// set namespace used for unqualified girname
void set_ns(const std::string _ns);
// qualify girname, optionally wrt relative base
std::string qualify(
const std::string &girname, const std::string &base = "") const;
void add(const key_type &girname, const mapped_type::tree_type &n);
void discard(const key_type &girname);
const mapped_type::tree_type &tree(const std::string &girname) const;
const mapped_type *lookup(const std::string &girname) const;
// check for duplicate definition for ctype
// if ctype already claimed, returns non-empty claiming cpptype
std::string check_odr(const std::string &cpptype, const std::string &ctype);
};
#endif // REPOSITORY_HPP