Qt - Slot unerkannt durch macro madness



  • Zugrunde liegt folgender code:

    #include <boost/preprocessor/list/for_each_i.hpp>
    #include <boost/preprocessor/tuple/to_list.hpp>
    #include <boost/preprocessor/cat.hpp>
    #include <boost/preprocessor/facilities/overload.hpp>
    #include <boost/preprocessor/control/if.hpp>
    #include <boost/preprocessor/punctuation/comma_if.hpp>
    
    #define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
    #define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
    #define _TRIGGER_PARENTHESIS_(...) ,
    
    #define ISEMPTY(...)                                                    \
    _ISEMPTY(                                                               \
              /* test if there is just one argument, eventually an empty    \
                 one */                                                     \
              HAS_COMMA(__VA_ARGS__),                                       \
              /* test if _TRIGGER_PARENTHESIS_ together with the argument   \
                 adds a comma */                                            \
              HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__),                 \
              /* test if the argument together with a parenthesis           \
                 adds a comma */                                            \
              HAS_COMMA(__VA_ARGS__ (/*empty*/)),                           \
              /* test if placing it between _TRIGGER_PARENTHESIS_ and the   \
                 parenthesis adds a comma */                                \
              HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/))      \
              )
    
    #define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
    #define _ISEMPTY(_0, _1, _2, _3) HAS_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
    #define _IS_EMPTY_CASE_0001 ,
    
    #define ARG_PREFIX _
    
    #define _FORMAL_ARG_CONCAT(r, data, i, elem) BOOST_PP_COMMA_IF(i) elem BOOST_PP_CAT(data, i)
    #define _ACTUAL_ARG_CONCAT(r, data, i, elem) BOOST_PP_COMMA_IF(i) BOOST_PP_CAT(data, i)
    
    #define _EXPANDER(METHOD, ...) BOOST_PP_LIST_FOR_EACH_I(METHOD, ARG_PREFIX, BOOST_PP_TUPLE_TO_LIST((__VA_ARGS__)))
    
    #define _OVERLOADER_DELEGATE_EMPTY(...)
    #define _OVERLOADER_DELEGATE_NON_EMPTY(METHOD, ...) _EXPANDER(METHOD, __VA_ARGS__)
    
    #define _COND_EXPAND(METHOD, ...) BOOST_PP_IF(ISEMPTY(__VA_ARGS__), _OVERLOADER_DELEGATE_EMPTY, _OVERLOADER_DELEGATE_NON_EMPTY)(METHOD, __VA_ARGS__)
    
    #define SIGNAL_FORWARDER_TEMPLATE_INSTANTIATOR(NAME_ADDENDUM, RET_T, .../* args */) \
    class SignalForwarder ## NAME_ADDENDUM : public QObject \
    { \
        Q_OBJECT \
     \
    public slots: \
        RET_T proxySlot(_COND_EXPAND(_FORMAL_ARG_CONCAT, __VA_ARGS__)) \
        { \
            f_(_COND_EXPAND(_ACTUAL_ARG_CONCAT, __VA_ARGS__)); \
        } \
        \
    public: \
        SignalForwarder ## NAME_ADDENDUM (std::function<RET_T(__VA_ARGS__)> const& func) \
            : f_(func) \
        {} \
        \
    private: \
        std::function<RET_T(__VA_ARGS__)> f_; \
    };
    
    SIGNAL_FORWARDER_TEMPLATE_INSTANTIATOR(V0, void())
    

    Was ist das Ziel? Die Macros miemen das verhalten von variadic templates (denn Q_OBJECT Klassen dürfen keine templates sein)
    Ich habe das ding durch den Präprozessor gehaun und rauskommt, was ich erwarte:

    (prettyfied by hand)

    class SignalForwarderV0 : public QObject 
    { 
    	Q_OBJECT 
    
    public slots: 
    	void proxySlot() 
    	{ f_(); } 
    
    public: 
    	SignalForwarderV0 (std::function<void()> const& func) 
    		: f_(func) 
    	{} 
    
    private: 
    	std::function<void()> f_;
    };
    

    proxySlot() ist aufrufbar und hat den typ "void (SignalForwarderV0::*)()"

    aber dennoch erhalte ich von Qt die Meldung

    "void (SignalForwarderV0::*)()QObject::connect: No such slot SignalForwarderV0::proxySlot( )"

    folgendes funktioniert aber (absichtlich nicht eingerückt):

    #define TEST_E
    class SignalForwarderV0 : public QObject { Q_OBJECT public slots: void proxySlot(TEST_E) {} public: SignalForwarderV0 (std::function<void()> const&) {} };
    

    Frage: Ich brauche ein Workaround, einen Fix, eine Alternative, oder zumindest eine Erklärung, warum hier Qt sich weigert den Slot zu finden.



  • Wenn sich da seit den ersten Versionen von QT nichts geändert hat, dann wird der MOC ja vor dem Präprozessor ausgeführt, so daß deine PP-Makros zu spät ersetzt werden.

    Du müstest also in dieser Reihenfolge vorgehen:
    - C++ Präprozessor
    - MOC
    - C++ Compiler

    Verwendest du denn ein Makefile oder eine IDE?



  • qt-creator, weil am Stressfreisten mit qt.



  • Dann müßtest du mal schauen (bzw. im Internet suchen), ob man dort die Build-Reihenfolge ändern kann (ich habe den QT-Creator noch nie verwendet).



  • Qt ist sicher eine der besten für c++ erhältlichen GUI's.
    Aber (auch aus technischen Gründen) es ist immer noch kein gutes C++ !!!
    Wenn man tiefer einsteigt, bekommt man schon probleme an der einen oder anderen STelle.

    Warum sollte man also sowas, wie Du hier versuchst, machen?
    Ne GUI willst du doch nicht damit bauen oder?

    Für Logik Schichten würd ich immer noch auf näher am Standard liegende Bibs greifen(stl / boost), auch für nen Signal / Slot Mechanismus.

    Variante B.
    Schreib dir nen eigenen Generator, der dir die Klassen aus Templates ferig ausformuliert und füg das als prebuild step ein.
    Beispiele dafür sind IDL compiler, GPB Proto "compiler" aka generatoren, QT Translation tools .... etc.
    den Präprozessor kann man wahrscheinlich nicht bei der Reihenfolge vor einen Prebuild Step ziehen, das würde an anderen Stellen sicher krachen.

    mit skriptbasierten buildtools/buildgeneratoren (cmake, ant) kannst sowas aber auch direkt in die buildchain einfügen.

    Ciao ...



  • Das ist Teil einer GUI Anwendung und gedacht als Brücke für Signale an Klassen die keine QObjects sein sollen oder sein können.

    Vielleicht nehme ich mir einfach boost wave und erfinde ein schlaues pragma zum Kennzeichnen und lass die Datei vorverarbeiten ohne includes.

    EDIT: oder ein script, was einfacher portabel wäre.


Anmelden zum Antworten