【问题标题】:Accessing a variable in C++ by "stitching" its name together通过将变量名称“拼接”在一起来访问 C++ 中的变量
【发布时间】:2014-07-03 06:26:54
【问题描述】:

假设我有一个变量:

 int fish5 = 7;

我可以通过连接术语“fish”和“5”来访问fish5吗?

理想的解决方案如下所示:

 printf("I am displaying the number seven: %i", fish + 5);

【问题讨论】:

  • 这应该在运行时还是编译时工作?编译时可以使用预处理器。在运行时这是不可能的。
  • 一个更合乎逻辑的场景是将变量(fish1,fish2,...)存储在一个向量/数组中,并使用索引来检索它。 fishes[4] 将是第五条鱼。
  • 将它们存储在一个数组中并使用索引来检索它,请参阅康斯坦丁的回答(以及我在这里的评论)
  • @user3800357:然后向我们展示上下文。世界各地的人们以某种​​方式使用 cuda 没有这个问题 ;)
  • @user3800357 您可能需要针对 CUDA 部分添加一些信息,因为要求不同。

标签: c++ variables int


【解决方案1】:

不,不是你想要的。但是在您的示例中,您可以使用数组(仅当您想将变量名与数字连接时才有效):

int fish[6] = {0};
fish[5] = 7;

printf("I am displaying the number seven: %i", fish[5]);

另请参阅 here 以获取对 C++ 中数组的引用。

另一种解决方案是改用 std::map,就像 Thrustmaster 在 cmets 中指出的那样。

然后你可以这样写:

#include <map>
#include <string>

int main(int argc, char* argv[]){
  std::map<std::string, int> map;
  map.insert(std::make_pair("fish5", 7));
  printf("I am displaying the number seven: %d", map[std::string("fish") + std::to_string(5)]);
  return 0;
}

有关std::map 的更多信息,请参阅here

【讨论】:

  • @Thrustmaster 每次都存储名称“fish”是不合逻辑的,为什么不只存储值并使用向量/列表呢?
  • @Constantin 我有同样的想法,这是用户所追求的场景。
  • @Thrustmaster 谢谢,我也添加了std::map
  • 如果支持,最好在这里使用std::unordered_map
【解决方案2】:

不可能将解决方案从编译时转换到运行时,因为 c++ 是一种编译语言,而不是解释语言。变量名在编译后“失去了意义”。它们只是一组带有地址的符号。这意味着,在编译后要求 fish5 之类的东西是没有意义的。

要实现您想要的,您需要以某种方式以编程方式将名称绑定到对象,例如使用映射,将名称存储为键,将对象引用存储为值。这就是 python 的工作方式,以及为什么在 python 中您实际上可以通过代码中的名称访问对象。

如果有人想知道为什么例如 gdb 或故障转储是有意义的,原因几乎相同。符号名称必须在编译时保存(嵌入在可执行文件中或外部文件中),然后外部工具可以找出某个地址下的变量名称是什么。但是如果没有这些信息,编译的可执行文件也可以正常工作。

或者,您需要以更方便的方式记住引用本身,以使其可计算。例如。将其存储在数组中并以fish[5] 访问;尽管在本例中可以在编译时对其进行评估,但您可以在运行时使用相同的方法,使用变量代替 5

编译时和运行时之间的区别非常重要,因为您实际上可以在编译时使用预处理器做您想做的事,但这仅仅是因为它是编译时。

【讨论】:

  • 可能(在运行时通过变量名访问变量)使用操作系统特定的技巧。看我的回答。
  • 我认为这在“gdb 如何工作”部分中有介绍,但如果不清楚,我可以明确说明。我也认为传播这种机制是危险的,或者至少是一种相当可疑的做法,但仍然有可能。
【解决方案3】:

您可以使用操作系统特定的东西,例如(在 Posix 上,例如 Linux)dlsym(3) 在运行时通过(未剥离的)ELF 可执行文件中的符号表访问变量。

因此,最好将要访问的变量声明为extern "C",例如

 extern "C" int fish17;

(否则,请考虑编译器特定的name mangling

同时声明一个程序句柄:

 void *progdlh;

早在main初始化它

 progdlh = dlopen(NULL, RTLD_NOW|RTLD_GLOBAL);
 if (!progdlh) { fprintf(stderr, "dlopen failure %s\n", dlerror()); 
                 exit(EXIT_FAILURE); }

然后,要通过计算名称检索变量,您可以尝试:

 char nambuf[32];
 snprintf (nambuf, sizeof(nambuf), "%s%d", "fish", 17);
 int *pvar = dlsym(progdlh, nambuf);
 if (!pvar) { fprintf(stderr, "dlsym %s failure %s\n", nambuf, dlerror()); 
                 exit(EXIT_FAILURE); }
 printf ("variable %s is %d\n", nambuf, *pvar);

您可能应该将您的程序与-rdynamic 标志(以及-ldl 库)链接起来

我的答案应该适用于 Linux。

【讨论】:

  • +1 一般方法是合理的,但是在流上使用printf-family 函数...? ;-P
【解决方案4】:

您只能在编译时使用预处理器执行此操作。完整示例:

#include <cstdio>

#define JOIN(a,b) a##b

int main(void) {
    int fish5 = 5;
    std::printf("I am displaying the number five: %i", JOIN(fish, 5));
    return 0;
}

但是,这是严格的编译时间。如果你尝试JOIN(fish, fish),你会得到error: ‘fishfish’ undeclared,如果你尝试JOIN("fish", fish),你会得到error: pasting ""fish"" and "fish" does not give a valid preprocessing token

在 C++ 中,变量名在运行时不存在,因此无法在运行时进行操作,除非有一些深入的调试信息黑客可以通过名称字符串查找变量(就像调试一样)。如果使用字符串对您来说是有效的方法,那么最好明确地有一个从字符串到变量地址的映射。其他答案已经通过使用std::map 显示了如何做到这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-25
    • 2012-11-22
    • 2023-03-16
    • 1970-01-01
    • 2016-04-14
    相关资源
    最近更新 更多