1

I try to find a way to call functions depending on one String-Parameter.

Enums or Int are ok too for the Parametertype. Maybe there is something more ? Is there a way to do it like this:

myFunction(string functionParameter, int value){
this->functionParameter(value);}

What is the best way for this? I know there are some similar Questions, but i didnt found a Answer that really fits my Problem.

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
Peter
  • 1,655
  • 22
  • 44
  • 2
    You will need to create the mapping from string to function yourself; the language doesn't support doing it automatically. – Alan Stokes Dec 27 '13 at 16:34

5 Answers5

3

Just use a map to map from strings to functions:

void f1()
{
    std::cout << "f1!" << std::endl;
}

void f2()
{
    std::cout << "f2!" << std::endl;
}

void f3()
{
    std::cout << "f3!" << std::endl;
}

int main()
{
    std::unordered_map<std::string,std::function<void()>> map;

    map["f1"] = f1;
    map["f2"] = f2;
    map["f3"] = f3;

    map["f1"]();
    map["f2"]();
    map["f3"]();
}

This outputs:

f1!
f2!
f3!

Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • 1
    Note that using an unknown name as in `map["too"]();` will have undefined behavior. – Dietmar Kühl Dec 27 '13 at 17:02
  • best answer I've seen here. Anyone writing a class with templates is shooting a target with a tank cannon. Way overkill here folks. – C.J. Dec 27 '13 at 17:19
  • @user3072101 in SO, the way to thank is to accept the answer :) Jokes apart, if an answer solves your problem, accept it. – Manu343726 Dec 27 '13 at 20:46
  • @CJohnson: encapsulate the solution in a class seems better to me (Hard coded function type is correct to me, generic template class is not necessary better). So the 'fix' to check if name is known can be done to only one place (instead of 3 here). – Jarod42 Dec 27 '13 at 22:15
2

C++ doesn't have direct support to call functions using the name. You'll need to create the mapping somehow. The easiest approach is probably to create a map of a suitable std::function<...> type:

void f(int);
void g(int);
typedef std::function<void(int)> Function;
std:: map<std::string, Function> functions;
// ...
functions["f"] = f;
functions["g"] = g;

void call(std::string const& name, int x) {
    auto it = functions.find(name);
    if (it->second != functions.end()) {
        it->second(x);
    }
    else {
        // deal with unknown functions
    }
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
0

You can map the string to the function pointer. Try something like this:

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

class X;

template<class X>
class handler_factory;

template<>
class handler_factory<X>
{
private:
    using HandlerType = void (X::*)(int);
public:
    handler_factory();

    HandlerType get(const std::string& name) const
    {
        if (handlers.find(name) == handlers.end())
            return nullptr;
        else
            return (*handlers.find(name)).second;
    }
private:
    std::map<std::string, HandlerType> handlers;
};

class X
{
public:
    friend class handler_factory<X>;
private:
    void f(int);
    void h(int);
};

handler_factory<X>::handler_factory()
{
    handlers["f"] = &X::f;
    handlers["h"] = &X::h;
}

void X::f(int) { std::cout << "X::f();"; }
void X::h(int) { std::cout << "X::h();"; }

Your class (in this example X) can have a function dispatch_method that looks like:

template<typename... Args>
void dispatch_method(const std::string& name, Args&&... args)
{
    if (find_handler(name))
        (this->*find_handler(name))(std::forward<Args>(args...));
}

Where find_handler is a helper method:

private:
    auto find_handler(const std::string& name)
        -> decltype(handler_factory<X>().get(name))
    {
        return handler_factory<X>().get(name);
    }

Then you can call it like this:

int main()
{
    X{}.dispatch_method("f", 5);
}
David G
  • 94,763
  • 41
  • 167
  • 253
  • @CJohnson Then you clearly are not familiar with C++11 features. `X{}` instantiates an object using *aggregate-initialization*, which in this case is no different than using `X().f(...)`. You don't even have to instantiate the object that way (I did it for brevity). You could also do `X x; x.dispatch_method("f", 5);`. – David G Dec 27 '13 at 17:19
  • Just because you can, doesn't mean you should. This code is not maintainable at all. – C.J. Dec 27 '13 at 17:22
  • @CJohnson I don't get how you see it as unmaintainable. In my opinion this is very maintainable code. And if the only thing you could point out was my use of `X{}` as being hard to read, then I don't see how you can judge the rest of my code similarly. – David G Dec 27 '13 at 17:25
0

You may use something like:

#include <map>
#include <functional>
#include <stdexcept>
#include <string>

template<typename T> class Caller;

template<typename Ret, typename... Args>
class Caller<std::function<Ret(Args...)>>
{
public:
    typedef std::function<Ret(Args...)> FuncType;

    void add(const std::string& name, FuncType f)
    {
        functions[name] = f;
    }

    Ret call(const std::string& name, Args... args)
    {
        auto it = functions.find(name);

        if (it == functions.end()) {
            // Or any other error
            throw std::runtime_error("unknown " + name + "function");
        }
        return (it->second)(args...);
    }

private:
    std::map<std::string, FuncType> functions;
};

So lets test it:

int minus(int a) { return -a; }

int main(int argc, char** argv)
{
    Caller<std::function<int (int)>> caller;

    caller.add("+1", [](int a) { return a + 1; } );
    caller.add("minus", minus);

    caller.call("minus", -42); // calls minus(-42), returns 42
    caller.call("+1", 41);     // calls the lambda, returns 42
    return 0;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

This is similar to question here. You need to create a map like this map<string, class::method>, then you can use its signature to search for function and call it.


Two ways are available for you:

1. Without using any 3rd-party library (in row C++):

    #include <map>
    #include <string>

    struct Math
    {
        double sinFunc(double x) { return 0.33; };
        double cosFunc(double x) { return 0.66; };
    };

    typedef double (Math::*math_method_t)(double);
    typedef std::map<std::string, math_method_t> math_func_map_t;

    int main()
    {

        math_func_map_t mapping;
        mapping["sin"] = &Math::sinFunc;
        mapping["cos"] = &Math::cosFunc;

        std::string function = std::string("sin");
        math_func_map_t::iterator x = mapping.find(function);
        int result = 0;

        if (x != mapping.end()) {
            Math m;
            result = (m.*(x->second))(20);
        }
    }

2. By using Boost library: The most convenient notation for method is function<signature> where function is either included in boost or in <utility>.

The signature would be like this.

        map<string, function<double (double)> map; ...
        map["sin"](1.0); 
Community
  • 1
  • 1
herohuyongtao
  • 49,413
  • 29
  • 133
  • 174