Peter
Peter

Reputation: 1687

call functions depending on a string Parameter

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.

Upvotes: 1

Views: 1032

Answers (5)

Dietmar Kühl
Dietmar Kühl

Reputation: 153810

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
    }
}

Upvotes: 2

herohuyongtao
herohuyongtao

Reputation: 50657

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); 

Upvotes: 0

Jarod42
Jarod42

Reputation: 217135

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;
}

Upvotes: 0

David G
David G

Reputation: 96800

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);
}

Upvotes: 0

Manu343726
Manu343726

Reputation: 14174

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!

Upvotes: 3

Related Questions