如何编写一个“知道”自己的变量名的类

时间:2015-12-30 16:17:50

标签: c++

所以让我假设我有以下类定义:

x = (('a', 'b'), ('foo', 'bar'))
pd.DataFrame(data=list(sum(x, ())), index=['One', 'Two', 'Three', 'Four']).transpose()

  One Two Three Four
0   a   b   foo  bar

我想要的是,如果我写下面的代码:

class my_named_int {
public:
    int value;
    const string name;
    my_named_int(int _value, /*...Does Something Go Here...?*/);
};

我希望输出为:

int main() {
    my_named_int my_name(5);
    std::cout << my_name.name << ":" << my_name.value << std::endl;
}

显然,解决此问题的最简单方法是编写如下代码:

my_name:5

但是,当然,这会增加我的代码的重复性。有没有办法在没有变量名称的“双重写入”的情况下在C ++中执行此操作,如果是这样,我将如何处理它?<​​/ p>

4 个答案:

答案 0 :(得分:7)

在C ++中,变量不知道自己的名字。

您可以做的最好的事情是使用预处理器:

#define DECLARE_NAMED_INT(value, name) my_named_int name((value), #name);

那说这不是惯用的C ++,所以你可以考虑查看你想要解决的真正问题,并将那个作为另一个问题。具体来说,我的意思是反射(这似乎是这个问题的内容)不是C ++特性,因此这些问题通常以其他方式解决。

答案 1 :(得分:2)

C ++不支持将反射作为一种语言特性,但人们有很多方法可以实现类似反射的属性。

首先,您可以查看boost::fusion - 在融合中,您可以执行以下操作:按顺序迭代结构的所有变量。 Fusion有一些模板和宏,允许你在程序的一个部分中获取声明的任意结构,然后使用它进行元编程,就好像它是成员变量类型的std::tuple一样。在boost::spirit文档和示例中有一些有用的实际示例。

然而,

Fusion并不跟踪变量的名称,只跟踪它们的类型和顺序。

如果你希望能够在编译时迭代结构中变量的类型和名称对列表,那么就可以完成它,但据我所知,它没有库,而你#&# 39;我必须滚动你自己的东西。 (如果有一个lib,我想知道!)

在另一个项目中,我基本上做了这个(在C ++ 11中),如下所示:

  • 我创建了一个名为serial_struct_field的类型。这是一个编译时数据结构,它只是一个没有成员的类型,它包含一个名为typedef的{​​{1}}和一个没有参数的type函数,它返回一个名为{的字符串文字{1}}。您还应该有一个与成员变量对应的static constexpr成员指针。这样做的目的是表示您希望在某些name中有关成员变量的所有编译时元数据。
    (实际上static constexpr是一个概念,而不是一个实际的类型,如你所见,这里不需要继承或任何东西。)
  • 我创建了一个名为&#34; serial_struct&#34;的类型特征。串行结构(使用我系统中的某些宏构建)是一种可以通过此特征提供struct类型的类型列表的结构。有一些模板函数可以检查特征,然后可以根据需要将访问者应用于每个结构成员,因此您可以根据需要以多种不同的方式进行读/写。
    • 例如,您可以制作像&#34; lua_state_visitor&#34;可以读取或写入serial_struct_field等原始内容到serial_struct_field,然后您可以使用int, double, std::string, std::vector<std::string>之类的访问者模式或类似的内容。
    • 然后你可以有一个不同的访客来阅读和写作json等。
    • 如果您正确定义了它,那么您可以在lua_State内使用lua_state_visitor my_visitor{get_lua_state()}; my_serial_struct.write(my_visitor);并且它可以正常工作。 :)
  • 要制作一个串行结构,我提供了三个宏serial_structserial_structBEGIN_SERIAL_STRUCT。在代码中它看起来像这样:

    SERIAL_FIELD

    结果结构与

    基本相同
    END_SERIAL_STRUCT

    就运行时特性而言,但宏等设置了一些类型和功能,以便您可以进行&#34;泛型序列化&#34;。

  • 要如图所示自己实现宏,您需要能够累积编译时信息列表。这有点棘手,但有一些技巧可以做到这一点而不会在C ++ 11中出现太多复杂情况。
    如果您愿意将宏更改为如下所示:

    struct foo {
      BEGIN_SERIAL_STRUCT(foo);
      SERIAL_FIELD(std::string, id);
      SERIAL_FIELD(int, my_int);
      SERIAL_FIELD(double, my_double);
      SERIAL_FIELD(std::vector<std::string>, my_vector);
      END_SERIAL_STRUCT;
    
      std::unique_ptr<bar> something_that_cant_really_be_serialized;
      std::shared_ptr<foo_context> more_such_things;
    };
    

    或其他什么,然后实现起来要简单得多,基本上你只需要使用宏来完成代码生成,而不必使用帮助模板。
    (但是,它也是一个设计考虑因素,模板通常比宏更健壮,并且它们不能总是很好地协同工作。例如,如果您的串行字段中的类型具有多个模板参数(因此逗号)可能会混淆你的宏并给你带来麻烦的问题,所以对于一个复杂的/可能会变得复杂的应用程序你可能会更好地使用引擎盖下的模板...)

对于第一个版本,我使用了类似于here描述的技术,但进行了更改,以便它可以全部发生在struct foo { std::string, id; int my_int; double my_double; std::vector<std::string> my_vector; std::unique_ptr<bar> something_that_cant_really_be_serialized; std::shared_ptr<foo_context> more_such_things; }; 的定义中。对于第二个版本,如果您只想迭代宏中的列表,则可以使用已描述的here等已知技术。两个版本都只有几百行,并且是独立的。

基本上你可以反思如果你真的想要在C ++ 11中,如果你愿意忍受在你想要使用它的每个结构的某些宏后面隐藏几百行的样板。 .YMMV

答案 2 :(得分:0)

也许这就是你可以使用的:https://github.com/I3ck/cppDbg
否则使用一个构造函数,该构造函数也将字符串作为名称或执行与cppDbg类似的操作

答案 3 :(得分:0)

出现的一个想法是使用HashMap(或C ++中的无序映射)将所有变量绑定到它们所代表的内容。例如,假设您有my_named_int my_name(5);

您必须将此内容翻译为

myMap.insert(std::make_pair<std::string,my_named_int>("my_name",my_name(5));

并使用

检索它
myMap['my_name]

使用迭代器,您可以遍历已存储的所有变量。对于代码构建器而言,这可能会派上用场(因为您可以轻松显示所有存储变量的所有值)