Mittels std::string zu Funktion gelangen



  • Heho, ich überlege mir gerade bzw. versuche herauszufinden, wie man es am besten löst eine Zuordnung und Aufrufung von std::string zu einer gewünschten Funktion zu erstellen. Also wie man am besten eine Funktionentabelle aufstellt aus der die entsprechende Funktion herausgefischt wird, eben je nach string(etwa "sin" -> sin()).
    Speziell geht es dabei natürlich über Funktionen mit verschiedenen return types oder verschiedenen Argumentenlisten. Derzeit fällt mir dazu nur ein für int und double (return) types separate Listen zu erstellen und dann zu überlegen welche man derzeit benötigt.
    Aber wie macht man das am geschicktesten? Vielen Dank für eure Hilfe.

    Ps.: Hätte schwören können ich hätte vorhin schonmal das thema eröffnet und der thread wäre verschwunden^^



  • boost::any wäre eine Möglichkeit.



  • Na ja, ne Möglichkeit wäre sowas:

    #include <unordered_map>
    #include <iostream>
    #include <string>
    #include <stdexcept>
    
    class blubb
    {
      std::unordered_map<std::string, void (*) ()> functions_;
    
    public:
      template <typename R, typename... Args>
      R call(const std::string& name, Args... args)
      {
        auto found = functions_.find(name);
    
        if (found == functions_.end())
          throw std::runtime_error("blubb");
    
        return reinterpret_cast<R (*) (Args...)>(found->second)(args...);
      }
      template <typename F>
      void set(std::string name, F f)
      {
        functions_[std::move(name)] = reinterpret_cast<void (*) ()>(f);
      }
    };
    
    double add(double a, double b)
    {
      return a + b;
    }
    
    int main()
    {
      blubb b;
      b.set("add", add);
      std::cout << "5 + 3 = " << b.call<double>("add", 5.0, 3.0) << '\n';
    }
    

    Aber ehrlich gesagt.. super gefährlich und nicht gerade schön. Ich würde eher nach einer komplett anderen Lösung suchen, wenn unterschiedliche maps nicht in Frage kommen.



  • Danke schonmal für die Antworten.
    Doch doch, unterschiedliche Maps kommen in Betracht(werds vermutlich auch so machen), aber würde gerne wissen ob es da bessere/andere Standardlösungen gibt.



  • Hmmm, ärgerlich an der std::map variante ist(so wie ich das sehe), das man, auch wenn nur eine vorhanden ist, funktionen mit von anderen abweichender parameterzahl in eine extra std::map packen muss, da sie einen eigenen funktionenzeigertypen benötigen 😕



  • du könntest verschiedene structs als Parameter übergeben in denen dann die Parameter stecken

    greetz KN4CK3R



  • Namenloser324 schrieb:

    Heho, ich überlege mir gerade bzw. versuche herauszufinden, wie man es am besten löst eine Zuordnung und Aufrufung von std::string zu einer gewünschten Funktion zu erstellen.

    Wozu denn eigentlich?



  • krümelkacker schrieb:

    Namenloser324 schrieb:

    Heho, ich überlege mir gerade bzw. versuche herauszufinden, wie man es am besten löst eine Zuordnung und Aufrufung von std::string zu einer gewünschten Funktion zu erstellen.

    Wozu denn eigentlich?

    parser von arithmetischen ausdrücken bei denen auch noch einige funktionen aufrufbar sein sollen



  • Schreib deine Funktionen als T(vector<T>), wobei T der Werttyp ist.



  • struct value_type
    {
    	enum Enum
    	{
    		Integer,
    		Float,
    		Boolean,
    		Nil,
    	};
    };
    typedef value_type::Enum ValueType;
    
    typedef long long Integer;
    typedef double Float;
    typedef bool Boolean;
    
    struct Value
    {
    	ValueType type;
    	union
    	{
    		Integer i;
    		Float f;
    		Boolean b;
    	};
    
    	Value();
    	Value(Integer i);
    	Value(Float f);
    	Value(Boolean b);
    };
    
    Integer toInteger(const Value &value)
    {
    	switch (value.type)
    	{
    	case value_type::Integer: return value.i;
    	case value_type::Float: return static_cast<Integer>(value.f);
    	case value_type::Boolean: return value.b;
    	default: throw std::runtime_error("Not convertible to integer");
    	}
    }
    
    Float toFloat(const Value &value);
    
    typedef std::function<Value (const std::vector<Value> &)> Function;
    
    struct FunctionRegistry
    {
    	void add(std::string name, Function function);
    	Value call(const std::string &name, const std::vector<Value> &arguments) const;
    	//impl sollte klar sein
    
    private:
    
    	typedef std::map<std::string, Function> FunctionsByName;
    
    	FunctionsByName m_functionsByName;
    };
    
    static void expectArgumentCount(const std::vector<Value> &args, size_t count)
    {
    	if (args.size() != count)
    	{
    		throw std::runtime_error("Wrong argument count");
    	}
    }
    
    int main()
    {
    	FunctionRegistry reg;
    	reg.add("sin", [](const std::vector<Value> &args) -> Value
    	{
    		expectArgumentCount(args, 1);
    		return Value(
    			static_cast<Float>(std::sin(
    				toFloat(args[0]))));
    	});
    	cout << toFloat(reg.call("sin", std::vector<Value>(1, Value(123)))) << endl;
    }
    


  • Kellerautomat schrieb:

    Schreib deine Funktionen als T(vector<T>), wobei T der Werttyp ist.

    Gute Idee, der vector ist natürlich elegant.
    @TyRoXx
    wow, vielen dank, muss mir das erstmal ne weile anschauen bis ich da durchsteige 🙂 sehr cool!



  • Ne Frage zum Code von dir TyRoxx:

    was genau wird hier gemacht:

    reg.add("sin", [](const std::vector<Value> &args) -> Value
        {
            expectArgumentCount(args, 1);
            return Value(
                static_cast<Float>(std::sin(
                    toFloat(args[0]))));
        });
    

    ist da nicht aufruf der funktion mit definition verwechselt?!
    und wieso gibt es dort die geschweifte klammer? wird das array initialisiert?!



  • Das ist kein Array, das ist eine Lambadafunktion von C++11. Genaue Syntax am besten selber googeln.



  • Jonas OSDever schrieb:

    Das ist kein Array, das ist eine Lambadafunktion von C++11. Genaue Syntax am besten selber googeln.

    Ah, gut zu wissen, damit habe ich noch gar nicht gearbeitet, danke.


Anmelden zum Antworten