pseudoPODs
Smart Class Members That Masquerade as POD Members
C++ doesn't intentionally support this innovation, so the implementation is somewhat roundabout, which some will find entertaining, and others anathema.
File main.cpp (www.dlmowery.com/pseudoPOD/main.cpp) contains a sample class called fred_c that shows the raw code underlying pseudoPODs, without macros or templates to obscure the code. It has three integers:
int1 is an ordinary POD integer. It has no explicit get() & set() functions, because the compiler implicitly generates the code that performs the read and write functions for PODs.
int2 is an integer read & written using familiar get_int2() & set_int2() functions. Its value is saved in private integer int2_value.
int3 is a pseudoPOD integer. Its get_int3() and set_int3() functions are identical to those used by int2, except that they read & write int3 rather than int2. int3's value is saved in int3_value, same as for int2.
ElapsedTime is a fourth integer, identical to int3, except that it demonstrates that you can add code to a pseudoPOD to make it an active integer. ElapsedTime's get_ElapsedTime() function has some extra code that calls the operating system using GetTickCount() to find out how much time has elapsed since Windows was started. Reading fred.ElapsedTime actively returns the current elapsed time, rather than passively returning the last value written to it. The set_ElapsedTime() function has extra code that writes a message to cout.
Class fred_c's constructor() has 1 extra line of code for each pseudoPOD, which copy the "this" pointer that points to fred_c into the nested classes int3 and ElapsedTime, so they can use it to call the get() & set() functions in fred_c.
Nested classes are classes that are declared within the declaration of an enclosing class, as nested classes int3_nest and ElapsedTime_nest are declared within enclosing class fred_c. The advantage of nesting is that the scope of the nested class is confined to the enclosing class, so some enclosing class other than fred_c can have a nested class of the same name without the two identically named nested classes being confused, because each nested class may be used only in the enclosing class in which it is defined.
int3 uses nested class int3_nest to convert what appear to be reads & writes of PODs into function calls to the familiar get_int3() and set_int3() functions. int3_nest doesn't alter the data in any way, it just converts POD like calls to get() & set() calls. In fact, if you were to make get_int3() & set_int3() public: rather than private:, you could use POD like calls and get() & set() function calls interchangeably, and obtain exactly the same results.
The instance of int3_nest created at the end of the int3_nest class declaration is named int3, so the compiler interprets the statement i = fred.int3; as a call to int3_nest's read function inline operator int(); . At this point we want to perform what could be a simple operation, that of passing the call on to the familiar get_int3() function in the enclosing class.
Unfortunately C++ has a unidirectional class hierarchy, such that an enclosing class can easily call functions in a nested class, but a nested class cannot directly call functions in an enclosing class, not even if the classes are mutual friends, and no matter how many punctuation characters one uses to specify the function in the enclosing class.
Instead, what we have to use is an inefficient kludge of a workaround. The nested class includes a pointer to the enclosing class (fred_c *ep;). When an instance of the enclosing class is created, the constructor() of the enclosing class writes the "this" pointer of the enclosing class into the ep pointer of the nested class. Now the nested class can use the ep pointer to call functions in the enclosing class.
Returning to the case ofi = fred.int3; , the compiler calls int3_nest's read function inline operator int(); , which in turn employs ep->get_int3() to call the familiar get_int3() in the enclosing class. get_int3() returns the value of int3, which int3_nest's read function inline operator int(); returns to the compiler, which in turn copies it into i.
Writing a pseudoPOD works much the same. For example, for fred.int3 = 35; , the compiler calls int3_nest's inline int3_nest & operator=(int in); function, which in turn calls the familiar set_int3() function in the enclosing class, which writes the new value into int3_value, then returns the new value, which int3_nest ignores, returning the "this" pointer to int3 instead.
Returning the "this" pointer, rather than the value of int3, more precisely supports chained operations by getting the compiler to call get_int3() every time the chained statement needs to read the value of int3. In the statement
i = fred.int3 = 38;
the compiler will, through the translating class int3_nest, call set_int3() once to write the value of 38 into int3, and then get_int3() once to read the value of int3 so it can write it into i. If int3_nest's inline int3_nest & operator=(int in); function returned the value of int3 rather than a "this" pointer to int3, then the compiler would write the returned value into i without calling get_int3(). The value written into i might still be correct, but get_int3() would not get a chance to do whatever it's supposed to do each time int3 is read, such as count the number of times int3 is read, or check the value read for validity, or even modify the returned value of int3.
The other assignment statements (operator++, operator--, operator+=, etc) handle the 12 other assignment operators (++, --, +=, -=, *=, /=, %=, &=, ^=, |=, <<=, >>=), by calling the familiar get_int3() function in the enclosing class to get the current value of int3, then modifying it as specified by the operator (for ++ add 1, etc.), and calling set_int3() to write the new value.
Functions are not required for unary + or - because the compiler automatically translates those to calls to the read function inline operator int() .
The function test_fred_c(void), in main.cpp following class fred_c, performs some simple comparative tests on fred.int1, fred.int2, and fred.int3, and confirms that calling pseudoPOD int3 as though it were a POD integer produces the same results as calling POD int1. In addition, it reads fred.ElapsedTime repeatedly to show that the elapsed time changes, and writes it once to show that it writes a message to the console.
The remainder of file main.cpp has class fred2_c, which is similar to class fred_c, but has a far wider variety of pseudoPODs, and builds them with both macros and templates. Function test_fred2_c() runs more extensive tests on fred2_c's pseudoPODs.