在编译时增量构建变量指针的向量

时间:2018-04-18 14:03:36

标签: c++ c++14

我正在尝试解决一个问题,我有一些分散的变量声明(在一个编译单元中),我想在编译时构建一个地址向量(如果不可能作为一个空间高效的向量,其他数据结构也可以使用,例如链表。

static type var1;
static type var2;
static type var3;

// ...
for (type *i : varlist)
   do something with each varX

作为一个扩展示例和基本原理,假设我有一个脚本语言,并且我有办法通过获取句柄来修改C ++中的脚本变量,比如scriptvar *。通过调用名称为get_var的函数可以找到这些句柄:

scriptvar *var1 = get_var ("namespace::var1");
scriptvar *var2 = get_var ("namespace::var2");

这很慢,所以我希望将scriptvar *值缓存在全局变量中。不幸的是,我必须在初始化脚本语言之后调用get_var,这可能发生在程序的后期,因此我不能在初始化表达式中使用get_var,但必须延迟调用它。

如果我可以编写一个scriptvarwrapper类或者使用其他一些方法可以让我在任何时候声明这些全局句柄变量,那么这将是很好的,但是在编译时也可以构建一个节省空间的这些vairables向量我以后可以访问的时间:

struct scriptvarwrapper
{
  scriptvar *handle;
  const char *name;
  / ...
};

static scriptvarwrapper var1 ("namespace::var1");
static scriptvarwrapper var2 ("namespace::var2");
static scriptvarwrapper var3 ("namespace::var3");

void init_vars ()
{
  for (scriptvarwrapper *i : somecontainer)
     i->handle = get_var (i->name);
}

如果这个容器最终只是内存中包含指向这些变量的指针的数组/向量数据结构,那将是理想的。显然,目标是在编译时构建它,因此在构造函数中将某些内容放入std::vector的解决方案无法解决问题。

更新

澄清问题 - 我想要的是一个解决方案,它在编译时自动编译所有这些声明变量的数组或列表,而不必单独列出它们,也不需要构造函数在运行时动态构建列表,大概是通过一些很酷又漂亮的元编程方法,比如一个constexpr函数,它以某种方式将所有这些vairables链接在一个列表中,或者最好是导致只是一个指向内存中所有scriptvarwrapper对象的指针数组的东西,或者以特殊值终止或者已知的大小。

具体来说,必须手动将scriptvarwrapper对象放入静态数组中,也不会将它们放入构造函数中的std :: vector中。

这个的基本原理是可维护性 - 如果我在程序的任何地方添加变量,我不想再单独列出它,因为这很容易忘记 - 而且效率 - 我不想建立动态数据运行时的结构,实际上是编译时已知的常量。

对某种数组的偏好是由于效率 - 在编译时为每个这样的对象产生100字节结构的解决方案当然不是很有用,同样会为将来的扩展分配一个非常大的数组。

更新2:

为了进一步说明,存储的确切语法和布局以及类型并不重要。重要的是,我可以拥有一些独立的,分散在文件中的变量声明,并且它们的地址是自动的,没有手动枚举放入某种只读容器,可以某种方式迭代在运行时查找所有这些声明。

目标是有效地找到所有这些变量而不会忘记单独列出一个变量,也不必在运行时动态构建数据结构,因为信息在编译时都是已知的,并且C ++非常酷语言与编译时元编程。唉,我不知道怎么样,甚至不可能。

更新3:

对于所有这些更新,我很抱歉,我正在学习表达此问题的难度。以下是事情的样子:

static scriptvarwrapper var1 ("name");
ADD_LIST (var1); // magic macro

static scriptvarwrapper var2 ("name");
ADD_LIST (var2);

这里的关键是,虽然我必须列出每个变量,甚至可能使用丑陋的宏,但很难忽视或忘记列出变量,因为ADD_LIST直接位于声明的位置 - 请记住,声明可能分散在一个长文件中,甚至包含在一些包含文件中,所以我正在寻找一种解决方案,这使得很难忘记在我的列表中包含一个声明。

因此,理想情况下,构造函数或仅仅声明scriptvarwrapper的行为会确保其列出,因此无法忽略它。将所有内容放在构造函数中的std::vector的解决方案都可以工作,除非由于运行时开销而感到难看。

作为一个老C手,我考虑使用GCC扩展将它们放入自己的ELF部分,就像构造函数本身在ELF系统上工作一样 - 它们通过指针收集到它们自己的部分中,所有这些部分都是在链接时串联,在末尾有一个特殊的目标文件,用于传递sentinel结束值。

1 个答案:

答案 0 :(得分:1)

如果我理解你的问题,我并不完全确定,但为什么不简单地使用经典的旧数组:

static int var1;
static int var2;
static int var3;

static int* vars[] = { &var1, &var2, &var3, nullptr };

for(size_t i = 0; vars[i]; ++i)
    std::cout << *vars[i] << std::endl;

这适用于所有数据类型,并且保证在编译时发生。

以下版本也是在编译时构建的(至少在Visual C ++ 2017中):

static const auto vararr = std::array<int*, 3>{ &var1, &var2, &var3 };

你可以为scriptvarwrapper做同样的事情:

struct scriptvarwrapper
{
    scriptvar *handle;
    const char *name;
};

static scriptvarwrapper vars[] = {
    {nullptr, "var1"},
    {nullptr, "var2"},
    {nullptr, nullptr}
};

void init_vars()
{
    for (size_t i = 0; vars[i].name; ++i)
        vars[i].handle = get_var(vars[i].name);
}

在C ++中,脚本变量'var1'可以由vars [0] .handle访问,'var2'在vars [1] .handle中。

也许您更喜欢以下解决方案:

struct scriptvarwrapper
{
    scriptvar **handle;
    const char *name;
};

static scriptvar *var1 = nullptr;
static scriptvar *var2 = nullptr;

static scriptvarwrapper vars[] = {
    { &var1, "var1"},
    { &var2, "var2"},
    {nullptr, nullptr}
};

void init_vars()
{
    for (size_t i = 0; vars[i].name; ++i)
        *vars[i].handle = get_var(vars[i].name);
}

var1和var2作为scriptvars,初始化为nullptr并使用'scriptvarwrapper'添加到编译时数组'vars'。在'init_vars'中,脚本变量被初始化,然后可以通过访问'var1'和'var2'来使用(甚至不知道用于初始化它们的编译时数组)

非编译时但易于使用的解决方案:

class ScriptVar
{
public:
    ScriptVar(const char *name_)
        : name(name_)
    {
        vars.insert(this);
    }

    scriptvar* operator->()
    {
        return handle;
    }

    static void initVars()
    {
        for (auto var : vars)
            var->handle = get_var(var->name);
    }

private:
    static std::set<ScriptVar*> vars;
    const char *name;
    scriptvar *handle;
};

const ScriptVar var1("namespace::var1");
const ScriptVar var2("namespace::var2");

每个定义的ScriptVar都在ScriptVar :: vars中注册,在调用ScriptVar :: initVars()之后,可以使用 - &gt;访问所有已定义的ScriptVars。操作