On 12/28/2010 10:17 AM, Öö Tiib wrote:
[...]
What I'm striving to solve is how to store that second level
information. We can't store Nan in a double saying that -999.9 is NaN,
and we can't say that -1 in an int is a missing value.
So my question remains; is there a nice and uniform way of storing this?
We could use a std:
air for everything with pair.first giving the
signal (NaN, Missing, etc.) and pair.second giving the value.
I could probably come up with something, but I was just wondering if the
community had had similar experience and how it's been solved in
different contexts.
It's certainly not universal, but... This is larger than I like
to post, but since my site is currently unavailable, here's the
code I use. (There are some dependencies in there, but they
should be more or less obvious, and easy to replace.)
Fallible.hh:
============
/
****************************************************************************/
/* File:
Fallible.hh */
/* Author: J.
Kanze */
/* Date:
25/10/1994 */
/* Copyright (c) 1994,2003 James
Kanze */
/*
------------------------------------------------------------------------
*/
/* Modified: 22/05/2003 J.
Kanze */
/* Converted documentation to
Doxygen. */
/* Modified: 26/04/2008 J.
Kanze */
/* Eliminated need for default
constructor. */
/*
------------------------------------------------------------------------
*/
//!@file Fallible.hh
//!@brief
//! A generic class for returning "maybe" values.
//!
//! Here, we've extended the version in Barton and Nackman to
//! support a more generalized error code, by means of a
second
//! traits template parameter. By default, the class works as
//! before (and in fact, we didn't have to modify a single
line of
//! code because of the added facility, although the class is
//! widely used in our code), but the user can add a second,
//! traits template parameter to specify how to use a
different
//! status type.
//
---------------------------------------------------------------------------
#ifndef GB_Fallible_hh_20061203izn6Lk4kky3qvxlFVfxSpKam
#define GB_Fallible_hh_20061203izn6Lk4kky3qvxlFVfxSpKam
#include "Gabi/Global.hh"
#include "Gabi/Util/HashCode.hh"
namespace GabiNS {
namespace Util {
// DefaultFallibleTraits:
// ======================
//
//!@brief
//! The default <tt>Traits</tt> for <tt>Fallible</tt>, giving
the
//! behavior of the <tt>Fallible</tt> in Barton and Nackman.
//
---------------------------------------------------------------------------
class DefaultFallibleTraits
{
public:
// StatusType:
// ===========
//
//! Logically, the status type should be <tt>bool</tt>.
//! However... <tt>Fallible< std::string ></tt>
is
//! probably the most frequent single instantiation of
//! <tt>Fallible</tt>, et it's not rare to want to use it
to
//! return a string constant. Which has the type <tt>char
//! const[]</tt>, which degenerates rapidly into a
<tt>char
//! const*</tt>. Regretfully, for historical reasons,
//! pointers (including the <tt>char const*</tt> here)
convert
//! implicitly into <tt>bool</tt>. Which means that if
//! <tt>StatusType</tt> were simply <tt>bool</tt>, using a
//! "constant string" to initialize a
//! <tt>Fallible< std::string ></tt> would
resolve
//! to the constructor taking a <tt>StatusType</tt> (the
one
//! for invalid values), and not to the one taking a
//! <tt>std::string const&</tt> (for valid values). So we
//! wrap. And without an implicit conversion, since that
//! would still result in an ambiguity.
//!
//! A priori, this makes it more difficult to use the
various
//! functions which take an explicit StatusType. But
given
//! that here, there are only two possible values, and
that
//! for any given function, only one is legal (and that
one is
//! the default value), it's not a problem.
//
-----------------------------------------------------------------------
struct StatusType
{
explicit StatusType( bool value ) : value( value ) {}
bool value ;
} ;
static bool isOk( StatusType status )
{
return status.value ;
}
static StatusType defaultInvalid()
{
return StatusType( false ) ;
}
static StatusType defaultValid()
{
return StatusType( true ) ;
}
} ;
// Fallible :
// ==========
//
//!@brief
//! A generic class for returning "maybe" values.
//!
//! This class is used to return values from functions that
may
//! fail. Normally (supposing no failure), the class converts
//! automatically (but with a "user-defined" conversion) to
the
//! type on which it is instantiated. In the failure case, an
//! attempted conversion causes an assertion failure. There
are
//! also functions for testing for failure.
//!
//! See Barton and Nackman, <i>Scientific and Engineering C++</
i>,
//! Addison-Wesley, 1994, section 6.4.4.
//!
//! Compared to the version described in Barton and Nackman,
this
//! version has been extended to take a second template
parameter,
//! which allows defining a user specific type for the
validation;
//! in particular, this type allows distinguishing between
//! different types of errors, or even different types of
//! non-errors.
//!
//! Also, unlike the implementation described in Barton and
//! Nackman, this implementation does not require a default
//! constructor (except for one function); it is sufficient
that
//! ValueType be CopyConstructible and Assignable.
//!
//! The template is thus defined over two parameters:
//!
//! <dl>
//! <dt><tt>ValueType</tt></dt>
//! <dd>
//! The type of the value when everything is OK. This
type
//! requires a default constructor, a copy constructor and
an
//! assignment operator, all accessible.</dd>
//!
//! <dt><tt>Traits</tt></dt>
//! <dd>
//! This type determines how we decide whether an instance
is
//! a valid value or not. It must contains:
//!
//! <ul>
//! <li>The definition or the declaration (e.g.
//! <tt>typedef</tt>) of a type <tt>StatusType</tt>,
which
//! can be used to evaluate validity. For the
classical
//! Fallible, as described in Barton and Nackman, it
would
//! be <tt>bool</tt>, but it may also be an <tt>enum</
tt>,
//! with values for good and different types of bad,
or a
//! string. All that is needed is that somehow, it be
//! possible, using just a value of this type, to
//! determine whether the object is valid or not.
This
//! type must support copy and assignment.
//!
//! <li>A static function <tt>bool
//! isOk( StatusType 
</tt>, which given a
//! <tt>StatusType</tt>, returns true if it
corresponds to
//! a valid value, and false otherwise.
//!
//! <li>Two static funtions <tt>StatusType
//! defaultInvalid()</tt> and <tt>StatusType
//! defaultValid()</tt>. With the exception of
assignment
//! of a <tt>ValueType</tt> to a <tt>Fallible</tt>,
these
//! functions are only used as default arguments.
//! </ul></dd>
//! </dl>
//!
//! \warning
//! The types <tt>ValueType</tt> and
//! <tt>Traits::StatusType</tt> are used to disabiguate
some
//! of the functions, including the constructors, and must
//! thus be distinct.
//!
//! Note that the copy constructor, the copy assignment
operator
//! and the destructor are furnished by the compiler.
//
---------------------------------------------------------------------------
template< typename ValueType,
typename Traits = DefaultFallibleTraits >
class Fallible
{
public :
typedef typename Traits::StatusType
StatusType ;
//! \pre
//! <tt>! Traits::isOk( status )</tt>
//!
//! \post
//! - <tt>! isValid()</tt>
//! - <tt>status() == status</tt>
//!
//! Note that because of the default argument, this
//! constructor also serves as the default constructor.
//
-----------------------------------------------------------------------
explicit Fallible( StatusType status
=
Traits::defaultInvalid() ) ;
//! \pre
//! <tt>Traits::isOk( status )</tt>
//!
//! \param value
//! The value of the object.
//!
//! \param status
//! The status to be associated with the object.
(This
//! defaults to the default valid status, as defined
by
//! the traits class.)
//!
//! \post
//! - <tt>isValid()</tt>
//! - <tt>value() == value</tt>
//! - <tt>status() == status</tt>
//
-----------------------------------------------------------------------
explicit Fallible( ValueType const& value,
StatusType status
=
Traits::defaultValid() ) ;
//! \post
//! - <tt>status() == other.status()</tt>
//! - <tt>! isValid() || value() == other.value()</
tt>
//
-----------------------------------------------------------------------
Fallible( Fallible const& other ) ;
~Fallible() ;
//! \post
//! - <tt>status() == other.status()</tt>
//! - <tt>! isValid() || value() == other.value()</
tt>
//
-----------------------------------------------------------------------
Fallible& operator=( Fallible const& other ) ;
//! \param value
//! The value of the object.
//!
//! \post
//! - <tt>isValid()</tt>
//! - <tt>value() == value</tt>
//! - <tt>status() == Traits::defaultValid()</tt>
//
-----------------------------------------------------------------------
Fallible& operator=( ValueType const& value ) ;
//! \param status
//! The new status.
//!
//! \pre
//! <tt>! Traits::isOk( status )</tt>
//!
//! \post
//! - <tt>! isValid()</tt>
//! - <tt>status() == status</tt>
//
-----------------------------------------------------------------------
Fallible& operator=( StatusType status ) ;
//! \return
//! <tt>Traits::isOk( status() )</tt>
//
-----------------------------------------------------------------------
bool isValid() const ;
//! \return
//! The validity state.
//
-----------------------------------------------------------------------
StatusType status() const ;
//! \return
//! The current value.
//!
//! \pre
//! <tt>isValid()</tt>
//
-----------------------------------------------------------------------
ValueType const& value() const ;
//! \return
//! <tt>value()</tt>
//!
//! \pre
//! <tt>isValid()</tt>
//
-----------------------------------------------------------------------
operator ValueType() const ;
//! \param defaultValue
//! The value to be returned if <tt>! isValid()</
tt>.
//!
//! \return
//! <tt>isValid() ? value() : defaultValue</tt>
//
-----------------------------------------------------------------------
ValueType const& elseDefaultTo( ValueType const&
defaultValue ) const ;
//! \param newStatus
//! The new validity status.
//!
//! \pre
//! <tt>! Traits::isOk( newStatus )</tt>
//!
//! \post
//! <tt>! isValid()</tt>
//!
//! The equivalent of assigning
//! <tt>Fallible< ValueType, Traits >
( newStatus 
</tt>
//! to the object.
//
-----------------------------------------------------------------------
void invalidate( StatusType newStatus
=
Traits::defaultInvalid() ) ;
//! \param newValue
//! The value of the object.
//!
//! \param newStatus
//! Le nouvel état de validité.
//!
//! \post
//! - <tt>isValid()</tt>
//! - <tt>value() == value</tt>
//! - <tt>status() == newStatus</tt>
//!
//! The equivalent of assigning
//! <tt>Fallible< ValueType, Traits >
( value, newStatus 
</tt>
//! to the object.
//
-----------------------------------------------------------------------
void validate( ValueType const& newValue,
StatusType newStatus
=
Traits::defaultValid() ) ;
//! A special version of <tt>validate()</tt>, designed to
be
//! used with base types which are expensive to copy. It
can
//! be used efficiently, for example, even when the base
type
//! is an <tt>std::vector</tt>. Rather than take a new
value
//! as an argument, it returns a non-<tt>const</tt>
reference
//! to the value contained in the Fallible object, which
the
//! caller can then modify as he wishes.
//!
//! \param newStatus
//! The new validation status.
//!
//! \pre
//! <tt>Traits::isOk( newStatus )</tt>
//!
//! \return
//! Non-<tt>const</tt> reference to the contained
object.
//!
//! \post
//! - <tt>isValid()</tt>
//!
//! \attention
//! This function requires ValueType to be
//! DefaultConstructible. If <tt>valid()</tt> before
//! calling this function, the state of the value
object
//! is not changed; otherwise, the value object will
be
//! default constructed.
//!
//! \warning
//! Note too that using this function may require some
//! particular attention to issues of thread and
exception
//! safety, since it sets the state as valid \e before
//! having made the necessary modifications in the
value.
//!
//! (Note that if a function returns a <tt>Fallible</tt>,
//! rather than a reference to a <tt>Fallible</tt>, the
//! contained object will also be copied. Thus, this
function
//! is not particularly interesting in such cases. On the
//! other hand, it can be quite useful for objects which
//! contain <tt>Fallible</tt> in an implemention of lazy
//! evaluation.)
//
-----------------------------------------------------------------------
ValueType& validate( StatusType newStatus
=
Traits::defaultValid() ) ;
//! \return
//! True if both objects are invalid, or if both are
valid
//! and <tt>value() == other.value()</tt>.
//
-----------------------------------------------------------------------
bool isEqual( Fallible const& other ) const ;
//! Defines an ordering relationship between objects, with
all
//! invalid objects considered equal, and inferior to all
//! valid objects.
//!
//! \return
//! <tt>value().compare( other.value() )</tt> if both
//! objects are valid, otherwise 1 if this object is
//! valid, and the other not, otherwise 0 if both
objects
//! are invalid, otherwise -1 (i.e. if this object is
//! invalid, and the other not).
//
-----------------------------------------------------------------------
int compare( Fallible const& other ) const ;
//! \return
//! <tt>isValid()
//! ? HashTraits< ValueType >( value() )
//! : hashInit()</tt>
//
-----------------------------------------------------------------------
HashType hashCode() const ;
//!@cond implementation
private :
StatusType myStatus ;
union {
unsigned char myValue[ sizeof( ValueType ) ] ;
MaxAlignFor< ValueType >
dummyForAlignement ;
} ;
ValueType* valueAddress() ;
ValueType const* valueAddress() const ;
void construct( ValueType const& newValue ) ;
void assign( ValueType const& newValue ) ;
void setValue( ValueType const& newValue ) ;
void destroy() ;
//!@endcond
} ;
}
}
#include "Fallible.tcc"
#endif
// Local Variables: --- for emacs
// mode: c++ --- for emacs
// tab-width: 8 --- for emacs
// End: --- for emacs
// vim: set ts=8 sw=4 filetype=cpp: --- for vim
=== end ===
Fallible.tcc:
=============
/
****************************************************************************/
/* File:
Fallible.tcc */
/* Author: J.
Kanze */
/* Date:
03/11/1994 */
/* Copyright (c) 1994 James
Kanze */
/*
------------------------------------------------------------------------
*/
#include <assert.h>
#include <new>
namespace GabiNS {
namespace Util {
template< typename ValueType, typename Traits >
inline ValueType*
Fallible< ValueType, Traits >::valueAddress()
{
assert( Traits::isOk( myStatus ) ) ;
return static_cast said:
}
template< typename ValueType, typename Traits >
inline ValueType const*
Fallible< ValueType, Traits >::valueAddress() const
{
assert( Traits::isOk( myStatus ) ) ;
return static_cast< ValueType const* >(
static_cast< void const* >( myValue ) ) ;
}
template< typename ValueType, typename Traits >
inline void
Fallible< ValueType, Traits >::construct(
ValueType const& newValue )
{
::new ( myValue ) ValueType( newValue ) ;
}
template< typename ValueType, typename Traits >
inline void
Fallible< ValueType, Traits >::assign(
ValueType const& newValue )
{
*valueAddress() = newValue ;
}
template< typename ValueType, typename Traits >
inline void
Fallible< ValueType, Traits >::setValue(
ValueType const& newValue )
{
if ( Traits::isOk( myStatus ) ) {
assign( newValue ) ;
} else {
construct( newValue ) ;
}
}
template< typename ValueType, typename Traits >
inline void
Fallible< ValueType, Traits >::destroy()
{
valueAddress()->~ValueType() ;
}
template< typename ValueType, typename Traits >
Fallible< ValueType, Traits >::Fallible(
StatusType status )
: myStatus( status )
{
assert( ! Traits::isOk( status ) ) ;
}
template< typename ValueType, typename Traits >
Fallible< ValueType, Traits >::Fallible(
ValueType const& value,
StatusType status )
: myStatus( status )
{
assert( Traits::isOk( status ) ) ;
construct( value ) ;
}
template< typename ValueType, typename Traits >
Fallible< ValueType, Traits >::Fallible(
Fallible const& other )
: myStatus( other.myStatus )
{
if ( Traits::isOk( myStatus ) ) {
construct( other.value() ) ;
}
}
template< typename ValueType, typename Traits >
Fallible< ValueType, Traits >::~Fallible()
{
if ( Traits::isOk( myStatus ) ) {
destroy() ;
}
}
template< typename ValueType, typename Traits >
Fallible< ValueType, Traits >&
Fallible< ValueType, Traits >:
perator=(
Fallible const& other )
{
if ( other.isValid() ) {
setValue( other.value() ) ;
} else if ( isValid() ) {
destroy() ;
}
myStatus = other.status() ;
return *this ;
}
template< typename ValueType, typename Traits >
Fallible< ValueType, Traits >&
Fallible< ValueType, Traits >:
perator=(
ValueType const& value )
{
validate( value, Traits::defaultValid() ) ;
return *this ;
}
template< typename ValueType, typename Traits >
Fallible< ValueType, Traits >&
Fallible< ValueType, Traits >:
perator=(
StatusType status )
{
invalidate( status ) ;
return *this ;
}
template< typename ValueType, typename Traits >
bool
Fallible< ValueType, Traits >::isValid() const
{
return Traits::isOk( myStatus ) ;
}
template< typename ValueType, typename Traits >
typename Traits::StatusType
Fallible< ValueType, Traits >::status() const
{
return myStatus ;
}
template< typename ValueType, typename Traits >
ValueType const&
Fallible< ValueType, Traits >::value() const
{
assert( isValid() ) ;
return *valueAddress() ;
}
template< typename ValueType, typename Traits >
Fallible< ValueType, Traits >:
perator ValueType() const
{
return value() ;
}
template< typename ValueType, typename Traits >
ValueType const&
Fallible< ValueType, Traits >::elseDefaultTo(
ValueType const& defaultValue ) const
{
return isValid() ? value() : defaultValue ;
}
template< typename ValueType, typename Traits >
void
Fallible< ValueType, Traits >::invalidate(
StatusType newStatus )
{
assert( ! Traits::isOk( newStatus ) ) ;
if ( isValid() ) {
destroy() ;
}
myStatus = newStatus ;
}
template< typename ValueType, typename Traits >
void
Fallible< ValueType, Traits >::validate(
ValueType const& value,
StatusType newStatus )
{
assert( Traits::isOk( newStatus ) ) ;
setValue( value ) ;
myStatus = newStatus ;
}
template< typename ValueType, typename Traits >
ValueType&
Fallible< ValueType, Traits >::validate(
StatusType newStatus )
{
assert( Traits::isOk( newStatus ) ) ;
if ( ! isValid() ) {
construct( ValueType() ) ;
}
myStatus = newStatus ;
return *valueAddress() ;
}
template< typename ValueType, typename Traits >
bool
Fallible< ValueType, Traits >::isEqual(
Fallible const& other ) const
{
return isValid()
? ( other.isValid()
&& HashTraits< ValueType >::isEqual( value(),
other.value() ) )
: ! other.isValid() ;
}
template< typename ValueType, typename Traits >
int
Fallible< ValueType, Traits >::compare(
Fallible const& other ) const
{
return isValid()
? ( other.isValid()
? value().compare( other.value() )
: 1 )
: ( other.isValid()
? -1
: 0 ) ;
}
template< typename ValueType, typename Traits >
HashType
Fallible< ValueType, Traits >::hashCode() const
{
return isValid()
? HashTraits< ValueType >::hashCode( value() )
: hashInit() ;
}
}
}
// Local Variables: --- for emacs
// mode: c++ --- for emacs
// tab-width: 8 --- for emacs
// End: --- for emacs
// vim: set ts=8 sw=4 filetype=cpp: --- for vim