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?
class Object1
{
int A_;
string B_;
long C_;
REFLECTED_OBJECT(A_);
REFLECTED_OBJECT(B_);
REFLECTED_OBJECT(C_);
}
class Object2
{
int A_;
string B_;
REFLECTED_OBJECT(A_);
REFLECTED_OBJECT(B_);
}
class Object3
{
int A_;
long C_;
REFLECTED_OBJECT(A_);
REFLECTED_OBJECT(C_);
}
Fig.1 Description of classes
main()
{
Object1 object1;
Object2 object2;
Object3 object3;
object1.A = 12;
object1.B = "Welcome to C++";
object1.C = 444;
object1.Print();
object2.Print();
object3.Print();
object2.B = "Welcome to reflection!";
object1.Print();
object2.Print();
object3.Print();
object3.B = 123456;
object3.A = 77;
object1.Print();
object2.Print();
object3.Print();
}
Fig. 2 The program
object1={12,"Welcome to C++",444}
object2={12,"Welcome to C++"}
object3={12,444}
object1={12,"Welcome to reflection",444}
object2={12,"Welcome to reflection"}
object3={12,444}
object1={77,"Welcome to reflection",123456}
object2={77,"Welcome to reflection"}
object3={77,123456}
Fig. 3 The result
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:
typedef Type<int> Int;
typedef Type<long> Long;
typedef Type<bool> Bool;
typedef Type<string> String;
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:
#include "CheckReflection.h"
void main()
{
History history
;
Object1 object1;
Object2 object2;
Object3 object3;
object1.Number () = "12";
object1.Message () = "Welcome to C++";
object1.Value () = 444;
object1.Print();
object2.Print();
object3.Print();
object2.Message () = "Welcome to reflection!";
object1.Print();
object2.Print();
object3.Print();
object3.Value () = "123456";
object3.Number () = 77;
object1.Print();
object2.Print();
object3.Print();
history.Print();
}
Just run the program, and make sure that the result is the one described in the Introduction:
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}
History
- February, 2009: Version 1.0.
- Environment: Visual Studio .NET 2008.