4

So let's suppose I have the following class definition:

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

What I would like, is if I write the following code:

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

I would like the output to be:

my_name:5

Obviously, the easiest way to resolve this would be to write code that looks like this:

class my_named_int {
public:
    int value;
    const string name;
    my_named_int(int _value, const string & _name);
};

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

But of course, this adds repetitiveness to my code. Is there a way to do this in C++ without the "double-writing" of the variable name, and if so, how would I go about it?

Xirema
  • 19,889
  • 4
  • 32
  • 68
  • 4
    Why do you want to do this? – Untitled123 Dec 30 '15 at 16:19
  • 3
    Use a macro? Compiled code in C++ doesn't really know the names of things [at least not in a way that is understandable by the code - debug symbols don't really count] – Mats Petersson Dec 30 '15 at 16:19
  • 1
    Not possible (but perhaps some C++ preprocessor tricks might help) – Basile Starynkevitch Dec 30 '15 at 16:19
  • Not sure how to do this is c++ but I wrote a similar question here: http://stackoverflow.com/questions/32148060/get-a-parameters-old-name for c#. If it's possible in c# I bet it's possible in c++ – Seth Kitchen Dec 30 '15 at 16:22
  • @Untitled123 I'm writing a "Code Builder" where a person can write, in C++, the code they want translated into the scripting language of my choosing. For example, if I write `named_int my_var = 5;` I want a way that I could call, say, `my_var.to_code()` and get a plaintext string representation that looks like `int my_var = 5;`. – Xirema Dec 30 '15 at 16:23
  • how about not using this kind of feature? doing so is opposed to the C++ philosophy. c++ is not yet to support reflection, so try not mimic it – David Haim Dec 30 '15 at 16:32
  • @DavidHaim The thing is, what I'm trying to do involves *compile-time* analysis of the code, not *run-time* analysis. So it feels like it's more likely that the concept is possible. – Xirema Dec 30 '15 at 16:35
  • 1
    What name would you expect for a dynamically allocated `my_named_int`? – jxh Dec 30 '15 at 16:44

4 Answers4

9

In C++ variables do not know their own name.

The best you can do is use the preprocessor:

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

That said this is not idiomatic C++ so you might consider looking at the real problem you're trying to solve and ask that as another question. Specifically I mean that reflection (Which seems to be what this question is about) is not a C++ feature and as such problems are typically solved in alternate ways.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • This is probably closer to what I'm actually trying to do. A lot of what I'm doing involves inspecting the code at compile-time, not necessarily at run-time. – Xirema Dec 30 '15 at 16:25
  • What do you mean by "Not idiomatic C++"? Are there significant design conventions I'd be violating by doing this? – Xirema Dec 30 '15 at 16:27
  • @Xirema Stroustrup himself said in "The C++ Programming Language" he wishes macros had been removed already. This is roughly what the "not idiomatic" stands for here, I guess. :-) – cadaniluk Dec 30 '15 at 16:43
  • @cad: On the other hand, using macros is the *only* way of doing it. Usually you say things are not idiomatic when there is some idiom which is the preferred way of doing it -- for instance GOTO is not idiomatic in C++ because you can always use other control structures which are better behaved wrt scopes and object lifetimes and such. But in this case there is *no* way to do it that doesn't use macros. – Chris Beck Dec 30 '15 at 20:14
2

It's true that C++ does not support reflection as a language feature, but there are many ways that people implement reflection-like properties.

For one thing you can look at boost::fusion -- in fusion, you can do things like, iterate over all the variables of a struct in order. Fusion has some templates and macros that allow you to take an arbitrary struct declared in one part of your program, and then do metaprogramming things with it as though it were a std::tuple of its member variable types. There are some useful practical examples of doing this in the boost::spirit documentation and examples.

Fusion however doesn't track the names of variables, just their types and their order.

If you want to be able to iterate over the list of type-and-name pairs over the variables in your struct at compile time, it can be done but as far as I know there is no library for it and you'll have to roll your own thing. (If there is a lib that does it I would like to know!)

In another project, I basically did this (in C++11) as follows:

  • I make a type called serial_struct_field. This is a compile-time data structure, it is just a type with no members, which contains a typedef called type and a static constexpr function of no arguments which returns a string literal called name. You also should have a static constexpr member pointer corresponding to the member variable. The purpose of this is to represent all the compile-time metadata you want to have about a member variable in some struct.
    (Actually serial_struct_field is a concept rather than an actual type, there's no need for inheritance or anything here, as you see.)
  • I make a type trait called "serial_struct". A serial struct (constructed using certain macros in my system) is one that can provide, via this trait, a typelist of serial_struct_field types. There are some template functions that check the trait and then can apply a visitor to each struct member if desired, so you can do reading / writing in many different ways as appropriate.
    • For instance, you could make something like "lua_state_visitor" that can read or write primitive things like int, double, std::string, std::vector<std::string> to a lua_State, and then you would use the visitor pattern like lua_state_visitor my_visitor{get_lua_state()}; my_serial_struct.write(my_visitor); or something like this.
    • Then you could have a different visitor for reading and writing json, etc.
    • If you define it properly then you can have serial_struct inside your serial_struct's and it will work fine. :)
  • To make a serial struct, I provide three macros BEGIN_SERIAL_STRUCT, SERIAL_FIELD and END_SERIAL_STRUCT. In code it looks like this:

    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;
    };
    

    The resulting structure is essentially the same as

    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;
    };
    

    as far as run-time characteristics are concerned, but the macros and such set up some types and functions so that you can do the "generic serialization".

  • To implement the macros themselves as illustrated, you need to be able to accumulate a list of compile time information. That's a bit tricky but there are some tricks to do it without too much complication in C++11.
    If you are willing to change the macro to look like this:

      SERIAL_FIELDS(
        (std::string, id),
        (int, my_int),
        (double, my_double),
        (std::vector<std::string>, my_vector));
    

    or something, then it is a lot simpler to implement, and you can basically do the code generation using only macros and not having to use helper templates.
    (But, it's also a design consideration, templates are usually a bit more robust than macros and they don't always play well together. For instance if your types in your serial fields have multiple template parameters (and thus commas) that can confuse your macros and give you mind-numbing problems, so for a complicated / potentially going to become complicated application you might be better off with using the templates under the hood as much as possible...)

For the first version I used a technique sort of like what is described here, but changed so that it can all take place inside the definition of a struct. For the second version, if you just want to iterate over a list within a macro, you can use known techniques like described here. Either version will be only a few hundred lines, and self-contained.

Basically you can have reflection if you really want in C++11, if you are willing to put up with a few hundred lines of boiler-plate hidden behind some macros for each structure that you want to use it in... YMMV

Community
  • 1
  • 1
Chris Beck
  • 15,614
  • 4
  • 51
  • 87
0

Maybe this is what you can use: https://github.com/I3ck/cppDbg
Otherwise use a constructor which also takes a string as name or do something similar as in cppDbg yourself

I3ck
  • 433
  • 3
  • 10
0

One idea that comes up is to bind all variables to what they represent using a HashMap (or unorderedmap in C++). For example, let's say you have my_named_int my_name(5);

You would have to write this to translate this to

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

and retrieve it with

myMap['my_name]

With iterators, you can loop through all variables you have stored. This might come in handy for code builder purposes (As you can easily show all values for all stored variables)

Untitled123
  • 1,317
  • 7
  • 20
  • 1
    A map will get used at some point in the actual final code, but this doesn't solve the problem of having to "double-write" the variable name to make the code work. – Xirema Dec 30 '15 at 16:31
  • At the same time, you don't have to use a variable name anymore if you only access from the map, so there is no double write :) – Untitled123 Dec 30 '15 at 16:34