As it was explained in the section called “Metamodel and object-relational mapping.”, the POLiTe and POLiTe 2 libraries depend on pre-processor macro calls included in class definitions to obtain metamodel description.
The IOPC library tries to avoid disadvantages of this approach mentioned in the section called “Conclusion”. IOPC makes the process of metamodel descriptions retrieval more user-friendly and transparent by utilizing a modified version of the OpenC++ source-to-source translator. For more on this topic, see the section called “IOPC SP”. The main point is that OpenC++ is incomplete and is not developed any more. This leaves users in a bad situation as they cannot use STL data types (because the OpenC++ does not handle templates very well) or even they cannot be sure if their code was processed correctly.
However, the idea to employ an external tool to analyze and process the source code is convenient, so IOPC 2 uses different tool to solve this situation. GCCXML[20] was chosen to replace OpenC++.
GCCXML is a XML output extension to GCC. GCCXML uses GCC to parse the input source code and then dumps all declarations into a XML file, which can be easily parsed by other programs. The downside is that it processes only declarations. Therefore it cannot be used as a source-to-source translator. Yet GCCXML represents ideal out-of-the-box solution for discussed scenario. GCCXML can handle the C++ language in its entirety and its usage does not involve the risk of translated source code misinterpretation.
IOPC 2 uses the generated XML file to gain knowledge of the structure of classes in the processed source code. This information is then made accessible via reflection interface provided with the library. User applications can then query the class metamodel and use it in a way similar to applications written in reflective languages. The reflection mechanism used in IOPC 2 is outlined in Figure 4.1, “Reflection using the GCCXML”.
Example 4.1, “Basic persistent class definition in IOPC 2” illustrates persistent class definition in IOPC 2. No special macros are needed. However, it presents only a basic, least convenient variant as more features are provided by using enhanced data types (see the section called “Enhanced data types”) and by specifying additional class metadata (see the section called “Class metadata”).
Example 4.1. Basic persistent class definition in IOPC 2
class Person : public iopc::OidObject { public: short int age; std::string name; }; class Employee : public Person { public: int salary; }; class Student : public Person { public: std::string studcardid; DbPtr<Employee> supervisor; }; class PhdStudent : public Student { public: short int scholarship; };
The process performs the following steps:
First, the source file is processed by the GCCXML. GCCXML dumps all declarations from the source file into an easy to parse XML file. An excerpt from the XML file containing elements and attributes related to the processed
Person
class is displayed in Example 4.2, “GCCXML output”The XML file is then processed by the IOPC SP[21] utility (a part of the IOPC 2 project). IOPC SP produces compilable C++ file containing data structures that describe classes declared in the source file and their attributes (type descriptions). This file, respectively its compiled version, is called a type catalogue. For translated
Person
class see Example 4.3, “IOPC SP output”In the last step, the original source files and created type catalogues are compiled and linked together into a final application.
Example 4.2. GCCXML output
<!-- Class Person IOPC SP searches for members which are of Field type. (Other members may be methods, constructors, operators and other --> <Class id="_1319" name="Person" members="_2960 _2961 _2962 _2963 _2964 _2965 " bases="_2649 "> <Base type="_2649" access="public" virtual="0"/> </Class> <!-- fields related to the Person class --> <Field id="_2960" name="age" type="_195" access="public" /> <Field id="_2961" name="name" type="_1608" access="public"/> <!-- field data types --> <FundamentalType id="_195" name="short int"/> <Typedef id="_1608" name="string" type="_1564"/>
Example 4.3. IOPC SP output
// Header file .ih META_BLOCK1(::Person) META_PARENT1(iopc::OidObject) META_ATTR1(age, short int, Attribute::VISIBILITY_PUBLIC) META_ATTR1(name, std::string, Attribute::VISIBILITY_PUBLIC) META_BLOCK2(::Person) META_ATTR2(::Person, age) META_ATTR2(::Person, name) META_BLOCK3(::Person, Person, ) META_BLOCK4 // Source file .ic META_REGISTER_TYPE(::Person)
The process is flexible and customisable. Developers can integrate it with their favourite IDEs or build systems. IOPC 2 project contains an example of seamless IDE integration, see the section called “iopcsp”. This way the build process is almost transparent and users do not need to take any additional steps to make the reflection work.
Information stored in the type catalogues are accessible via reflection interface provided by IOPC 2. It allows developers to query the class metamodel and to execute reflective operations upon it. IOPC 2 library supports the following:
Looking up a
Type
object by class name. TheType
class (and its descendants) has similar purpose to prototypes from the POLiTe library or toIopcClassObjectImpl
from the IOPC library. The difference is that it does not perform any database mapping, it just provides access to the metamodel.Obtaining a list of classes.
Traversing a class hierarchy.
Obtaining list of attributes, obtaining a concrete attribute by looking it up by its name, listing inherited attributes.
Querying or setting attribute values at run-time.
Creating object instances.
Automatic dirty status tracking on an attribute or object level.