Introduction

Applications which have a mechanism for objects binding are well known, quite popular, and also widely used. Spreadsheets are an example. A variable tied to a particular row and column in a spreadsheet may depend on one or more other variables contained in the same document. Notice that, throughout the paper, I am not interested in objects binding within a given application. (Methods for either explicit or implicit delegation are well known). The point I wish to address is this: Is it possible to implement the objects binding at the level of classes and also create some "hidden" mechanism for exchange of values for certain attributes of these classes? At the level of code, the following is meant: Given three classes specified in Fig. 1, is there any way to write a program given in Fig. 2 in order to produce the nine lines presented in Fig. 3?

A brief reflection shows that the problem can be solved under the following assumptions:

Objects are samples of classes; object attributes are members of these classes.

Two classes are considered to be related to each other if some (or all) attributes of these classes are identical (such as name and type), and if change of an attribute's values in one of the objects automatically leads to a change in the value of the same attribute in another object.

If a new object appears in a system, its attributes are automatically set to the current values of the similar attributes of the other objects.

Each class must have a container of linked attributes whilst each attribute must keep the following values:

Type of attribute

Name of attribute

Pointer to the attribute's value in a class

Linked classes must have a pointer to a global object that manages a system of exchange values. A manager contains a vector of pointers to linked classes. If a value of one of the attributes of a class changes, a manager should find the objects (instances of classes) which have the same attributes and subsequently change the values of these attributes.

An exchange mechanism is run through the operator of the assignment. Hence, the attribute is also an object and should contain a pointer to the owner class. A system architecture is depicted in Fig.4.

Fig.4 Conceptual description of the system

Implementation

Each class whose attributes are linked must inherit the interface IReflection. This interface contains a vector of linked attributes <object_ptr>objects_ptr, and methods for obtaining/assigning the values of attributes as well as for calling the replication procedure. An abstract class IReflection contains a pointer to the object ExchangeManager, which contains information on all classes supporting the interface IReflection. When we create such a class, the latter automatically adds itself to the manager container; when we delete such a class, it excludes itself from the container. Pay attention to the constructor and the destructor for the class IReflection.

Parameters of an attribute are described by the class object_ptr. This class contains both the name and type of a class member, as well as a pointer to it. The list of parameters is described by the macros REFLECTION_START, REFLECTED_OBJECT, and REFLECTION_FINAL and is formed when a class is initialised within the procedure Init.

The ExchangeManager class is a singleton. It contains a container of pointers to classes of the type IReflection, and contains the methods Initiation and Distribution. The first method lets the class attributes obtain the initial values. The second method allows sending the changed values to other objects.

Previously, it was stated that an attribute is also an object (of a scalar type). Its properties are described in the class Type. The class contains a value of the attribute, a number of constructors, and the assignment operator. At this time, four types of variables -- Int, Long, Bool, and String – are described below:

If necessary, this list can be extended. To this end, we have to add two lines to the files TypesWrapper.h and Reflection.cpp. For example, if someone prefers to use real numbers, it is necessary to add a typedef Type<double>Double to the first file and TransformBase<Double> transformDouble to the second file.

The system is supplemented by several supporting interfaces and classes. They provide a relatively simple way to assign and update the attributes of various types, and to transform the values of attributes to the text. The classes TransformBase and ConversionHelper are derived from the interfaces ITransform and IConversion, respectively. The class TransformHelper is a singleton object type, and contains a container for transformation of data types. This class is used in the methods Update and Exchange from the class IReflection. The class ConversationHelper is used in the function fooConversion, which in turn is used in the class History. An object of this class is located in the main program, and it monitors any changes in the values of the variables. The template Destroyer<SINGLETON> and three additional classes -- Destroyer<ExchangeManager>, Destroyer<TransformHelper>, and Destroyer<ConversationHelper> -- are used to correctly eliminate the objects ExchangeManager*, TransformHelper*, and ConversationHelper* on exit from the application.

Conclusion

The project attached to the article contains all the necessary components to create and successfully run the program described in the Introduction. Description of the classes can be found in the file CheckReflection.h. This description is almost identical to the description presented earlier. A text of the test program is in the file CheckReflection.cpp:

Comments and Discussions

It's a charm indeed. A very strong C++. You get my five.
Some friendly comments:
1. I am not sure I got it right but for me it seems like the last instantiated IReflection object overwrites all simular values in IReflections instantiated before.
Like:
Object1 object1;
Object2 object2;
object1.Number() = "12";
Object3 object3;
THe default constructor will set number_ = 0 in all objects.
Sure, you can use something like Object3 object3("12",...);
but it will do exchange for number_ for all objects again.
2. What about multithreading?
3. Little bit more comments in code would do no harm.

Run application...
1>Object1={12,"Welcome to C++",444}
1>Object2={12,"Welcome to C++"}
1>Object3={12,444}
1>Object1={12,"Welcome to reflection!",444}
1>Object2={12,"Welcome to reflection!"}
1>Object3={12,444}
1>Object1={77,"Welcome to reflection!",123456}
1>Object2={77,"Welcome to reflection!"}
1>Object3={77,123456}
1>History={"12,Welcome to C++,444,Welcome to reflection!,123456,77"}

3) About multithreading I just do not think. But this problem is solved simply, I hope. To the singletons must be added synchronize objects to protect data transform operations.

In addition it exchanges its number__ data member with the number_ memmber of the eobject2 twice, when it does REFLECTED_OBJECT(number_) and again with REFLECTED_OBJECT(value_). Not a big deal I think.