Do you realy write code like this:
"METHOD(public, void, store2nd, (Y y))"
I wonder how if you ever use IntelliSense and how does it work for you...
Anyway, nice implementation for using "reflection" in C++.
Actually, such attempt had already being made. In general, there are 2 ways that I found doing reflection in C++. The first approach is the "ugly" macro syntax you're referring to. Example are the AGM::LibRefelection and Visual Component Framework (VCF)[^].The second approach is by reading and prasing the header to obtain the class definition. An example of it is SEAL C++ Reflection[^] library. The reason I went with the AGM::LibReflection macro implementation is for serveral reason.
(1) Most of the other framework that has reflection included are defined mostly for visual component. You will need to adapt and accept their framework -- including Event dispatching... As for me, I am writing backend server service that does not need them.
(2) The AGM::LibReflection does not require a particular base class that your object must inherit.
(3) I liked the SEAL C++ Reflection, but the library has heavy dependencies on many external library. Althought I have not tried, it might be able to extract just the reflection portion from that SEAL library. The distribution of SEAL wasn't really setup for do-it-yourself compliation.
(4) The number of the object that I need to explore reflection is relatively small. I only care about those class that I need to re-configure at runtime externally.
(5) We do not want to link again ton of external packages. It makes this package relatively "light" and small...
For heavy use of C++ reflection, you probably want to look at package like SEAL C++ to see if you can accept them. You are right that the syntax isn't really user friendly...
I think I may have found a way to avoid the limitation of having to first create an instance.. I'd be curious to hear what you think.
It creates a little singleton, registers the class with a small instantiator function and then the singleton deletes itself ( which I think is ok given the circumstance ).
Its not quite done, I think I can also add a number of base classes using a seperate ADDBASECLASS macro. I'll probably have to make a BEGINCLASSDEF and ENDCLASSDEF so I dont have to make more singletons.
typedef void* NewInstanceFunc( void );
I thought of somethings similiar; but I actually do not like this solution for serveral reasons
(1) It makes assumption that there is a default constructor, it is available and that the constructor is public and callable. And the class is not an abstract base class.
(2) Same assumption about the destructor
(3) The extra code will make template class invalid. Yes, there is a way to solve it; but your current implementation is not yet there.
(4) It requires the developer to put an extra declaration outside the class definition. If this would have been a requirement that the developer can live with, the developer can simply just define a static instance: const ClassName my_class_instance;
and that serve the same purpose as well...
Regarding defining multiple base class using ADDBASECLASS macro... Yes, you can indeed capture the information, but its usage is limited in term of what the reflection library can do. One of the reason that why we insist the requirement of single non-virtual inhertiance is that we want to make sure that for any Derived *p, the following is true: p == static_cast<Base *> (p) . It makes casting to and from Base class simple. Yes, we can still retrofit our reflection library to work in a limited way. For example, if a Method is defined in the base class and you intended to invoke it, the reflection library need to adjust the Derived *. The situation becomes a little complex when you are invoking a virtual function that may or may not being overriden in the Derived class. The usage is limited in the way that automatic casting (whether implicit or down casting) will not work for parameter type. This limitation is due to we have no way of knowing if a particular parameter is a pointer or reference type ahead of time and therefore, it is impossible to implement the templated invoke() methods. Beside, you can re-assign a referrence type parameter anyway. The question is really how often the multiple base classes is really needed. And can you live with the fact that the second base class will have limited behavior compare to first base class. In any case, yes, we can certainly captures that multiple base classes... but is it worth it?
Regarding your proposed MACRO BEGINCLASSDEF and ENDCLASSDEF, yes it will help to group all of the definition together, but it also restrict the programmer from providing an inline defintion of the method inside the class declaration. I am just guessing at this point because I have not seen how you plan to implement that. You have a good point about the need to group all of the reflective constructor/method/field together. This is due to the way that the LibReflective library insert nested empty class element within the class declaration. Because the compiler are require to allocate at least 1 bytes for any structure (even if there is no data member), the class size can become fairly large if you mix reflective declaration and field. I should probably state this as a guideline in the article.
Thanks for your feedback, I am curious how you handle templated classes. I think I will be typedefing anything I use for refelections, bit if there is a better way...
The assumption about a default pramaterless constructor seemed fine to me, as that is a restriction of c# also ( you dont NEED a paramterless contructor but it quite a bit more complicated to not have them ). As for the single inheriting, that is also a restriction of c#, one I really don't like. I figured it wouldn't be that hard to implement multiple inheriting, tho I havent got far enough in to hit any walls. Do you have anything similar to interfaces in c#? It similar to multiple inheritance but you can derive from multiple pure virutal classes.
All I really want to use reflection for is a fancy factory, so I wont be needing the methods, and my property system will be available after a class has been created.
As you already know, the real class registration (the point of time the class is known to the system) is when the object of that class instaniated. And when an object instantiate, the exact type of all of its templated parameter are known, so that is no mistake as to what type of object it is. The other reason when it works is that a templated class with different template parameter are completely 2 seperate class that does not share any global space. Each class has its own set of static data variables -- and static data variable are used extensively as the method of registering the class into LibReflection.
I have absolutely zero knowledge about c#, so this LibReflection library will probably fail to compile in C#. I am even amaze if it actually compile under VC++ due to the heavy MACRO that it rely on. Can someone actually try to compile it on VC++ ? Which version work and which doesn't ??
Both of them failed but the warnings and errors were few (something like 4 or 5).
I tried to check how the errors could be checked but i realized it was too complex for a person who didn't write the code.
One line the MSVC isn't able to understand now is this:
std::string errmsg = findMismatchedType0< 0 > (m_callable, typeid(void), typeid(void), typeid(void));
And this is the error signaled:
reflection.hpp(2016) : error C2677: binary '>' : no global operator defined which takes type 'const class type_info' (or there is no acceptable conversion)
Moreover i wonder why the compiler doesn't complain about the other line below: