Boost logo

Boost Users :

From: ravioli_at_[hidden]
Date: 2002-05-06 06:37:21


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]


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net