[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)
withDECL_RUNTIMECLASS(int, TestClass, void)
,and replacingIMPL_RUNTIMECLASS(int, TestClass, TestClass::TestFunc)
withIMPL_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!
-
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 inIMPL_RUNTIMECLASS
doesn't seem to be valid syntax for anything in c++. -
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 inIMPL_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 macroIMPL_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:
- 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.
- 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!
-
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.
-
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 memberclass_TestClass
which type isRuntimeClass<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
andIMPL_RUNTIMECLASS
. My purpose is to specify a variadic parameter list in template parameter, and it can be used to specialize the structRuntimeClass
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!
-
Ok, with the static member the only problem I found is that your
m_pfnFunction
has a return typeT*
, while you pass it a pointer to function returning an int. So changing the signature toT (*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 withint 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)
. -
Ok, with the static member the only problem I found is that your
m_pfnFunction
has a return typeT*
, while you pass it a pointer to function returning an int. So changing the signature toT (*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 withint 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!
-
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 )? -
Ok, with the static member the only problem I found is that your
m_pfnFunction
has a return typeT*
, while you pass it a pointer to function returning an int. So changing the signature toT (*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 withint 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
Hi, Mr. Chris-Kawa.
Yes, I had enabled thec++11
by addingQMAKE_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 :- 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; - In class
TestClass
, I didn't declare the member functionTestFunc
asstatic
. This cause the member function has a different type withRuntimeClass::m_pfnFunction
, and make compiler report error on filling it into instance ofRuntimeClass
- In the macros
DECL_RUNTIMECLASS
andIMPL_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 oftemplate 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 !
- In struct