Programs need data. Programs provide only as good a work result as the input data were complete and valid. For some programs, the input data is regular files or information obtained from the network (for example, your browser). Other programs operate on source codes. These second "meta-programs" also need data. The better they are, the better the result will be.
What kind of data do we “feed” to such programs? Well, in C ++, more important than “what?” Is the question “when?” (Remember Morpheus?). A C ++ program is just a sequence of bits that the compiler is trying to read and understand. And in the process of this “understanding” the compiler converts the C ++ code into machine instructions and (most interestingly!) Executes some of the code of your program. Yes, we are talking about meta-programming at compile time.
Returning to the question "what?". We want to have access to all entities that can theoretically only be available at the compilation stage: types, members of classes, functions, arguments, namespaces, line numbers of code, file names - and all this would be nice to have in "pure form", without any strange preprocessing hacks or using third-party utilities. In addition, it would be good to get less obvious things: information about the convertibility of some types into others, relations of inheritance and aggregation, friendly classes and functions, etc.
')
The C ++ compiler already has all this information! But, unfortunately, not in a form accessible to meta-programming. It turns out such a strange situation - we can execute some code at the compilation stage - but we don’t have most of the data. Well, here it would be logical to ask the question "How can we get this data?".
The idea lies on the surface and many, for certain, successfully applied it earlier: to make the above data accessible to meta-programs without requiring any information from the compiler.
Let's see who we all have. The first is the compiler. The second, the meter program, and the last (in order, but not least) is the programmer. Well, since the machines have not yet captured the world, and most programs today are written by all the same people.
These compilation steps must be visible and understandable to all three of them. Today, C ++ programmers, regardless of pain, write code in a form that is more convenient for the compiler and the metaprogram. The main examples are the
traits idiom, the
type_traits library
, and sometimes special code generators that parse C ++ code and “take out” class relationships. For example, the script gen-meta.py from
LEESA generates type lists (Boost MPL vectors) for classes that contain other classes.
When the code is not autogenerated, we make it pleasant for the programmer using macros. Many people do not like macros due to the fact that they "hide" the code and data, which are essentially behind them, but this is not about that now. There are many examples of really powerful macros:
Boost SIMD , Boost MPL (when there were no variadic templates yet),
smart enumerations , and much more. When macros are used intelligently, they really look like magic. I happened to feel this while working on the RefleX library.
RefleX is a type modeling tool using reflection at compile time for
DDS Topics. It is open, but you need
RTI Connext DDS to try RefleX with your hands. In the course of its work, the native C \ C ++ type is converted into its representation, called TypeObject, and your data is marshaled into what is called a DynamicData object. Note that both TypeObject and DynamicData are serializable, since in real code it is often necessary to save data to disk or transfer it over the network.
Here is an example:
The macro RTI_ADAPT_STRUCT expands into approximately
120 lines of C ++ code containing ShapeType information that can be used at the compilation stage. It is based on the macro
BOOST_FUSION_ADAPT_STRUCT . This macro opens the interior of a specific type for the RefleX library. RefleX meta-programs use this data for their own purposes. Information includes class member types, their names, enumerations, and other information.
I wrote the last two open-source libraries using this mechanism: in one, “data” was generated by a Python script, in the other — by macros like the ones described above. These libraries were very powerful.
A natural step in the evolution of this paradigm should be its support at the level of the standard language. If something gives such significant advantages - the language and the compiler should take the trouble to provide the necessary functionality.
All this leads us to the main topic of this article:
reflection at the compilation stage . We need it. This is a necessary and correct step in the evolution of the C ++ language. When it is available, all these macros and code generators will not be needed. We just have all the necessary information, where it is needed and when it is needed. All this will work faster, look simpler and in general just be a stunning feature.
Standard C ++ 1y promises to be very interesting, we will follow the news.