1

Is there a way in c++ to reference a member variable from the user input. Say I have a struct that looks like

struct numbers {double X, Y, Z};

And I ask the user to enter a number, and what variable it is:

int main()
{
numbers Input;
string memberVariable;
double variableValue;
cout << "Enter number" << endl;
cin >> variableValue; //User enters 100.0 for example
cout << "Enter variable" <<endl;
cin >> memberVariable; //User enters a char or string that equals X
}

Is there a way to assin Input.X to 100.0 based off of what the user inputted. I know this is possible with an if or switch statement, however I have 5 different structs and each with 10 different member variables so it would be nice to base it off of user input and not have to write all the different if statements

user2840470
  • 919
  • 1
  • 11
  • 23
  • 1
    possible duplicate of [Access variable value using string representing variable's name in C++](http://stackoverflow.com/questions/2911442/access-variable-value-using-string-representing-variables-name-in-c) – Sneftel Jul 22 '14 at 20:10
  • 1
    Actually, on second thought this isn't a close duplicate. That one asks about unqualified variables, this one asks about members. The difference is quite relevant to an answer. – Ben Voigt Jul 22 '14 at 20:15
  • @BenVoigt I don't see how really... both these questions are asking how to have user input map to a variable name. What have I/we missed? – thecoshman Jul 22 '14 at 20:21
  • You cannot do that generically (the linked question above has chosen an answer which explains why), but you can use a `map` as a dictionary to index all variables of type `variable_type` (in your case: `double`) you wish to keep track of that way (another answer of that same question supports that solution). – didierc Jul 22 '14 at 20:21

2 Answers2

1

What you ask for is possible with a little bit of advance preparation. Your ultimate goal is to access a variable by a string entered at runtime.

Let's first look for any method at identifying a member selectively at runtime. That's what pointer-to-members are for. Quick example:

void set_member( numbers& target, double numbers::*which_member, double value )
{
    target.*which_member = value;
}

set_member(Input, &numbers::x, 6.0);
set_member(Input, &numbers::y, -3.14);

In your example, all the member variables are the same type, so this gets us really close. All that we need in addition is a mapping from the name to the pointer-to-member.

std::map<std::string, double numbers::*> numbers_members =
    { { "x", &numbers::x },
      { "y", &numbers::y },
      { "z", &numbers::z } };

void set_named_member( numbers& target, set::string which_member, double value )
{
    target.*(numbers_members[which_member]) = value;
}

set_member(Input, member_variable, variableValue);

Making the member map can be made a bit easier using a macro using the stringizing operator:

#define member(Type,MemName) { #MemName, &Type::MemName },

This can even be automated using some static analysis tool that produces a list of members by type (for example, the XML output format from the doxygen tool contains all the needed information; that plus a perl script launched by your makefile can give fairly effective reflection capability).


If you want to handle members of varying types, it gets a little more complicated. You will need to define an interface that works on all types, maybe it accepts the value as a string, and performs type-specific parsing (boost::lexical_cast is a reasonable choice for that), before assigning the member. Such a member-of-arbitrary-type assigner could be implemented using templates.

Then you map object would go from the member name to this member-assigner functor. When calling the functor, you would pass in the object instance and the string representation of the value.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I should flag this as "not an answer" `:-p` I will downvote if there are no hand-drawn diagrams. – rubenvb Jul 22 '14 at 20:18
  • I'm thinking of writing the "any-type-member" version up as a blog post, depending on whether anyone indicates interest. It would be a bit too much and too complicated code to write on the fly. – Ben Voigt Jul 22 '14 at 20:30
  • soo... you basically answered the same question as what this was being marked as a duplicate off – thecoshman Jul 22 '14 at 20:33
  • @thecoshman: Except for the part about member variables, which changes the solution quite a lot (although the concept is roughly the same). Certainly it is a "related" question, but not a duplicate. – Ben Voigt Jul 22 '14 at 20:35
  • @BenVoigt no, they both basically boil down to accessing a variable using the name a user types in. – thecoshman Jul 22 '14 at 20:42
  • @thecoshman: I was going to mark it as a duplicate and post this answer there, but before doing so, I realized that I could not, because an answer demonstrating pointer-to-member would would make no sense on the other question. That convinced me it is not a duplicate. A member is not a variable until the object instance is specified. (There's a big difference between `&numbers::x` and `&Input.x`) – Ben Voigt Jul 23 '14 at 16:45
0

If you don't care too much about the performance overhead of a std::function<> you can have it a bit more readable:

See it Live On Coliru

#include <iostream>
#include <functional>
#include <map>

using namespace std;

struct Numbers { double X, Y, Z; };

int main()
{
    Numbers Input;
    string memberVariable;
    double variableValue;

    map<string, function<double&(Numbers&)> > mappings {
        { "X", &Numbers::X },
        { "Y", &Numbers::Y },
        { "Z", &Numbers::Z },
    };

    cout << "Enter number" << endl;
    cin  >> variableValue;            // User enters 100.0 for example
    cout << "Enter variable" << endl;
    cin  >> memberVariable;           // User enters a char or string that equals X

    mappings[memberVariable](Input) = variableValue;
}
sehe
  • 374,641
  • 47
  • 450
  • 633