Hi All, Is there any need in a template wrapping an object of any type, and giving it an extra NULL value ? This is sometimes necessary : - When working with database if a column can be NULL. - When a value may be unspecified. The idea is to wrap an object in a class that will behave exactly like this object, except that it will have some extra properties : - The is_null() and to_null(bool) operators are added. - Serialization (Operators >> and <<) take into account the "NULL" string. There are three basic usage : - The 'null' flag can be stored in the object (Default behaviour). - The 'null' flag can be a special value : See specialization for pointers (Use of NULL special value), doubles and float (Using NotANumber NaN special value), - The 'null' flag and/or the value itself are accessed by the template, giving it a member function as template parameter. Please find a test program; The library itself - a single include file - is written and runs on GnuC and VC++ 6. Thanks. RC /***************************************************************************** ** null_tst.cpp *****************************************************************************/ #include <stddef.h> #include <stdlib.h> #include <iostream.h> #include <string> #include "null.h" #include <bitset> #include <typeinfo> /***************************************************************************** ** tst0 ** ** Simple test of the template nullable_t ** *****************************************************************************/ static void tst0( int theArgC, const char ** theArgV ) { nullable_t< double > myD ; // Behaves like a double. myD = 3.14159 ; // Twice the value. double myValueD = myD.value() + myD ; // Simply print the value exactly like a double. cout << "Pi=" << myD << endl ; myD.to_null() ; assert( myD.is_null() ); // Now, it prints the string "NULL" cout << "Pi=" << myD << endl ; // myPtr is a char * variable, but the 'null' flag is stored with // a NULL pointer, thus saving space. nullable_t< char * > myPtr ; myPtr = "Hello world" ; // Simply prints the string cout << "Sz=" << sizeof(myPtr) << " Str=" << myPtr << endl ; myPtr = NULL ; bool myIsNull = myPtr.is_null(); assert( myIsNull ); // Prints the string "NULL" cout << "Str=" << myPtr << endl ; // As well myPtr.to_null() ; cout << "Str=" << myPtr << endl ; } // tst0 /***************************************************************************** ** tst1 ** ** Same test, but the value and the 'null' flag are stored elsewhere : And the storage place ** is given to the template by giving it a functor. Thus, the flags can be stored elsewhere than in ** the object itself, for saving space, for example. ** ** We must have the same semantic as a nullable object, but the bonus is that the flag does not ** need to be stored in the object itself. *****************************************************************************/ // This class, which does not take any memory, allows to store a value using a member function // given as template parameter. For this test only. template< class T, const T & (*func_get)(void), T & (*func_set)(void) > class return_value_t { public: inline operator const T & (void) const { return (*func_get)(); } inline return_value_t & operator = ( T the_b ) { (*func_set)() = the_b ; return *this ; } }; // return_value_t // For testing purpose only : Object for reading // a static object (whose reference is given as template parameter) // through an accessor. Thus, this accessor can be given as a parameter // the nullable_t. template< class T, const T & the_ref > struct global_get { static const T & value(void) { return the_ref; }; }; // For testing purpose only : Object for writing // a static object (whose reference is given as template parameter) // through an accessor. Thus, this accessor can be given as a parameter // the nullable_t. template< class T, T & the_ref > struct global_set { static T & value(void) { return the_ref; }; }; // This global flag will be set/get by soime nullable_t object, although // these object do not contain it (Thus the use of accessors).. bool stt_bool ; typedef return_value_t< bool, global_get< bool, stt_bool >::value, global_set< bool, stt_bool >::value > glob_flag_t ; // This global double value will be set/get by soime nullable_t object, although // these object do not contain it (Thus the use of accessors).. double stt_double ; typedef return_value_t< double, global_get< double, stt_double >::value, global_set< double, stt_double >::value > glob_doub_t ; // This class takes room only for storing the 'double' value. The flag is // changed with an accessor. typedef nullable_t< double, double, glob_flag_t > glob_double_nullable_01_t ; // This class does not take any room : The double and boolean values are // read/written with accessors (the member functions). typedef nullable_t< double, glob_doub_t, glob_flag_t > glob_double_nullable_11_t ; // This class stores only the boolean flag : The double value is read/written // with a member function given as template parameter. typedef nullable_t< double, glob_doub_t, bool > glob_double_nullable_10_t ; // Values for playing with double variables. #define VALD1 98765.0 #define VALD2 56789.0 #define VALD3 13579.0 #define VALD4 12.0 // Now we can freely manipulate these objects, like double // variables having the extra NULL value. static void tst1( int theArgC, const char ** theArgV ) { glob_double_nullable_01_t myG01 ; myG01 = VALD1 ; assert( myG01 == VALD1 ); assert( myG01.is_null() == false ); myG01 = VALD2 ; assert( myG01 == VALD2 ); myG01.to_null( true ); assert( myG01.is_null() == true ); glob_double_nullable_10_t myG10 ; myG10 = VALD3 ; assert( myG10 == VALD3 ); assert( myG10.is_null() == false ); myG10.to_null( true ); assert( myG10.is_null() == true ); glob_double_nullable_11_t myG11 ; myG11 = VALD4 ; assert( myG11 == VALD4 ); myG11.to_null( true ); assert( myG11.is_null() == true ); } // tst1 /***************************************************************************** ** main *****************************************************************************/ int main( int theArgC, const char ** theArgV ) { tst0( theArgC, theArgV ); tst1( theArgC, theArgV ); return EXIT_SUCCESS ; } /***************************************************************************** ** null_tst.cpp *****************************************************************************/ [Non-text portions of this message have been removed]