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
#include
#include
#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]