简体   繁体   中英

c++ dynamically accessing member variables

Right now I have a switch statement that takes in an input and choose one of the following actions:

Option 1

for(; i < queue_size; ++i)
{
   prepared->setString(i+1, queue.at(i).model);
}

Option 2

for(; i < queue_size; ++i)
{
   prepared->setString(i+1, queue.at(i).manufacturer);
}

Option 3

for(; i < queue_size; ++i)
{
   prepared->setString(i+1, queue.at(i).name);
}

In PHP, you would be able to do the same doing something like this:

$queue[i][$member];

$member could then be set to "name", "manufacturer", etc.

Is there any way to do something similar or more robust in C++?

Thanks in advance for any help/suggestions!

Using C++11 std::function or boost::function if you don't have C++11:

std::map<YourSwitchType, std::function<void(void)> functionMap;

then define functions such as

void manufacturString() {
  for(; i < queue_size; ++i) {
    prepared->setString(i+1, queue.at(i).manufacturer);
  }
}

for each case, and populate the map with these.

functionMap[someSwitchValue] = std::bind(&ThisType::manufactureString, this);

Then you can just call them:

functionMap[someSwitchValue]();

One advantage of this approach is that it doesn't limit you to member functions. You can put non-member functions, functors, static member and non-member functions in the same map. The only limitation is that after binding, they return void and take no arguments (that is specific to this example).

You could do this with a map from your property names to pointer-to-member. But it's a bit of work (you need to create that mapping yourself), and the syntax gets a bit hairy. (And all the members you want to address this way must be of the same type.)

Demo:

#include <iostream>
#include <map>

struct Foo {
    std::string name;
    std::string address;
};

typedef std::string Foo::* FooMemPtr;
typedef std::map<std::string, FooMemPtr> propmap;

int main()
{
    propmap props;
    props["name"] = &Foo::name;
    props["address"] = &Foo::address;

    /* ... */

    Foo myfoo;
    myfoo.*(props["name"]) = "myname";
    myfoo.*(props["address"]) = "myaddress";
    std::cout << myfoo.*(props["address"]) << std::endl;
    std::cout << myfoo.*(props["name"]) << std::endl;
}

If you can use enums instead of strings, then you can access name, manufacturer, etc. indexed off of the enum values. It depends on how dynamic you need to be.

Use the STL map to perform this. It works as you would do in PHP.

One option is to pass an extractor functor to the function:

#include <string>
#include <vector>
#include <boost/bind.hpp>

struct Some {
    std::string model, manufacturer, name;
};

struct Prepared {
    void setString(size_t, std::string const&);
};

template<class Extractor>
void foo(Extractor extract) {
    Prepared* prepared = 0;
    std::vector<Some> queue;
    size_t i, queue_size = queue.size();
    for(; i < queue_size; ++i) {
        prepared->setString(i+1, extract(queue.at(i)));
    }
}

int main() {
    // C++03
    foo(boost::bind(&Some::model, _1));
    foo(boost::bind(&Some::manufacturer, _1));
    foo(boost::bind(&Some::name, _1));

    // C++11
    foo([](Some const& some){ return some.model; });
    foo([](Some const& some){ return some.manufacturer; });
    foo([](Some const& some){ return some.name; });
}

You can do this type of thing using member variable points (there are also member function pointers). The boost bind functions are more generic, but this is ultimately what they are doing underneath (at least in this scenario).

#include <iostream>
#include <string>

struct item
{
    std::string a, b;
};

//the really ugly syntax for a pointer-to-member-variable
typedef std::string item::*mem_str;

//show each member variable from the list
void show_test( item * objs, size_t len, mem_str var )
{
    for( size_t i=0; i < len; ++i )
        std::cout << objs[i].*var << std::endl;
}

int main()
{
    //create some test data
    item test[2];
    test[0].a = "A-Zero";
    test[0].b = "B-Zero";

    test[1].a = "A-One";
    test[1].b = "B-One";

    //show variables
    show_test( test, 2, &item::a );
    show_test( test, 2, &item::b );

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM