tags:

views:

805

answers:

6

Hi, i was wondering if there was a way to do this in C++?

void func1(const std::string& s)
{
std::cout << s << std::endl;
}

void func2(int me)
{
std::cout << me << std::endl;
}

int main()
{
std::map<std::string, boost::function< ??? > > a_map;

a_map["func1"] = &func1;
a_map["func1"]("HELLO");

}

Is there any way to do what i have above using boost function and a map?

+2  A: 

You probably can't use the std::map since it is a homogenous container. Try, something like boost::variant (they support the visitor pattern) or boost::tuple

dirkgently
boost::variant is the way to go here. I've done this in code and it works great.In a few lines (of MPL) I support a map of int,float,double,String and a function0 of each of those types.
George
+2  A: 

There are ways to store the functions, the problem is, in order to be able to call the function with the desired argument you'd have to know the calling signature of the function anyways, and if you have that information, you might as well use separate maps, or use a more complicated object than boost::function.

If you're willing to do a bit of work and have a finite number of signatures, you could just do something like this:

class MultiFunc
{
protected:
    MultiFunc() {}

public:
    typedef void (*stringFunc)(const std::string&);
    typedef void (*intFunc)(int);

    static MultiFunc *Create(stringFunc function);
    static MultiFunc *Create(intFunc function);

    virtual void operator()(const string &) { throw exception(); }
    virtual void operator()(int) { throw exception(); }
    virtual ~MultiFunc();
};

class MultiFuncString : public MultiFunc
{
private:
    stringFunc Function;
public:
    MultiFuncString(stringFunc function) : Function(function) {}
    virtual void operator()(const string &arg) { Function(arg); }
};

class MultiFuncInt : public MultiFunc
{
private:
    intFunc Function;
public:
    MultiFuncInt(intFunc function) : Function(function) {}
    virtual void operator()(int arg) { Function(arg); }
};

MultiFunc *MultiFunc::Create(MultiFunc::stringFunc function)
{
    return new MultiFuncString(function);
}
MultiFunc *MultiFunc::Create(MultiFunc::intFunc function)
{
    return new MultiFuncInt(function);
}

void func1(const std::string& s)
{
std::cout << s << std::endl;
}
void func2(int me)
{
std::cout << me << std::endl;
}

int main()
{
    map<string, MultiFunc *> a_map;
    a_map["func1"] = MultiFunc::Create(&func1);
    (*a_map["func1"])("Hello");
    a_map["func2"] = MultiFunc::Create(&func2);
    (*a_map["func2"])(3);

    // Remember to delete the MultiFunc object, or use smart pointers.
}

This outputs:

Hello
3

Unfortunately, you can't make templated virtual functions or you easily generalize this all.

Eclipse
A: 

Hello uberjumper,

read this link below. It talks about using boost::bind to store the function pointers in std::map

http://www.gamedev.net/community/forums/topic.asp?topic_id=526381&amp;whichpage=1&amp;#3411515

Warrior
A: 

store interfaces:

struct IStringData
{
    virtual std::string get() const = 0;
    virtual ~IStringData() {}
};

and make implementaions, one will just hold string value, other implementation will store functor, maybe you will have other implementations in future.

bb
A: 

No. You can't. Since boost::function isn't polymorphic, it breaks down there. (It takes a fixed set of argument types.)

There was talk about work in that direction on the boost mail-list, though, so search the archives and see if there is some code you could youse.

A workaround would be to use boost::function but then you need to add to the map not your real functions (i.e. func1/func2) but dispatch functions that extracts the type from the any-container and calls the real function. (And bails if it's wrong, just as in any dynamic langugage.)

Marcus Lindblom
+1  A: 

What you are trying to do sounds a little weird. Normally, you would have a container be a collection of abstract types or objects or functions with the same signature. Otherwise, how would you know how to call the function when you are iterating the container? I like to make the container a collection of function objects with a known signature, then use Boost.Bind to store closures that call the function with additional arguments.

For example:

typedef boost::function<void, void> Function;
typedef std::map<std::string, Function> Functions;

Functions functions:

void foo()
{
  ...
}

functions["foo"] = foo;

void bar(std::string &s)
{
  ...
}

// binds the value "hello" to the s parameter
functions["bar"] = boost::bind(bar, "hello");
1800 INFORMATION