Template Variant Parameter Auflösung



  • Ich befasse mich seit Kurzem mit Templates Variants unter C++11. Nun möchte ich ein Objekt T per Lambda Expression erzeugen dessen Konstruktor Argumente ich dynamisch zur Laufzeit auflöse um Dependency Injection über einen IOC Container nachzubilden.

    Ich habe folgende Konstrukte:

    struct A{};
    struct B{};
    
    struct C
    {
    	C(std::shared_ptr<A> a, std::shared_ptr<B> b)
    	:m_a(a), m_b(b){}
    
    	std::shared_ptr<A> m_a = nullptr;
    	std::shared_ptr<B> m_b = nullptr;
    };
    

    Nun möchte ich ein Objekt vom Typ C erzeugen, dessen Abhängigkeiten A und B ich dynamisch zur Laufzeit bestimme. Das funktioniert bestens sofern ich den konkreten Typen von A und B kenne. Hierfür habe ich mir folgende Funktion gebaut:

    template <typename T>
    std::shared_ptr<T> resolve(void) const
    {
         return std::make_shared<T>();
    }
    
    template <typename T, typename ... D>
    void wire(void)
    {
         auto delegate = [this]() -> T * {
             return new T(this->resolve<D>()...);
         };
    }
    

    Nun habe ich aber den Fall, dass ich auch verschiedene Typen Ausprägungen habe, die ich über einen Namen unterscheide und über diesen Namen auflösen kann. Hiefür habe ich eine weitere Resolve Funktion:

    template <typename T>
    std::shared_ptr<T> resolve(const std::string& name) const
    {
         ...
    }
    

    Nur wie bekomme ich das mit meiner Wire-Funktion verheiratet? Ich hatte mir gedacht ein separates Template hierfür zu nuzten, das den Zieltypen kennt und selber ein logischen Container abbildet, der alle relevanten Daten hält (u.A. den möglichen Namen).

    template <typename T>
    struct Dependency
    {
    	Dependency(const std::string& name)
            :m_type(typeid(T)), m_name(name){}
    
    	std::type_index m_type;
            std::string m_name = "undefined";
    };
    

    So dass ich folgenden wire Aufruf erhalte:

    template <typename D>
        D resolve(const D& dependency) { 
          ...  hier D auswerten und konkretes Resultat zurückgeben ...
        } 
    
        template <typename T, typename... D>
        void wire(const D&... dependency)
        {
           auto delegate = [&] {
               return new T(this->resolve(dependency)...);
           };
    
           delegate();	   
        }
    

    Das mag der Compiler jedoch nicht, da er im lambda Ausdruck die Abhängigkeit nicht in den Rückgabewert des resolve Aufrufes überführt sondern D ... durch die konkreten dependencies ergänzt die der C Konstruktor nicht kennt ...

    test.cpp: In instantiation of 'X::wire(const D& ...)::<lambda()> [with T = C; D = {DEP<A>, DEP<B>}]':
    test.cpp:89:39: required from 'struct X::wire(const D& ...) [with T = C; D = {DEP<A>, DEP<B>}]::<lambda()>'
    test.cpp:90:8: required from 'void X::wire(const D& ...) [with T = C; D = {DEP<A>, DEP<B>}]'
    test.cpp:136:26: required from here
    test.cpp:90:8: error: no matching function for call to 'C::C(DEP<A1>, {DEP<A>, DEP<B>})'

    Ist mein Vorhaben überhaupt möglich? Wenn ja wie?

    ~Edit v. Arcoth, Code-Tags~


  • Mod

    Kannst du das evtl. in einem zusammenhängenden minimalen Code transformieren? Ist ein bisschen schwierig, beim Lesen mehrere Codeblöcke gleichzeitig zu überblicken.


  • Mod

    Habe dir Code-Tags spendiert.

    Ich stimme camper zu, habe aber noch eine Frage:

    template <typename D>
        D resolve(const D& dependency) { 
          ...  hier D auswerten und konkretes Resultat zurückgeben ...
        }
    

    Müssen nicht alle resolve -Funktionstemplates shared_ptr 's zurückgeben?



  • Stimme Euch zu, war wohl gestern etwas spät. Jetzt wo ich das ganze noch einmal gelesen habe, kommt es mir auch etwas schwammig vor (zudem ist das ganze Fehlerhaft, wie Acroth es korrekt bemerkt hat) ...

    Leider ist das Problem auch sehr komplex ... Hier einmal der Versuch es zu vereinfachen:

    #include <type_traits>
    #include <unordered_map> 
    #include <iostream> 
    
    template <typename T>
    struct D
    {
    	D(){}
    
    	std::shared_ptr<D> resolve(void) {
    		return std::make_shared<D>();
    	}
    };
    
    struct X
    {
        template <typename T, typename ... D>
        void wire(const D& ... dependency){
            auto delegate = [&] {
                return new T(this->resolve(dependency)...);
            };
            delegate();
        }
    
        template <typename D>
        std::shared_ptr<D> resolve (const D& dependency){
            return dependency.resolve();
        }	
    };
    
    struct A{};
    struct B{}; 
    struct C
    {
        C(std::shared_ptr<A> a, std::shared_ptr<B> b)
        :m_a(a), m_b(b){}
    
        std::shared_ptr<A> m_a = nullptr;
        std::shared_ptr<B> m_b = nullptr;
    };
    
    int main()
    {
      try{
    	X().wire<C>(D<A>(), D<B>());
      }
      catch(...){
           printf("Unknown exception caught!");
           return 1;
      }
      // return successfully
      return 0;
    }
    

    Ziel ist es ein transientes Objekt vom Typ C zu erstellen und dabei die Abhängigkeiten dynamisch aufzulösen. Zur Vereinfachung habe ich dem Dependency Object D eine resolve Methode verpasst, die eigentlich vom IOC Container aufgerufen werden muss.

    Das Problem bleibt jedoch bestehen. Beim Auflösen der Lambda-Closure in der Wire-Funktion wird an den Konstruktor von C die Dependency (std::shared_ptr<D<A> und std::shared_ptr<D<B>>) übergeben und nicht in das Ergebnis std::shared_ptr<A> bzw. std::shared_ptr<B> aufgelöst, so dass es zu folgendem Compile-Fehler kommt:

    test.cpp: In instantiation of 'X::wire(const D& ...)::<lambda()> [with T = C; D = {D<A>, D<B>}]':
    test.cpp:80:40: required from 'struct X::wire(const D& ...) [with T = C; D = {D<A>, D<B>}]::<lambda()>'
    test.cpp::81:9: required from 'void X::wire(const D& ...) [with T = C; D = {D<A>, D<B>}]'
    test.cpp::111:28: required from here
    test.cpp::81:9: error: no matching function for call to 'C::C(std::shared_ptr<D<A> >, std::shared_ptr<D<B> >)'
    };
    ^
    test.cpp::81:9: note: candidates are:
    test.cpp:💯5: note: C::C(std::shared_ptr<A>, std::shared_ptr<B>)
    C(std::shared_ptr<A> a, std::shared_ptr<B> b)
    ^
    test.cpp:💯5: note: no known conversion for argument 1 from 'std::shared_ptr<D<A> >' to 'std::shared_ptr<A>
    test.cpp::98:8: note: C::C(const C&)
    struct C

    Hoffe ich konnte jetzt das Ganze etwas entwirren 😉



  • Der kann D<A> nicht zu A konvertieren.


  • Mod

    sollte das nicht

    template <typename T>
    struct D
    {
    	D(){}
    
    	std::shared_ptr<T> resolve(void) {// !
    		return std::make_shared<T>(); // !
    	}
    };
    // ...
    
    struct X
    {
    // ...
    
        template <typename D>
        auto resolve (const D& dependency){
            return dependency.resolve();
        }  
    };
    

    sein?



  • Danke Camper, stimmt natürlich, da hab ich etwas gepennt. Aber diese Korrektur ändert nichts, der Fehler bleibt bestehen:

    test.cpp: In instantiation of 'X::wire(const D& ...)::<lambda()> [with T = C; D = {D<A>, D<B>}]':
    test.cpp:62:40: required from 'struct X::wire(const D& ...) [with T = C; D = {D<A>, D<B>}]::<lambda()>'
    test.cpp:63:9: required from 'void X::wire(const D& ...) [with T = C; D = {D<A>, D<B>}]'
    test.cpp:87:28: required from here
    test.cpp:63:9: error: no matching function for call to 'C::C(std::shared_ptr<D<A> >, std::shared_ptr<D<B> >)'
    };
    ^
    test.cpp:63:9: note: candidates are:
    test.cpp:77:5: note: C::C(std::shared_ptr<A>, std::shared_ptr<B>)
    C(std::shared_ptr<A> a, std::shared_ptr<B> b)
    ^
    test.cpp:77:5: note: no known conversion for argument 1 from 'std::shared_ptr<D<A> >' to 'std::shared_ptr<A>'
    test.cpp:75:8: note: C::C(const C&)
    struct C
    ^
    test.cpp:75:8: note: candidate expects 1 argument, 2 provided
    test.cpp:75:8: note: C::C(C&&)
    test.cpp:75:8: note: candidate expects 1 argument, 2 provided
    test.cpp: In instantiation of 'std::shared_ptr<_Tp1> X::resolve(const D&) [with D = D<A>]':
    test.cpp:63:9: required from 'X::wire(const D& ...)::<lambda()> [with T = C; D = {D<A>, D<B>}]'
    test.cpp:62:40: required from 'struct X::wire(const D& ...) [with T = C; D = {D<A>, D<B>}]::<lambda()>'
    test.cpp.cpp:63:9: required from 'void X::wire(const D& ...) [with T = C; D = {D<A>, D<B>}]'
    test.cpp:87:28: required from here
    test.cpp:69:35: error: passing 'const D<A>' as 'this' argument of 'std::shared_ptr<_Tp1> D<T>::resolve() [with T = A]' discards qualifiers [-fpermissive]
    return dependency.resolve();
    ^
    test.cpp:69:35: error: could not convert 'D<T>::resolve() [with T = A]()' from 'std::shared_ptr<A>' to 'std::shared_ptr<D<A> >'
    test.cpp: In instantiation of 'std::shared_ptr<_Tp1> X::resolve(const D&) [with D = D<B>]':
    test.cpp:63:9: required from 'X::wire(const D& ...)::<lambda()> [with T = C; D = {D<A>, D<B>}]'
    test.cpp:62:40: required from 'struct X::wire(const D& ...) [with T = C; D = {D<A>, D<B>}]::<lambda()>'
    test.cpp:63:9: required from 'void X::wire(const D& ...) [with T = C; D = {D<A>, D<B>}]'
    test.cpp:87:28: required from here
    c:/binrev/repository/ioc-deps/src/signals.cpp:69:35: error: passing 'const D<B>' as 'this' argument of 'std::shared_ptr<_Tp1> D<T>::resolve() [with T = B]' discards qualifiers [-fpermissive]
    test.cpp:69:35: error: could not convert 'D<T>::resolve() [with T = B]()' from 'std::shared_ptr<B>' to 'std::shared_ptr<D<B> >'
    make[2]: *** [CMakeFiles/signals.dir/src/signals.cpp.obj] Error 1
    make[1]: *** [CMakeFiles/signals.dir/all] Error 2
    make: *** [all] Error 2


Anmelden zum Antworten