generic NULL / INVALID extension for class

F

Frank Bergemann

Hi,

i remember to have read about how to extend existing classes (in a
generic way), so they provide some NULL or INVALID "instance" (e.g).
This then can be used e.g. by factories, which per definition return
an instance of kind of object they are dedicated for.
But in some cases, factory might not be able to create a correct
object.
For such there was a way to return dedicated instances, which indicate
conditions like NUL/nil/INVALID.
So they can be compared with result of factory.
E.g. as Class::Invalid or Class::NULL:

All this in order to do without exceptions to indicate back NULL/nil/
INVALID for this.
And to do without boost::eek:ptional or boost::variant as another option.

Can s.o. gimme a reference (book, web) to solution for that?

- many thanks!

rgds

Frank
 
R

red floyd

Frank said:
Hi,

i remember to have read about how to extend existing classes (in a
generic way), so they provide some NULL or INVALID "instance" (e.g).
This then can be used e.g. by factories, which per definition return
an instance of kind of object they are dedicated for.
But in some cases, factory might not be able to create a correct
object.
For such there was a way to return dedicated instances, which indicate
conditions like NUL/nil/INVALID.
So they can be compared with result of factory.
E.g. as Class::Invalid or Class::NULL:

All this in order to do without exceptions to indicate back NULL/nil/
INVALID for this.
And to do without boost::eek:ptional or boost::variant as another option.

Can s.o. gimme a reference (book, web) to solution for that?

Pointers already do this.

Have your factory return a (smart) pointer to the new instance. Return
NULL if it's invalid.

You should use a smart pointer, playing with raw pointers leads to
ownership questions.
 
K

Kai-Uwe Bux

Frank said:
Hi,

i remember to have read about how to extend existing classes (in a
generic way), so they provide some NULL or INVALID "instance" (e.g).
This then can be used e.g. by factories, which per definition return
an instance of kind of object they are dedicated for.
But in some cases, factory might not be able to create a correct
object.
For such there was a way to return dedicated instances, which indicate
conditions like NUL/nil/INVALID.
So they can be compared with result of factory.
E.g. as Class::Invalid or Class::NULL:

All this in order to do without exceptions to indicate back NULL/nil/
INVALID for this.
And to do without boost::eek:ptional or boost::variant as another option.

Can s.o. gimme a reference (book, web) to solution for that?

I usually use a "box" container for that. A box<T> can be empty or hold
exactly one element of type T. The empty state represents NULL/nil/INVALID,
but you also want to define std::less< box<T> > so that the empty box is
greater than any non-empty box so that the state also represents infinity.
I found that simple container to be amazingly useful.


Best

Kai-Uwe Bux
 
J

James Kanze

Frank Bergemann wrote:
I usually use a "box" container for that. A box<T> can be
empty or hold exactly one element of type T. The empty state
represents NULL/nil/INVALID, but you also want to define
std::less< box<T> > so that the empty box is greater than any
non-empty box so that the state also represents infinity. I
found that simple container to be amazingly useful.

That sounds somewhat similar to the Fallible idiom, popularized
by Barton and Nackman many years ago. (In my own work, I've
extended it to support a multi-valued error code.) I'm not sure
it's quite the same thing, though---at least in Fallible, the
object had to be default constructible, because there always was
an instance of the object, regardless. (The idea had occured to
me to implement it so that this requirement wasn't present, but
in practice, I've never found it to be a problem, so I haven't
bothered.)
 
K

Kai-Uwe Bux

James said:
That sounds somewhat similar to the Fallible idiom, popularized
by Barton and Nackman many years ago.

If only I knew more, I wouldn't have to reinvent things over and over again.

(In my own work, I've
extended it to support a multi-valued error code.) I'm not sure
it's quite the same thing, though---at least in Fallible, the
object had to be default constructible, because there always was
an instance of the object, regardless. (The idea had occured to
me to implement it so that this requirement wasn't present, but
in practice, I've never found it to be a problem, so I haven't
bothered.)


Well, my code started with this prototype. The real version uses an
allocator (and also implements the comparison operators as free-standing
functions):

template < typename T >
struct box;

template < typename T >
void swap ( box<T> &, box<T> & );

template < typename T >
struct box {

typedef T value_type;
typedef value_type * pointer;
typedef value_type const * const_pointer;
typedef value_type & reference;
typedef value_type const & const_reference;

private:

friend void swap<> ( box &, box & );

pointer ptr;

public:

box ( void )
: ptr ()
{}

box ( value_type const & value )
: ptr ( new value_type ( value ) )
{}

box ( box const & other )
: ptr ( other.empty()
? 0
: new value_type ( other.item() ) )
{}

~box ( void ) {
this->clear();
}

box & operator= ( box const & other ) {
box dummy ( other );
swap( *this, dummy );
return( *this );
}

bool empty ( void ) const {
return ( this->ptr == 0 );
}

bool has_item ( void ) const {
return ( ! this->empty() );
}

bool has_item ( value_type const & t ) const {
return ( ! this->empty() && ( this->item() == t ) );
}

void clear ( void ) {
delete ( ptr );
ptr = 0;
}

void insert ( value_type const & value ) {
box dummy ( value );
swap( *this, dummy );
}

const_reference item ( void ) const {
assert( ! this->empty() );
return( *ptr );
}

reference item ( void ) {
assert( ! this->empty() );
return( *ptr );
}


bool operator== ( box const & other ) const {
if ( this->empty() ) {
return( other.empty() );
}
if ( other.empty() ) {
return( false );
}
return( this->item() == other.item() );
}

bool operator!= ( box const & other ) const {
return( ! this->operator==( other ) );
}

bool operator< ( box const & other ) const {
if ( this->empty() < other.empty() ) {
return( true );
}
if ( this->empty() ) {
return( false );
}
return( this->item() < other.item() );
}

bool operator<= ( box const & other ) const {
return( ! ( other < *this ) );
}

bool operator> ( box const & other ) const {
return( other < *this );
}

bool operator>= ( box const & other ) const {
return( other <= *this );
}

}; // struct box<>

template < typename T >
void swap ( box<T> & a, box<T> & b ) {
std::swap( a.ptr, b.ptr );
}

template < typename T >
std::eek:stream & operator<< ( std::eek:stream & ostr, box< T > const & b ) {
if ( b.empty() ) {
ostr << '#';
} else {
ostr << "[ " << b.item() << " ]";
}
return( ostr );
}

template < typename T >
std::istream & operator>> ( std::istream & istr, box< T > & b ) {
char chr;
istr >> chr;
if ( chr == '#' ) {
b.clear();
return( istr );
}
if ( chr == '[' ) {
typename box<T>::value_type val;
if ( istr >> val ) {
istr >> chr;
if ( chr == ']' ) {
b.insert( val );
return( istr );
}
}
}
istr.setstate( std::ios_base::failbit );
return( istr );
}

The dynamic allocation does not require T to be default constructible. I
mostly use it to add a formal infinite value to a type that supports
ordering (I rarely have use for error codes).

Alternatively, one can implement box<T> based on pair<bool,T>.


Best

Kai-Uwe Bux
 
G

gpderetta

That sounds somewhat similar to the Fallible idiom, popularized
by Barton and Nackman many years ago. (In my own work, I've
extended it to support a multi-valued error code.) I'm not sure
it's quite the same thing, though---at least in Fallible, the
object had to be default constructible, because there always was
an instance of the object, regardless. (The idea had occured to
me to implement it so that this requirement wasn't present, but
in practice, I've never found it to be a problem, so I haven't
bothered.)

Boost.optional does exactly that and I found that behavior to be
extremely valuable, for example if you can't initialize a non default
constructible member in a constructor initializer list (and you do not
want to add a zombie state to the object itself), you can just wrap it
in a boost optional (this shouldn't really be necessayr with well
designed objects, but real world is though :( ).

Also boost optional doesn't even require asignability (useful for
boost lambdas which are not asignable), nor even copy
constructibility, if you use the inplace-constructor functionality.
 
G

gpderetta

That sounds somewhat similar to the Fallible idiom, popularized
by Barton and Nackman many years ago.

If only I knew more, I wouldn't have to reinvent things over and over again.
(In my own work, I've
extended it to support a multi-valued error code.) I'm not sure
it's quite the same thing, though---at least in Fallible, the
object had to be default constructible, because there always was
an instance of the object, regardless. (The idea had occured to
me to implement it so that this requirement wasn't present, but
in practice, I've never found it to be a problem, so I haven't
bothered.)

Well, my code started with this prototype. The real version uses an
allocator (and also implements the comparison operators as free-standing
functions):
[...]
The dynamic allocation does not require T to be default constructible. I
mostly use it to add a formal infinite value to a type that supports
ordering (I rarely have use for error codes).

You can use tr1::aligned_storage and inplace new to do away with
dynamic allocation and still not require default constructibility
(that's what boost optional does at least).
 
J

James Kanze

James Kanze wrote:

[...]
If only I knew more, I wouldn't have to reinvent things over
and over again.

The problem is, you'll never know enough. Too many things have
already been invented.
Well, my code started with this prototype.

[code deleted... maintains a pointer to the object, which
may be null, ctor dynamically allocates the object, uses
deep copy]
The dynamic allocation does not require T to be default
constructible.

My original code just copied Barton and Nackman. The Barton and
Nackman version was really designed with built-in types in mind
(int, double, etc.), and to be used in contexts where
performance could be an issue, so dynamic allocation wasn't
used. As I said, once I had it working, I thought about
extending it so that the default constructor wasn't necessary,
but in practice, I only use it for value types, and I make it a
practice of providing a default constructor for value types, to
support the occasional case where the user might want to do
something like:

Type t ;
if ( c )
t = a ;
else
t = b ;

(with, of course, more complicated processing in each branch).
So I ended up viewing not using the default constructor as more
of an optimization that I didn't need. (My idea was to use a
local buffer of "unsigned char[ sizeof( T ) ]" in the object,
with suitable tricks to ensure alignment. You then either copy
construct, or don't construct.)
I mostly use it to add a formal infinite value to a type that
supports ordering (I rarely have use for error codes).

Interesting. When I added comparison (so that you could use it
in std::set, for example), I ended up putting invalid objects at
the lowest position. More by chance than anything else;
treating them as infinity does seem more logical.

As for the error codes, I needed them once or twice, and they
didn't seem to difficult to add, so I just went ahead and did
it.

It turned out slightly more difficult that expected, because one
frequent use is Fallible<std::string>, initialized with a string
literal (i.e. char const*)---to support error codes, of course,
you need a constructor which takes the status type, and if the
choice of constructors is Fallible( bool ) and Fallible(
std::string const& ), guess which one overload resolution
chooses for Fallible( "xxx" ). So the I created a special type
for the default (which doesn't support implicit conversion from
a char const*).
Alternatively, one can implement box<T> based on pair<bool,T>.

That's basically what mine is (except, of course, it was
implemented long before std::pair became available, and the
"bool" can be replaced with another type, by means of a second,
traits argument to the template).

Like the one in Barton and Nackman, it supports implicit
conversion to the target type (with an assertion failure if !
isValid()) and a function elseDefaultTo(...). In practice, I
don't really use the first (and sort of wish I hadn't
implemented it to begin with), but the second is used almost as
frequently as value().

If you're interested in having a look, the code is available at
my site: http://kanze.james.neuf.fr/code-en.html
(http://kanze.james.neuf.fr/doc/en/Basic/html/index.html for the
documentation---look for the file Fallible.hh, rather than the
class, since that's where there's also a link to the defaults
traits class, and
http://kanze.james.neuf.fr/code/Util/Basic/Fallible/index.html
for the code (with all of the actual implementation in the
headers in the subdirectory gb).
 
F

Frank Bergemann

about boost::eek:ptional: i would like to have the opportunity to add X
"special" states to my object. boost::eek:ptional would gimme #1 only.
boot::variant would offer more, but it would introduce a "not-that-
nice" API for the user.

I am currently playing with this as a starter:
----------------------------------------------
#include <iostream>

using namespace std;

template <class C>
class _None
{
private:
bool _isNone;
public:
_None() : _isNone(true) { /* void */ } // default
~_None() { /* void */ }
bool isNone() const { return _isNone; }
};

template <class C>
class None
{
private:
bool _isNone;
public:
None() : _isNone(false) { /* void */ } // default
~None() { /* void */ }
bool isNone() const { return _isNone; }
typedef _None<C> none_t;
static none_t none;
};

class X : public None<X>
{
private:
int _a;
public:
X(int a) : _a(a) { /* void */ }
~X() { /* void */ }
int operator() () { return _a;}
} ;

template <class X>
_None<X> None<X>::none;

int main()
{
X a(10);
X b(5);
cout << "a holds '" << a() << "'" << endl;
cout << "b holds '" << b() << "'" << endl;
cout << endl;

cout << "b.isNone is '" << b.isNone() << "'" << endl;

cout << "None<X>::none.isNone() is '" <<
None<X>::none.isNone() << "'" << endl;

X::none_t dummy;

cout << "dummy.isNone() is '" << dummy.isNone() << "'" <<
endl;

return 0;
}

--------------------------------------------------------

The interface is incomplete yet - especially i need to add support for
comparison.
(hope that will be possible).
And i'll try to "merge" clas _None<> with class None<>.
Drawback of this approach: it adds some bool element to the class
(extends size).
But maybe it is possible to use inheritance the other way around
instead.
I.e. inherit a None<X> from the class X (CRTP?) to not extend the size
of class X(?)
But i am not sure, if that will work. I "guess" using inheritance to
derive a special None<X> from X will only work with pointers then
(e.g. for the factory to return either X or None<X>). And it will
force me to use a more complex syntax that plays with inheritance for
determing, if some returned X is "just X" or None<X>.

rgds

Frank
 
F

Frank Bergemann

Here's the result, which "works for me":

-------------------------------------
#ifndef VALUEEXTENSION_H_
#define VALUEEXTENSION_H_
/**
* This module supports adding value conditions like
* "unknown", "null", "none", "invalid", etc to class X.
* Purpose is to ease handling such conditions for X object factories
and their users
* - with minimum impact to the classes X themselves.
* The only requirements for class X are:
* 1) it has to public inherit from the related base class(es) of this
module
* to add the new value condition
* 2) it has to provide some static mkDummy() factory for dummy
objects,
* which this module can model to X::Unknown(), X::Invalid(),
X::Null(), etc images.
* The mkDummy() result does NOT necessarily need to be a complete
instance of X.
* I.e. efforts for its construction can be minimized.
* But it MUST support compliant operator==/operator!= for X.
*
* Notes
* Only mutual excluding additional values conditions are
supported.
* but NOT combinatory value conditions.
*
* TODO:
* I yet didn't find a way to drop the requirement for a mkDummy()
method for X.
* The problem is, that e.g. _None<X> needs to be compatible with
type X
* - i.e. it needs to be an instance of X (properly "flavoured").
* But _None<> doesn't "know" about which c'tor are provided by X
* and which parameters they require.
* The only other option, that came up my mind was to require a
DEFAULT c'tor for X always.
* But this could also have side-effects for X API and its users.
* A solution might be to use some existing default c'tor - if it
is already available.
* And require X to add some private default c'tor for _None<X> (as
friend)
* - in case there is yet not default c'tor.
* This is still under investigation.
*/
#if 0 // DISABLED for "comment code"

/**
* example, but a macro (see below) is used to support arbitrary value
extension
*/
template <class C>
class _None
{
private:
bool _isNone;
public:
_None() : _isNone(false) { /* void */ }
~_None() { /* void */ }
bool isNone() const { return _isNone; }

bool operator==(_None const& rhs)
{
return _isNone == rhs._isNone;
}

bool operator!=(_None const& rhs)
{
return !operator==(rhs);
}

static C& None()
{
static C dummy = C::mkDummy();
static bool prime_time(true);
if (prime_time) {
prime_time = false;
dummy._isNone = true;
}
return dummy;
}
};

/**
* user class sample
*/
class X : public _None<X>
{
private:
int _a;
public:
X(int a) : _a(a) { /* void */ }
~X() { /* void */ }

static X mkDummy() { return X(4711); } // required for _None<>
et.al. !!!

int operator() () { return _a;}
bool operator==(X const& rhs)
{
// base class evaluation required !!!
if (_None<X>::eek:perator!=(rhs)) {
return false;
}
// own stuff evaluation
if (_a != rhs._a) {
return false;
}
return true;
}
bool operator!=(X const& rhs)
{
return !operator==(rhs);
}
};

#endif // DISABLED for "comment code"

#define _CREATE_VALUE_EXTENDING_BASE_CLASS(STATE_NAME) \
template <class X> \
class _##STATE_NAME \
{ \
private: \
bool _is##STATE_NAME; \
public: \
_##STATE_NAME() : _is##STATE_NAME(false) { /* void */ } \
~_##STATE_NAME() { /* void */ } \
bool is##STATE_NAME() const { return _is##STATE_NAME; } \
\
bool operator==(_##STATE_NAME const& rhs) \
{ \
return _is##STATE_NAME == rhs._is##STATE_NAME; \
} \
\
bool operator!=(_##STATE_NAME const& rhs) \
{ \
return !operator==(rhs); \
} \
\
static X& STATE_NAME () \
{ \
static X dummy = X::mkDummy(); \
static bool prime_time(true); \
if (prime_time) { \
prime_time = false; \
dummy._is##STATE_NAME = true; \
} \
return dummy; \
} \
};

/**
* some "standard" extensions
*/

_CREATE_VALUE_EXTENDING_BASE_CLASS(None)
_CREATE_VALUE_EXTENDING_BASE_CLASS(Invalid)
_CREATE_VALUE_EXTENDING_BASE_CLASS(Null)
-------------------------------------------

and here a little test program

-----------------------------------
#include <iostream>

using namespace std;

#include "value_extension.h"


class X : public _None<X>
{
private:
int _a;
public:
X(int a) : _a(a) { /* void */ }
~X() { /* void */ }

static X mkDummy() { return X(4711); } // required for None<> et.al.

int operator() () { return _a;}
bool operator==(X const& rhs)
{
// base class evaluation
if (_None<X>::eek:perator!=(rhs)) {
return false;
}
// own stuff evaluation
if (_a != rhs._a) {
return false;
}
return true;
}
bool operator!=(X const& rhs)
{
return !operator==(rhs);
}
};

X func1()
{
return X(4711);
}

X func2()
{
return X::None();
}

int main()
{
X a(10);
X b(5);
cout << "a holds '" << a() << "'" << endl;
cout << "b holds '" << b() << "'" << endl;
cout << endl;

cout << "b.isNone is '" << b.isNone() << "'" << endl;

cout << "X::None().isNone() is '" << X::None().isNone() << "'" <<
endl;

X get1 = func1();
cout << "get1.isNone() is '" << get1.isNone() << "'" << endl;

X get2 = func2();
cout << "get2.isNone() is '" << get2.isNone() << "'" << endl;

if (func1() == X::None()) {
cout << "func1() returns X::None()" << endl;
}
else {
cout << "func1() returns NOT X::None()" << endl;
}

if (func2() == X::None()) {
cout << "func2() returns X::None()" << endl;
}
else {
cout << "func2() returns NOT X::None()" << endl;
}

if (func2() != X::None()) {
cout << "func2() returns != X::None()" << endl;
}
else {
cout << "func2() returns NOT != X::None()" << endl;
}

return 0;
}
--------------------------------

I would like to get rid off using macro here.
And the flaw listed as TODO.
But yet don't know how(?)

rgds

Frank
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,176
Messages
2,570,947
Members
47,501
Latest member
Ledmyplace

Latest Threads

Top