【发布时间】:2018-04-18 14:03:35
【问题描述】:
我正在尝试解决一个问题,即我有许多分散的变量声明(在单个编译单元中),并且我想在编译时构建它们地址的向量(如果不可能作为节省空间的向量,其他数据结构也可以使用,例如链表)。
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 类或使用其他一些方法来允许我在任何时候声明这些全局句柄变量,而且还可以在编译时为这些变量构建一个节省空间的向量,那就太好了我可以稍后访问:
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 函数,它以某种方式将所有这些变量链接到一个列表中,或者最好只产生一个指向内存中所有 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 系统上工作一样——它们通过指针收集到自己的部分,所有这些部分都是在链接时连接,最后带有一个特殊的对象文件,用于传递哨兵结束值。
【问题讨论】:
-
能否在初始化脚本语言后立即使用 get_var 缓存变量?
-
也许
std::map可以解决问题 -
@AndrewKashpur 如果您的意思是 get_var 返回的句柄是否无限期保持有效,则此示例的答案是肯定的。问题是在程序初始化时不能调用get_var,否则构造函数可以简单地调用它。
-
@user2176127 std::map 不能在编译时使用,可以吗?
-
您的问题可以通过“反射”解决,但愚蠢的 C++ 目前不提供此功能。