separate header + unit test for this bool check mixin
This commit is contained in:
parent
5291f6e41a
commit
b45cd49c8b
10 changed files with 276 additions and 28 deletions
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
ACCESSCASTED.hpp - util template to access a value using conversion or cast as appropriate
|
||||
ACCESS-CASTED.hpp - util template to access a value using conversion or cast as appropriate
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
|
@ -21,13 +21,13 @@
|
|||
*/
|
||||
|
||||
|
||||
/** @file accesscasted.hpp
|
||||
** Helper for accessing a value employing either conversion or downcast
|
||||
/** @file access-casted.hpp
|
||||
** Helper for accessing a value, employing either conversion or downcast,
|
||||
** depending on the relation of the source type (type of the original value)
|
||||
** and the target type (type we need within the usage context).
|
||||
** When instantiating AcessCasted<TAR>, we get a template static function
|
||||
** \c AcessCasted<TAR>::access<SRC>(SRC& elm), but the actual implementation
|
||||
** is chosen using boost::type_traits. If no viable implementation can be
|
||||
** is chosen using boost::type_traits. If no sensible implementation can be
|
||||
** selected, \c EmptyVal<TAR>::create() is invoked instead, which by default
|
||||
** creates a NULL value or similar by using the no-argument ctor of the
|
||||
** type TAR. Alternatively, you may define an specialisation of EmptyVal,
|
||||
|
|
@ -38,8 +38,8 @@
|
|||
*/
|
||||
|
||||
|
||||
#ifndef UTIL_ACCESSCASTED_H
|
||||
#define UTIL_ACCESSCASTED_H
|
||||
#ifndef UTIL_ACCESS_CASTED_H
|
||||
#define UTIL_ACCESS_CASTED_H
|
||||
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <boost/type_traits/remove_pointer.hpp>
|
||||
|
|
@ -143,23 +143,23 @@ namespace util {
|
|||
|
||||
template<typename ELM>
|
||||
static typename enable_if< use_dynamic_downcast<ELM&,TAR>, TAR>::type
|
||||
access (ELM& elem)
|
||||
{
|
||||
return dynamic_cast<TAR> (elem);
|
||||
access (ELM& elem)
|
||||
{
|
||||
return dynamic_cast<TAR> (elem);
|
||||
}
|
||||
|
||||
template<typename ELM>
|
||||
static typename enable_if< use_static_downcast<ELM&,TAR>, TAR>::type
|
||||
access (ELM& elem)
|
||||
{
|
||||
return static_cast<TAR> (elem);
|
||||
access (ELM& elem)
|
||||
{
|
||||
return static_cast<TAR> (elem);
|
||||
}
|
||||
|
||||
template<typename ELM>
|
||||
static typename enable_if< use_conversion<ELM&,TAR>, TAR>::type
|
||||
access (ELM& elem)
|
||||
{
|
||||
return elem;
|
||||
access (ELM& elem)
|
||||
{
|
||||
return elem;
|
||||
}
|
||||
};
|
||||
|
||||
121
src/lib/bool-checkable.hpp
Normal file
121
src/lib/bool-checkable.hpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
BOOL-CHECKABLE.hpp - mixin template for defining a safe conversion to bool
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2009, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file bool-checkable.hpp
|
||||
** When working with generic function objects and function pointers typed to
|
||||
** arbitrary signatures, often there is the necessity to hold onto such a functor
|
||||
** while hiding the actual signature behind an common interface ("type erasure").
|
||||
** The usual solution based on subclassing has the downside of requiring separate
|
||||
** storage for the concrete functor object, which might become problematic when
|
||||
** dealing with lots of functor objects.
|
||||
**
|
||||
** Especially when dealing with tr1::function objects, all of the type differences
|
||||
** are actually encoded into 3 internal pointers, thus yielding the same size for
|
||||
** all various types of functors. Building on this observation, we can create an
|
||||
** common container object to store the varying functors inline, while hiding the
|
||||
** actual signature.
|
||||
**
|
||||
** There remains the problem of re-accessing the concrete functor later on. As
|
||||
** C++ has only rudimental introspection capabilities, we can only rely on the
|
||||
** usage context to provide the correct function signature; only when using a
|
||||
** virtual function for the re-access, we can perform at least a runtime-check.
|
||||
**
|
||||
** Thus there are various flavours for actually implementing this idea, and the
|
||||
** picking a suitable implementation depends largely on the context. Thus we
|
||||
** provide a common and expect the client code to pick an implementation policy.
|
||||
**
|
||||
** @see control::Mutation usage example
|
||||
** @see function-erasure-test.cpp
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIB_BOOL_CHECKABLE_H
|
||||
#define LIB_BOOL_CHECKABLE_H
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Provide an implicit conversion to "bool".
|
||||
* Inherit (mix-in) from this template by providing the
|
||||
* concrete subclass type ("CRTP"). Additionally, to implement
|
||||
* the specific logic of this bool check, the subclass using this
|
||||
* template must provide a public function \c isValid()
|
||||
*
|
||||
* \par safe bool conversion
|
||||
* Generally speaking, a direct automatic conversion to bool would be
|
||||
* a dangerous feature, because bool is considered an "integral type" in C.
|
||||
* Thus, you'd not only get the ability to write very expressive checks
|
||||
* like \c if(object) -- you would also get automatic conversions to int
|
||||
* where you'd never expect them. In C++ there is a well-known idiom to
|
||||
* circumvent this problem. Unfortunately, this idiom isn't easy to
|
||||
* grasp, involving the convoluted syntax of member pointers.
|
||||
* So we try to hide away these details into a mixin.
|
||||
*
|
||||
* The idea this implementation is based on is the fact that a
|
||||
* pointer-to-member is "somewhat a pointer" but not \em really a pointer.
|
||||
* Similar to a pointer, a member pointer can be \em unbound, and this
|
||||
* is the property we exploit here. But implementation-wise, a member pointer
|
||||
* is a type information plus an offset into this type, and thus can't be
|
||||
* converted to an integral type. Thus, depending on the result of the
|
||||
* \c isValid() function, the conversion operator returns either a
|
||||
* bound or unbound member pointer.
|
||||
*
|
||||
* @todo list some usage examples here
|
||||
* @see bool-checkable-test.cpp
|
||||
*/
|
||||
template<class T>
|
||||
struct BoolCheckable
|
||||
{
|
||||
typedef bool (T::*ValidityCheck)() const;
|
||||
typedef ValidityCheck _unspecified_bool_type;
|
||||
|
||||
/** implicit conversion to "bool" */
|
||||
operator _unspecified_bool_type() const ///< never throws
|
||||
{
|
||||
ValidityCheck isValid (&T::isValid);
|
||||
T const& obj = static_cast<T const&> (*this);
|
||||
|
||||
return (obj.*isValid)()? isValid : 0;
|
||||
}
|
||||
|
||||
bool operator! () const ///< never throws
|
||||
{
|
||||
ValidityCheck isValid (&T::isValid);
|
||||
T const& obj = static_cast<T const&> (*this);
|
||||
|
||||
return !(obj.*isValid)();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
@ -31,14 +31,14 @@ namespace lumiera
|
|||
|
||||
|
||||
/**
|
||||
* Identification tuple for addressing frames unambiguosly.
|
||||
* Identification tuple for addressing frames unambiguously.
|
||||
*
|
||||
* @todo currently (7/08) this is a dummy implementation to find out
|
||||
* what interface the Proc layer needs. Probably the backend will
|
||||
* later on define what is actually needed; this header should then
|
||||
* be replaced by a combined C/C++ header
|
||||
*/
|
||||
class FrameID : boost::totally_ordered<FrameID>
|
||||
class FrameID : boost::totally_ordered<FrameID> ////////////TODO it seems we don't need total ordering, only comparison. Clarify this!
|
||||
{
|
||||
long dummy;
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
|
||||
#include "lib/variant.hpp"
|
||||
#include "lib/accesscasted.hpp"
|
||||
#include "lib/access-casted.hpp"
|
||||
|
||||
#include "lib/meta/typelist.hpp"
|
||||
#include "proc/mobject/placement.hpp"
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ namespace engine {
|
|||
|
||||
virtual BuffHandle fetch (FrameID const& fID) { return current_.fetch (fID); }
|
||||
|
||||
// note: allocateBuffer() is choosen specifically based on the actual node wiring
|
||||
// note: allocateBuffer() is chosen specifically based on the actual node wiring
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ namespace engine {
|
|||
* which form the actual invocation sequence. The various operations in this sequence
|
||||
* access the context via the references in this struct, while also using the inherited
|
||||
* public State interface. The object instance actually used as Invocation is created
|
||||
* on the stack and parametrized according to the necessities of the invocation sequence
|
||||
* on the stack and parametrised according to the necessities of the invocation sequence
|
||||
* actually configured. Initially, this real instance is configured without BuffTable,
|
||||
* because the invocation may be short-circuited due to Cache hit. Otherwise, when
|
||||
* the invocation sequence actually prepares to call the process function of this
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ namespace engine {
|
|||
* Intended as a performance shortcut to avoid calling
|
||||
* up through a chain of virtual functions when deep down
|
||||
* in chained ProcNode::pull() calls. This allows derived
|
||||
* classes to proxy the state inteface.
|
||||
* classes to proxy the state interface.
|
||||
*/
|
||||
virtual State& getCurrentImplementation () =0;
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ namespace engine {
|
|||
* on the State implementation; it could be a temporary, located in
|
||||
* the cache, used for feeding calculated frames over a network, etc.
|
||||
* @return a BuffHandle encapsulating the information necessary to get
|
||||
* at the actual buffer adress and for releasing the buffer.
|
||||
* at the actual buffer address and for releasing the buffer.
|
||||
*/
|
||||
virtual BuffHandle allocateBuffer (BufferDescriptor const&) =0;
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ namespace engine {
|
|||
/** try to fetch an existing buffer containing the denoted frame from
|
||||
* a cache or similar backing system (e.g. peer over the network).
|
||||
* @return either a handle to a readonly buffer, or a null handle
|
||||
* @note the client is resposible for not modifying the provided data
|
||||
* @note the client is responsible for not modifying the provided data
|
||||
*/
|
||||
virtual BuffHandle fetch (FrameID const&) =0;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include <nobug.h>
|
||||
//#include "include/nobugcfg.h"
|
||||
#include "lib/meta/function-erasure.hpp"
|
||||
#include "lib/bool-checkable.hpp"
|
||||
|
||||
#include <iostream>
|
||||
//#include <typeinfo>
|
||||
|
|
@ -37,7 +37,7 @@ using boost::format;
|
|||
}
|
||||
|
||||
struct TestIt1
|
||||
: lumiera::typelist::BoolCheckable<TestIt1>
|
||||
: lib::BoolCheckable<TestIt1>
|
||||
{
|
||||
|
||||
int val_;
|
||||
|
|
|
|||
|
|
@ -2,18 +2,39 @@ TESTING "Component Test Suite: common and basic components" ./test-lib --group=c
|
|||
|
||||
|
||||
|
||||
TEST "Hello test" HelloWorld_test 3 <<END
|
||||
TEST "Hello test world" HelloWorld_test 3 <<END
|
||||
out: ^This is how the world ends\.\.\.$
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "AllocationCluster_test" AllocationCluster_test <<END
|
||||
TEST "Allocation Cluster" AllocationCluster_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
PLANNED "Appconfig_test" Appconfig_test <<END
|
||||
PLANNED "basic application state" Appconfig_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "implicit conversion to bool" BoolCheckable_test <<END
|
||||
out: 1
|
||||
out: 2
|
||||
out: doIt
|
||||
out: 4
|
||||
out: 5
|
||||
out: doIt
|
||||
out: 7
|
||||
out: 8
|
||||
out: doIt
|
||||
out: 10
|
||||
out: 11
|
||||
out: doIt
|
||||
out: 13
|
||||
out: 14
|
||||
out: doIt
|
||||
out: sizeof.+lib.+test.+ExampleCustomType123.+ = [248]
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ test_lib_SOURCES = \
|
|||
$(testlib_srcdir)/allocationclustertest.cpp \
|
||||
$(testlib_srcdir)/appconfigtest.cpp \
|
||||
$(testlib_srcdir)/customsharedptrtest.cpp \
|
||||
$(testlib_srcdir)/bool-checkable-test.cpp \
|
||||
$(testlib_srcdir)/exceptionerrortest.cpp \
|
||||
$(testlib_srcdir)/factory-special-test.cpp \
|
||||
$(testlib_srcdir)/factorytest.cpp \
|
||||
|
|
|
|||
105
tests/lib/bool-checkable-test.cpp
Normal file
105
tests/lib/bool-checkable-test.cpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
BoolCheckable(Test) - verify the mixin for implicit conversion to bool
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2009, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/bool-checkable.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using std::cout;
|
||||
using lib::test::showSizeof;
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
/**
|
||||
* An example custom type,
|
||||
* which provides an implicit bool conversion
|
||||
* to express some special validity check.
|
||||
*/
|
||||
|
||||
class ExampleCustomType123
|
||||
: public lib::BoolCheckable<ExampleCustomType123>
|
||||
{
|
||||
|
||||
int val_;
|
||||
|
||||
public:
|
||||
|
||||
ExampleCustomType123 (int i)
|
||||
: val_(i)
|
||||
{ }
|
||||
|
||||
/** implements the custom logic to decide
|
||||
* if this instance can be considered "valid"
|
||||
*/
|
||||
bool
|
||||
isValid() const
|
||||
{
|
||||
return val_ % 3;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* @test verify the implementation of a validity check,
|
||||
* to be accessed as an implicit conversion to bool.
|
||||
*
|
||||
* @see lib::BoolCheckable
|
||||
* @todo list some usage examples here
|
||||
*/
|
||||
class BoolCheckable_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
for (int i=1; i<16; ++i)
|
||||
{
|
||||
ExampleCustomType123 test (i);
|
||||
|
||||
if (!test)
|
||||
cout << "doIt \n";
|
||||
if (test)
|
||||
cout << i << "\n";
|
||||
|
||||
ASSERT (test.isValid() == bool(test) );
|
||||
ASSERT (test.isValid() != !test);
|
||||
}
|
||||
|
||||
cout << showSizeof<ExampleCustomType123>() <<"\n";
|
||||
ASSERT (sizeof (int) == sizeof (ExampleCustomType123));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (BoolCheckable_test, "unit common");
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
Loading…
Reference in a new issue