user430926
user430926

Reputation: 4057

how to do function pointer like in C++, but for variable

sorry for the title, it's quite confusing to explain, but it will be more clear with example

In C, I used to write program like this :

void createClassA()
{
}

void createClassB()
{
}

typedef struct {
        const char *name;
        void (*create_class)();
} MapList;

MapList mapList[] = {
        {"A", &createClassA},
        {"B", &createClassB },
};

void create(const char *name)
{
        int i=0;
        for (i=0; i < sizeof(mapList) / sizeof(MapList); i++) {
                if (strcmp(name, mapList[i].name) == 0) mapList[i].create_class();
        }
}

int main(int argc, char** argv)
{
        if (argc < 1) return -1;
        create(argv[1]);

        return 0;
}

So if I have more types, all I have to do is to create the function itself and add it to the mapList.

but in C++, the best I can do is something like:

#include <iostream>

class myA{};
class myB{};
class myC{};

class Base
{
public:
        template<class T>
        void createClass()
        {
                T* t = new T();
                //t->doSomethingUseful();
        }

        void create(const std::string name)
        {
                if (name=="A") createClass<myA>();
                else if (name=="B") createClass<myB>();
                else if (name=="C") createClass<myC>();
        }
};

int main(int agrc, char** argv)
{
        if (argc<1) return -1;
        Base b;
        b.create(argv[0]);

        return 0;
}

Can I create something like :

typedef struct {
   std::string name;
   [CLASS_TYPE] class_type; <== I don't know how
} MapList;

so I can create a mapping List but fill it with class Type, something like

mapList[] = {
   {"A", myA},
   {"B", myB},
   {"B", myC},
};

and then I can create iteration like:

for (int i=0; i<sizeof(mapList) / sizeof(MapList); i++) {
   if (mapList[i].name == name) b.createClass<mapList[i].classType>();
}

Thanks guys :)

Upvotes: 0

Views: 95

Answers (4)

Shoe
Shoe

Reputation: 76240

In C++ you can use std::function in the <functional> standard header. Specifically, your example for the map list can be written as (with lambdas):

std::map<std::string, std::function<void()>> mapList;
mapList["A"] = []() { /* ... */ };
mapList["B"] = []() { /* ... */ };

or just (without lambdas):

void createClassA() {}
void createClassB() {}

std::map<std::string, std::function<void()>> mapList;
mapList["A"] = createClassA;
mapList["B"] = createClassB;

Upvotes: 2

Ilya Kobelevskiy
Ilya Kobelevskiy

Reputation: 5345

You can use derived classes and object factory, something like that would work. You can improve it by make create method to return smart pointers and make A and B constructors private to avoid creating objects on the stack.

class Base
{
static Base* create(const char *name);
};

class A : public Base
{
};

class B : public Base
{

};

Base* Base::create(const char *name)
{
if(strcmp(name,"A")==0)
return new A();
if(strcmp(name,"B")==0)
return new B();

return 0;
}

int main(int agrc, char** argv)
{
        if (argc<1) return -1;
        Base *b = Base::create(argv[0]);

        return 0;
}

It is somewhat similar to solution by @dasblinkenlight, but avoid virtual functions overhead.

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726579

In C++ you could do that with virtual functions:

struct ClassMaker {
    virtual void create_class() = 0;
};
struct ClassMakerA : public ClassMaker {
    virtual void create_class() {
        ...
    }
};
struct ClassMakerB : public ClassMaker {
    virtual void create_class() {
        ...
    }
};

Now you can create a std::map<std::string,ClassMaker*> with your "factories", like this:

ClassMakerA makerA;
ClassMakerB makerB;
// This syntax requires C++11
std::map<std::string,ClassMaker*> makers = {
    {"A", &makerA}
,   {"B", &makerB}
};

Finally, you can now call create_class() based on a std::string value, like this:

void create(const std::string &name) {
    ClassMaker *maker = makers[name];
    if (maker) maker -> create_class();
}

Upvotes: 1

JBL
JBL

Reputation: 12907

You could look for Boost::any, which provides "a variant value type".

Upvotes: 0

Related Questions