[SOLVED]How to use variadic template with variadic macro ?



  • Hello everyone!
    I got a problem on mix-using variadic template with variadic macro, could you help me?
    the problem codes are:

    #include <QCoreApplication>
    #include <iostream>
    // template class:
    template<typename T, typename... _Args>
    struct RuntimeClass
    {
       T* (*m_pfnFunction)(_Args&&... __args);
    };
    
    //declaration macro:
    #define DECL_RUNTIMECLASS(T, className, ...) \
       static RuntimeClass<T, __VA_ARGS__> class_##className;
    
    // instance implementation macro
    #define IMPL_RUNTIMECLASS(T, className, pfnFunc, ...) \
    RuntimeClass<T, __VA_ARGS__> className::class_##className\
    ={pfnFunc};
    
    class TestClass
    {
    public:
       int TestFunc(void);
        DECL_RUNTIMECLASS(int, TestClass)
    };
    
    int TestClass::TestFunc(void)
    {
       return 3;
    }
    
    IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc)
    
    int main(int argc, char *argv[])
    {
       QCoreApplication a(argc, argv);
    
       TestClass b;
        std::cout<<b.class_TestClass.m_pfnFunction()<<std::endl;
    
       return a.exec();
    }
    

    When I compile above code with MinGW-w64 v4.9.2, the compiler report error messages:

    ..\testconsole\main.cpp:12:38: error: template argument 2 is invalid
        static RuntimeClass<T, __VA_ARGS__> class_##className;
                                          ^
    ..\testconsole\main.cpp:23:5: note: in expansion of macro 'DECL_RUNTIMECLASS'
         DECL_RUNTIMECLASS(int, TestClass)
         ^
    ..\testconsole\main.cpp:16:28: error: template argument 2 is invalid
     RuntimeClass<T, __VA_ARGS__> className::class_##className\
                                ^
    ..\testconsole\main.cpp:31:1: note: in expansion of macro 'IMPL_RUNTIMECLASS'
     IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc)
     ^
    ..\testconsole\main.cpp:17:1: error: invalid type in declaration before '=' token
     ={pfnFunc};
     ^
    ..\testconsole\main.cpp:31:1: note: in expansion of macro 'IMPL_RUNTIMECLASS'
     IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc)
     ^
    ..\testconsole\main.cpp:17:10: error: cannot convert 'int (TestClass::*)()' to 'int' in initialization
     ={pfnFunc};
              ^
    ..\testconsole\main.cpp:31:1: note: in expansion of macro 'IMPL_RUNTIMECLASS'
     IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc)
     ^
    ..\testconsole\main.cpp: In function 'int main(int, char**)':
    ..\testconsole\main.cpp:38:34: error: request for member 'm_pfnFunction' in 'TestClass::class_TestClass', which is of non-class type 'int'
         std::cout<<b.class_TestClass.m_pfnFunction()<<std::endl;
    

    I had also tried to add 'void' to the macro parameter list by replacing DECL_RUNTIMECLASS(int, TestClass) with DECL_RUNTIMECLASS(int, TestClass, void)
    ,and replacing IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc) with IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc, void)
    But the compiler still reported some error message like this:

    ..\testconsole\main.cpp: In instantiation of 'struct RuntimeClass<int, void>':
    ..\testconsole\main.cpp:31:1:   required from here
    ..\testconsole\main.cpp:7:41: error: forming reference to void
        T* (*m_pfnFunction)(_Args&&... __args);
                                             ^
    ..\testconsole\main.cpp:17:10: error: too many initializers for 'RuntimeClass<int, void>'
     ={pfnFunc};
              ^
    ..\testconsole\main.cpp:31:1: note: in expansion of macro 'IMPL_RUNTIMECLASS'
     IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc, void)
     ^
    ..\testconsole\main.cpp: In function 'int main(int, char**)':
    ..\testconsole\main.cpp:38:34: error: 'struct RuntimeClass<int, void>' has no member named 'm_pfnFunction'
         std::cout<<b.class_TestClass.m_pfnFunction()<<std::endl;
    

    Could you help me to solve this problem? Thanks very much!


  • Moderators

    Could you elaborate on what the code is suppose to be doing? Or provide a code snippet without templates and macros that is doing it on a single type?

    It seems you create a static member struct with an uninitialized function pointer and then try to call it? I'm not sure what you're goal here is.
    Also the =; part in IMPL_RUNTIMECLASS doesn't seem to be valid syntax for anything in c++.



  • @Chris-Kawa
    Thanks for your reply!
    On your second question first, well, I'm very sorry about that for there was a code snippet lost in the source code above, maybe it is caused by the unescaped characters in the post. The correct code of macro IMPL_RUNTIMECLASS should be

    #define IMPL_RUNTIMECLASS(T, className, pfnFunc, ...) \
    RuntimeClass<T, __VA_ARGS__> className::class_##className\
    = { (pfnFunc) };
    

    On your first question: The above source code is a simplified partially implimentation of a template-based java-like reflex mechanism, it has shown where the compiling error was caused in the design. There are two reasons which made me to design a reflex machanism:

    1. I'm not familiar with Java, although it is a great program language. But I really need a reflex mechanism in C++ to satisfy the requirement in my work, and there is not enough time to allow me to learn java and convert my work from C++ to java.
    2. In my work, there are many kind of objects want to be created dynamically in program. On creating, some kind of them require one or two parameter to pass into constructor, and some kind of them requires none. I had try my best to normalize their parameter and decrease the type count of parameter, but there are still many type to be maintained.

    For the above two reasons, I had to implement a java-like reflex mechanism (or a simple version of it). After checking the language features of C++, I think of the template in C++,So I'm trying to design a template-based java-like reflex mechanism to provide the dynamic-creating ability for the different kind of objects.

    My initial design has referenced the design in MFC, a framework in visual C++. I used the templates and macroes to provide the ability of making any class to be a dynamic-creating root base class or to be a derived dynamic-creating class. To fit the various parameter type and count of constructor, I use the variadic template which is provided in C++11 standard, and variadic macro provided in C99 standard. But it seems that there are many conflicts with the combination of these two feature, or maybe I had written the wrong code.

    The above source code is a simplified partially implementation of my design, I provide it to show which part is wrong more clearly, and wish to make the answer of resolvation more easier. I don't know how to resolve it at this time. So if you have a solution, or another way to implement it, please tell me, OK? if you need all the code of my implementation, Just figure out it, I'll post them in the following posts. Thanks!


  • Moderators

    Well, you can't call a member function without an instance from a static member function, so I can't really hep you with that but...

    ...since you're on a Qt forum and your code seems to use Qt... it has a reflection system built in. That's why you put these Q_OBJECT macros and such in classes. The syntax is of course different than that in Java or MFC, but here's an example:

    Given a class like this:

    class TestClass : public QObject {
        Q_OBJECT
    public:
       Q_INVOKABLE TestClass(int foo) : data(foo) {}
       Q_INVOKABLE int TestFunc() { return data; }
    private:
       int data;
    };
    

    You can for example construct an object and call its "TestFunc" like this:

    const auto& meta = TestClass::staticMetaObject;
    auto testInstance = meta.newInstance(Q_ARG(int, 42));
    
    int returnValue;
    meta.invokeMethod(testInstance, "TestFunc", Q_RETURN_ARG(int, returnValue));
    qDebug() << returnValue;
    
    delete testInstance;
    

    You can also enumerate and access all the methods, constructors, parameter counts, names and types, enums and all sorts of other things. It's quite extensive. You can read more about it in the QMetaObject documentation.



  • @Chris-Kawa
    Thank you very much for your advice!

    In my works I need to group the classes that the objects will be constructed from into some classifications, I'm planning to use the runtime type-identification feature. Could you tell me if Qt has built-in runtime type identification feature or not? and if yes, could you tell me how to use it?

    Well... there is still a little question for me with your answer about my code :
    Did you means that the member functionTestClass::TestFunc is not a static member and can not be filled into the static member class_TestClass which type is RuntimeClass<T, __VA_ARGS__>? If it is, this is exactly an error, and misexpressed my design. Now I changed that member function into static, and recompiled this test projects, but there is still some compiling error, could you help me?

    The fixed souce code is:

    #include <QCoreApplication>
    #include <iostream>
    // template class
    template<typename T, typename... _Args>
    struct RuntimeClass
    {
       T* (*m_pfnFunction)(_Args... __args);
    };
    
    // declaration macro:
    #define DECL_RUNTIMECLASS(T, className, ...) \
       static RuntimeClass<T, __VA_ARGS__> class_##className;
    
    // instance implementation macro:
    #define IMPL_RUNTIMECLASS(T, className, pfnFunc, ...) \
    RuntimeClass<T, __VA_ARGS__> className::class_##className\
    ={pfnFunc};
    
    class TestClass
    {
    public:
       static int TestFunc(void);
        DECL_RUNTIMECLASS(int, TestClass)
    };
    
    int TestClass::TestFunc(void)
    {
       return 3;
    }
    
    IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc)
    
    int main(int argc, char *argv[])
    {
       QCoreApplication a(argc, argv);
    
       TestClass b;
        std::cout<<b.class_TestClass.m_pfnFunction()<<std::endl;
    
       return a.exec();
    }
    

    The compiler reports error messages is like below:

    ..\testconsole\main.cpp:12:38: error: template argument 2 is invalid
        static RuntimeClass<T, __VA_ARGS__> class_##className;
                                          ^
    ..\testconsole\main.cpp:23:5: note: in expansion of macro 'DECL_RUNTIMECLASS'
         DECL_RUNTIMECLASS(int, TestClass)
         ^
    ..\testconsole\main.cpp:16:28: error: template argument 2 is invalid
     RuntimeClass<T, __VA_ARGS__> className::class_##className\
                                ^
    ..\testconsole\main.cpp:31:1: note: in expansion of macro 'IMPL_RUNTIMECLASS'
     IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc)
     ^
    ..\testconsole\main.cpp:17:1: error: invalid type in declaration before '=' token
     ={pfnFunc};
     ^
    ..\testconsole\main.cpp:31:1: note: in expansion of macro 'IMPL_RUNTIMECLASS'
     IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc)
     ^
    ..\testconsole\main.cpp:17:10: error: invalid conversion from 'int (*)()' to 'int' [-fpermissive]
     ={pfnFunc};
              ^
    ..\testconsole\main.cpp:31:1: note: in expansion of macro 'IMPL_RUNTIMECLASS'
     IMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc)
     ^
    ..\testconsole\main.cpp: In function 'int main(int, char**)':
    ..\testconsole\main.cpp:38:34: error: request for member 'm_pfnFunction' in 'TestClass::class_TestClass', which is of non-class type 'int'
         std::cout<<b.class_TestClass.m_pfnFunction()<<std::endl;
    

    I think the major problem is the __VA_ARGS__ which is used to fill the template parameter in these two macroesDECL_RUNTIMECLASS and IMPL_RUNTIMECLASS. My purpose is to specify a variadic parameter list in template parameter, and it can be used to specialize the struct RuntimeClass into containing a pointer of function which has a parameter list specialized in compiling time and should be fixed in runtime.

    I don't know If I had explained clearly because of my poor english.If it bothers you, I'll be very sorry about it. And if it really does, please figure it out, I'll try my best to make it more clear to you. Thank you again for your patiency!


  • Moderators

    Ok, with the static member the only problem I found is that your m_pfnFunction has a return type T*, while you pass it a pointer to function returning an int. So changing the signature to T (*m_pfnFunction)(_Args... __args); works for me (I'm on VS2013).

    It also works for me if I change the TestFunc to take an argument, but I need to pass it's type in the DECL_RUNTIMECLASS and IMPL_RUNTIMECLASS as well.

    You could also extend it to work with non-static member functions, but then the call syntax becomes a little weird to say the least:

    #include <QCoreApplication>
    #include <iostream>
    
    template<typename T, typename className, typename... _Args>
    struct RuntimeClass
    {
       T (className::*m_pfnFunction)(_Args... __args);
    };
    
    #define DECL_RUNTIMECLASS(T, className, ...) \
       static RuntimeClass<T, className, __VA_ARGS__> class_##className;
    
    #define IMPL_RUNTIMECLASS(T, className, pfnFunc, ...) \
    RuntimeClass<T, className, __VA_ARGS__> className::class_##className = { (pfnFunc) };
    
    class TestClass
    {
    public:
       int TestFunc(int v);
       DECL_RUNTIMECLASS(int, TestClass, int)
    };
    
    int TestClass::TestFunc(int v)
    {
       return v;
    }
    
    IMPL_RUNTIMECLASS(int, TestClass, &TestClass::TestFunc, int)
    
    int main(int argc, char *argv[])
    {
       QCoreApplication a(argc, argv);
    
       TestClass b;
       std::cout<<(b.*b.class_TestClass.m_pfnFunction)(42)<<std::endl;
    
       return a.exec();
    }
    

    As for the RTTI question about Qt - yes, it supports its own variant of type identification at both compile and runtime. For Qt types it's built-in. For your own types you need to pass your type to Q_DECLARE_METATYPE macro.
    You can then for example identify your type with int type = qMetaTypeId<YourType>();. You could also identify your type by its name eg. int type = QMetatype::type("YourType") or get a name from id: const char* name = QMetaType::typeName(YourTypeId).



  • @Chris-Kawa
    Didn't your compiler in VS2013 reports error on __VA_ARGS__ in macro? The GCC in MinGW-w64(v4.9.2-5 on MSYS2) still consider it as an invalid template argument after fix the source code by your advice. And it is why I missfind the type-mismatch problem in souce -_-!

    Well, on the usage of macro __VA_ARGS__ in my source, could you find any non-standard feature that require ? I'm very doulted why GCC can not make it pass. If there is no non-standard feature in my souce, is there any way to work around the compiler error?

    On the other hand, thank you very much for your elaboration on Qt's builtin RTTI mechanism, It really help me on my work in future!


  • Moderators

    Just tested it in GCC 4.9.2 here https://ideone.com/k2LGST and in VS2015 Preview here: http://webcompiler.cloudapp.net/.
    Both compiled and run without errors.

    Variadic templates are part of C++11. Maybe you forgot to enable it ( CONFIG += c++11 in .pro file )?



  • @Chris-Kawa
    Hi, Mr. Chris-Kawa.
    Yes, I had enabled the c++11 by adding QMAKE_CXXFLAGS += -std=c++11 into my .pro file, so this isn't the real problem.

    Finally my code has passed the compiling, by adding ## as the prefix of macro __VA_ARGS__. I think there are multiple errors in my first version of code that bothered me and covered the real problem :

    1. In struct RuntimeClass, I took a return type to the member pointer of function which is different with the function I filled to that member;
    2. In class TestClass , I didn't declare the member function TestFunc as static. This cause the member function has a different type with RuntimeClass::m_pfnFunction, and make compiler report error on filling it into instance of RuntimeClass
    3. In the macros DECL_RUNTIMECLASS and IMPL_RUNTIMECLASS, I didn't prefix macro __VA_ARGS__ with ##. This make a redundant comma be left in template parameter list after macro expansion, which will cause the compiling error of template argument 2 is invalid.

    The source code you corrected notified me the above two problems. And with your selfless help I finally sovled the compiling error in my design! Thank you very much !


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.