pseudoPODs
Smart Class Members That Masquerade as POD Members
1) include the pseudoPOD.h file (www.dlmowery.com/pseudoPOD/pseudoPOD.h).
#include "pseudoPOD.h"
2) create one or more pseudoPODs - for example an integer named int3 that is a member of class fred_c.
ppod(fred_c, int, int3); // (compare to int int3; for a POD.)
where:
ppod() is the macro that creates the code supporting the pseudoPOD, which converts the POD style calls (e.g. i=fred.int3;) to get() & set() calls (e.g. i = fred.get_int3();).
fred_c is the name of the class that contains int3, and
int & int3 are the type and name of the variable.
3) In the constructor() of the class give each pseudoPOD a copy of the "this" pointer to the class, so the pseudoPOD's code can call the get() & set() functions in the class.
int3.ep=this;
4) Write the familiar get() & set() functions, using the form that the ppod() code will call.
int get_int3 () { // handle reads, e.g. i = fred.int3;
return (int3_value);
}
int set_int3(int in){ // handle writes, e.g. fred.int3 = 35;
int3_value = in;
return (int3_value);
}
In either or both of the get() or set() functions include the extra code that is required whenever the pseudoPOD is read or written.
If, for example, the pseudoPOD was the time in milliseconds that have elapsed since Windows was started, then get_int3() would include a call to the Win32 API function GetTickCount() to find out what time it is before returning the int3 value.
Or, if the pseudoPOD was a file date, then set_int3() would include calls to tell the operating system to change the file date whenever the caller sets int3 to a new date.
Coding Tactic: when adding a pseudoPOD member, first go to the enclosing class' constructor and initialize its this pointer (e.g. int1.ep = this;). Only after that add the member and its get_() & set_() functions, because if you fail to initialize the int1.ep pointer the operating system kills the program without explaining why, merely saying the program has encountered a problem and has been killed.
The following program shows an example of a class named fred_c that has 3 integers.
int1 is an ordinary POD integer, for which one uses raw reads and writes, i.e.:
i = fred.int1; and fred.int1 = i;
int2 is an integer that is only read & written using the familiar get_int2() & set_int2() functions, i.e.
i = fred.get_int2(); and fred.get_int2(i);
int3 is a pseudoPOD integer that can be read & written as though it were a POD integer, because the code created by ppod() converts the POD style reads & writes to calls to the get_int3() and set_int3() functions.
The example program performs three simple tests on each of the three integers, and demonstrates that they all produce the same result.
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
// call the code that tests the fred_c class.
void test_fred_c(void);
test_fred_c();
// Pause so user can see text in the console window.
std::cout << "Press enter to close window." << endl;
std::cin.get();
return EXIT_SUCCESS;
}
// include the macros & templates that create pseudoPODs
#include "pseudoPOD.h"
// class fred_c
class fred_c { public:
// class members:
int int1; // int1, an ordinary POD integer.
private:
int int2_value; // int2, read & written only
// through functions
// get_int2() & set_int2().
ppod(fred_c, int, int3); // int3, a pseudoPOD, when
// read & written as though it were
// a POD integer, converts the
// reads & writes to calls to
// functions get_int3() & set_int3().
// get() & set() functions for the integers:
// POD int1 requires no get() & set() functions,
// the compiler automatically generates the code
// that reads & writes it in response to
// statements like
// i = fred.int1; and fred.int1 = 35; .
// int2's familiar get() & set() functions:
public:
int get_int2 () { // handle reads, e.g.
// i=fred.get_int2();
// insert code, e.g. operating system calls,
// to perform the extra actions required when
// the code outside the class reads int2.
return (int2_value);
}
int set_int2(int in) { // handle writes,
// e.g. fred.set_int2(35);
// insert code, e.g. operating system calls, to
// perform the extra actions required when the
// code outside the class writes int2.
int2_value = in;
return (int2_value);
}
// int3's get() & set() functions
// (identical to int2's):
private:
int get_int3 () { // handle reads,
// e.g. i = fred.int3;
// insert code, e.g. operating system calls, to
// perform the extra actions required when the
// code outside the class reads int3.
return (int3_value);
}
int set_int3(int in) { // handle writes,
// e.g. fred.int3 = 35;
// insert code, e.g. operating system calls, to
// perform the extra actions required when the
// code outside the class writes int3.
int3_value = in;
return (int3_value);
}
// class fred_c's constructor:
public:
fred_c() {
int3.ep=this; // initialize int3's pointer
// to the enclosing class fred_c, so
// the code created by the ppod() call
// can call functions
// get_int3() & set_int3().
}
// class fred_c's destructor:
~fred_c() {};
};
// run a couple of comparative tests
// on fred_c's integers:
void test_fred_c (void) {
fred_c fred; // create an instance of
// fred_c named fred.
fred.int1 = 0; // use raw write for POD int1
fred.set_int2(0); // use function call to write int2
fred.int3 = 0; // use what looks like a POD
// write to write int3
// confirm that all three approaches produce
// the same result.
if ((fred.int1 != 0) ||
(fred.get_int2() != 0) ||
(fred.int3 != 0)) {
cout << "ERROR: int1/int2/int3 should be "
"0/0/0 but are "
<< fred.int1 << "/"
<< fred.get_int2() << "/"
<< fred.int3 << endl;
}
++fred.int1;
fred.set_int2(fred.get_int2() + 1);
++fred.int3;
if ((fred.int1 != 1) ||
(fred.get_int2() != 1) ||
(fred.int3 != 1)) {
cout << "ERROR: int1/int2/int3 should be "
"1/1/1 but are "
<< fred.int1 << "/"
<< fred.get_int2() << "/"
<< fred.int3 << endl;
}
fred.int1 += fred.int1 <<= fred.int1 = 3;
fred.set_int2(3);
fred.set_int2(fred.get_int2() << fred.get_int2());
fred.set_int2(fred.get_int2() + fred.get_int2());
fred.int3 += fred.int3 <<= fred.int3 = 3;
if ((fred.int1 != 48) ||
(fred.get_int2() != 48) ||
(fred.int3 != 48)) {
cout << "ERROR: int1/int2/int3 should be "
"48/48/48 but are "
<< fred.int1 << "/"
<< fred.get_int2() << "/"
<< fred.int3 << endl;
}
}
Note that the get() & set() functions for pseudoPOD int3 have the same form as the familiar get() & set() functions for int2.
Chaining support: while the pseudoPOD get() & set() functions return values, the ppod() code actually returns a "this" pointer to pseudoPOD int3, so that this code:
int i = fred.int3 = 38;
would call set_int3() once to write 38 into int3_value, and then call get_int3() to read 38 from int3_value so it could be written into i.