Monday, 2 June 2014

Plain Old Data (POD) on C++11

POD would not sound unfamiliar to you, if you are coming from C background. POD is widely used in Module-C programming, simply to mimic the C++ class feature to use POD to contain the data, implement stand-alone/global functions, and passing a pointer of POD into the function to manipulate. Another usage of POD in C is to act as the data container of data flow passing around modules. For example data to pass between DLLs or sockets.

Moving to C++ the first usage in C is replaced by C++ object oriented programming techniques. But the second usage is still valid. POD is still in use as the container of the data flow across the modules and sockets. Because C++ standard maintains the backwards compatibility towards older version of C++ standard  and C standard, anything has been valid on POD in C standard will still have to be valid in C++ standard too.

Two distinct features on POD required for the compatibility between C standard and C++ standard are,
    - Life time of POD: start when the memory allocated and end when memory released/reused
    - Memory footprint: memset/memcpy can work on it and no memory padding at the top

Example 1: features to maintain the compatibility between C standard and C++ standard
//********************************************************************************
struct Foo {
    int x;
    int y;
};

Foo foo;
memset(&foo, 0, sizeof(Foo));
int arr[2];
memcpy(&arr[0], &foo, sizeof(Foo)); // save foo
// ...... work on foo
memcpy(&foo, &arr[0], sizeof(Foo)); // recover foo
Foo* fooPtr = (Foo*) &arr[0]; // ok
assert(&foo == & foo.x) // ok
//********************************************************************************

1. POD in C++03
Anything works in C standard will still work fine on C++ standard. However C++ have extra requirements on POD, which limits the usage of C++ features on POD. For instance in Example 1 C++ compiler will generate default constructor, default copy constructor, default assignment operator and default destructor. How will these new features in C++ (comparing to C) affect the POD in order to maintain the compatibility? Some limits have to be set on the features of C++ in order to keep the compatibility.

According to C++03 standard, POD has to be aggregate (please read my other blog entry for more details,  Aggregate on C++11). And it can not have user-defined copy constructor and destructor, because its life time has nothing to do with the constructor and destructor, shown in Example 1.

So here is the list of requirements on POD in C++03
    - Must be aggregate
    - Any requirements on aggregate will apply to POD in C++03 (See Aggregate on C++11)
    - No user-defined  copy constructor and no user-defined destructor
    - Free to have user-defined assignment operator
    - Member variables must be build-in types (except reference) or any existing POD
    - Life time: the same as C POD
    - Memory footprint: the same as C POD

2. POD in C++11
There are significant changes on POD definition in C++11 comparing to the tight definition of C++03. But the compatibility is maintained backwards to C standard. In order to achieve the compatibility, the following features are still maintained in C++11,
    - I). Statically initialization at compiling time
    - II). Knowing the exact memory footprint at compiling time
    - III). Life time of POD: start when memory allocated and end when memory released/reused
    - IV). Memory footprint:  the same as C POD

C++11 has literately been moving away from the definition of  POD in C++03. In C++03 POD has to be aggregate and then extra limitation applied on it. C++11 has introduced 2  distinct features, which make more senses in programming, to define POD. (More rationals behind this move please refer to [6].) They are trivial classes and stand-layout classes. If classes (refers to class, struct and union in C++ standard) are both trivial and has a standard-layout, then they are qualified as POD.

Trivial classes
This property is to guarantee that classes can be statically initialized, and can be used directly with memset/memcpy to initialize like in C. Here is the definition of trivial classes in C++11
    - No virtual base class
    - No virtual functions
    - A trivial constructor and destructor  (not user-defined, default)
    - No non-trivial copy/move constructor (not user-defined)
    - No non-trivial copy/move assignment operator (not used-defined)
    - Can have as many as public/protected/private static variables and functions
    - Can have as many as public/protected/private non-static variables and functions
    - Hold all the properties for all its member variables recursively

Standard-layout classes
This property is to guarantee that class have the same memory footprint as C's POD. Requirements in C++11 are,
    - No virtual function
    - No virtual base class
    - All variables have the same accessibility (any of public, protected and private)
    - Can have base classes, but they have to be empty classes. (Otherwise reinterpret_cast and aliasing may not work)
    - Base classes can have static member variables only.
    - Hold all the properties for all its member variables recursively

Combine the requirements from both trivial classes and standard-layout classes.
    - No virtual function
    - No virtual base class
    - Can have base classes that are empty classes with any number of static variables
    - No user-defined creation/copy/move constructor
    - No user-defined copy/move assignment operator
    - No user-defined destructor
    - Can have explicit declaration to use any default constructor assignment operator and destructor ("default" keywords, see C++11 features - Explicitly defaulted/deleted member functions) See the motivating example in [6]
    - All non-static variables must have the same accessibility (public, protected or private) See the motivating example in [6]
    - Non-static variables do not necessarily have to have public accessibility.
    - Can have any public/protected/private static member variables
    - Can have any public/protected/private static/non-static functions
    - No reference type in non-static member variables
    - Hold all the properties in its member variables recursively

Comparing with C++03 there is a bit relaxing in POD, for instance the accessibility of non-static member variables. The main changes is the conceptional changes by introducing two new features, trivial classes and stand-layout classes.

Bibliography:
[1] C++03 Standard
[2] C++11 Standard
[3] http://www.stroustrup.com/C++11FAQ.html
[4] N2640 by Jason Merrill and Daveed Vandevoorde
[5] http://en.wikipedia.org/wiki/C++11
[6] [N2294=07-0154] Beman Dawes: POD's Revisited: Resolving Core Issue 568 (Revision 4)

No comments:

Post a Comment