draft a test to sharpen the idea of the function holder (erasure)

This commit is contained in:
Fischlurch 2009-06-20 04:43:52 +02:00
parent a565bfef73
commit 079030818d
6 changed files with 283 additions and 245 deletions

View file

@ -37,28 +37,22 @@
#ifndef LUMIERA_META_FUNCTION_CLOSURE_H
#define LUMIERA_META_FUNCTION_CLOSURE_H
#include "lib/meta/typelist.hpp" /////////////TODO
#include "lib/meta/generator.hpp" /////////////TODO
#include "lib/meta/function.hpp"
#include "lib/meta/tuple.hpp"
#include <tr1/functional>
#include "lib/util.hpp" ////////////////////////TODO
#include "lib/error.hpp"
namespace lumiera {
namespace typelist{
using std::tr1::bind;
//using std::tr1::placeholders::_1;
//using std::tr1::placeholders::_2;
using std::tr1::function;
using std::tr1::bind;
namespace func { // helpers for binding and applying a function to an argument tuple
namespace func { ///< helpers for binding and applying a function to an argument tuple
using tuple::element;
@ -392,157 +386,5 @@ namespace typelist{
/**
* Generic wrapper carrying a function object
* while hiding the actual function signature
* @note not statically typesafe. Depending on
* the actual embedded container type,
* it \em might be run-time typesafe.
*/
template<class FH>
struct FunErasure
: FH
{
template<typename FUN>
FunErasure (FUN const& functor)
: FH(functor)
{ }
};
/**
* Policy for FunErasure: store an embedded tr1::function
* Using this policy allows to store arbitrary complex functor objects
* embedded within a neutral container and retrieving them later type-safe.
* The price to pay is vtable access and heap storage of function arguments.
*/
class StoreFunction
{
/** Helper: type erasure */
struct Holder
{
enum { SIZE = sizeof(function<void(void)>) };
char storage_[SIZE];
virtual ~Holder() {}
};
/** embedding the concrete functor object */
template<typename SIG>
struct FunctionHolder : Holder
{
typedef function<SIG> Functor;
FunctionHolder (SIG& fun)
{
REQUIRE (SIZE >= sizeof(Functor));
new(&storage_) Functor (fun);
}
~FunctionHolder()
{
get()->~Functor();
}
Functor&
get()
{
return static_cast<Functor*> (&storage_);
}
};
/** embedded buffer actually holding
* the concrete Functor object */
Holder holder_;
public:
template<typename SIG>
StoreFunction (SIG& fun)
{
new(&holder_) FunctionHolder<SIG> (fun);
}
template<typename SIG>
function<SIG>&
getFun ()
{
REQUIRE (INSTANCEOF (FunctionHolder<SIG>, &holder_));
return static_cast<FunctionHolder<SIG>&> (holder_).get();
}
};
/**
* Policy for FunErasure: store a bare function pointer.
* Using this policy allows to store a conventional function ptr,
* while still being able to re-access it later with run-time typecheck.
* The price to pay is vtable access.
*/
class StoreFunPtr
{
/** Helper: type erasure */
struct Holder
{
void *fun_;
virtual ~Holder() {}
};
/** storing and retrieving concrete function ptr */
template<typename SIG>
struct FunctionHolder : Holder
{
FunctionHolder (SIG& fun)
{
fun_ = &fun;
}
SIG&
get()
{
return reinterpret_cast<SIG*> (&fun_);
}
};
/** embedded container holding the pointer */
Holder holder_;
public:
template<typename SIG>
StoreFunPtr (SIG& fun)
{
new(&holder_) FunctionHolder<SIG> (fun);
}
template<typename SIG>
function<SIG>&
getFun ()
{
REQUIRE (INSTANCEOF (FunctionHolder<SIG>, &holder_));
return static_cast<FunctionHolder<SIG>&> (holder_).get();
}
};
/**
* Policy for FunErasure: store an unchecked bare function pointer.
* Using this policy allows to store a conventional function ptr,
* and to retrieve it without overhead, but also without safety.
*/
class StoreUncheckedFunPtr
{
void *fun_;
public:
template<typename SIG>
StoreUncheckedFunPtr (SIG& fun)
{
fun_ = &fun;
}
template<typename SIG>
SIG&
getFun ()
{
return reinterpret_cast<SIG*> (&fun_);
}
};
}} // namespace lumiera::typelist
#endif

View file

@ -22,14 +22,30 @@
/** @file function-erasure.hpp
** Partial function application and building a complete function closure.
** This is a small addendum to (and thin wrapper for) tr1/functional, supporting
** the case when a function should be closed over (all) arguments, where especially
** the parameter values to close on are provided as a tuple.
** 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.
**
** @see control::CommandDef usage example
** @see function.hpp
** @see tuple.hpp
** 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
**
*/
@ -37,23 +53,15 @@
#ifndef LUMIERA_META_FUNCTION_ERASURE_H
#define LUMIERA_META_FUNCTION_ERASURE_H
#include "lib/meta/typelist.hpp" /////////////TODO
#include "lib/meta/generator.hpp" /////////////TODO
#include "lib/meta/function.hpp"
#include "lib/meta/tuple.hpp"
#include "lib/util.hpp"
#include "lib/error.hpp"
#include <tr1/functional>
#include "lib/util.hpp" ////////////////////////TODO
#include "lib/error.hpp"
namespace lumiera {
namespace typelist{
using std::tr1::bind;
//using std::tr1::placeholders::_1;
//using std::tr1::placeholders::_2;
using std::tr1::function;
@ -96,19 +104,19 @@ namespace typelist{
struct FunctionHolder : Holder
{
typedef function<SIG> Functor;
FunctionHolder (SIG& fun)
FunctionHolder (Functor const& fun)
{
REQUIRE (SIZE >= sizeof(Functor));
new(&storage_) Functor (fun);
}
~FunctionHolder()
{
get()->~Functor();
get().~Functor();
}
Functor&
get()
{
return static_cast<Functor*> (&storage_);
return *reinterpret_cast<Functor*> (&storage_);
}
};
@ -119,6 +127,11 @@ namespace typelist{
public:
template<typename SIG>
StoreFunction (SIG& fun)
{
new(&holder_) FunctionHolder<SIG> (function<SIG>(fun));
}
template<typename SIG>
StoreFunction (function<SIG> const& fun)
{
new(&holder_) FunctionHolder<SIG> (fun);
}
@ -144,7 +157,7 @@ namespace typelist{
/** Helper: type erasure */
struct Holder
{
void *fun_;
void *fP_;
virtual ~Holder() {}
};
@ -152,14 +165,15 @@ namespace typelist{
template<typename SIG>
struct FunctionHolder : Holder
{
FunctionHolder (SIG& fun)
FunctionHolder (SIG *fun)
{
fun_ = &fun;
REQUIRE (fun);
fP_ = reinterpret_cast<void*> (fun);
}
SIG&
get()
{
return reinterpret_cast<SIG*> (&fun_);
return *reinterpret_cast<SIG*> (&fP_);
}
};
@ -169,12 +183,17 @@ namespace typelist{
public:
template<typename SIG>
StoreFunPtr (SIG& fun)
{
new(&holder_) FunctionHolder<SIG> (&fun);
}
template<typename SIG>
StoreFunPtr (SIG *fun)
{
new(&holder_) FunctionHolder<SIG> (fun);
}
template<typename SIG>
function<SIG>&
SIG&
getFun ()
{
REQUIRE (INSTANCEOF (FunctionHolder<SIG>, &holder_));
@ -190,20 +209,25 @@ namespace typelist{
*/
class StoreUncheckedFunPtr
{
void *fun_;
void *funP_;
public:
template<typename SIG>
StoreUncheckedFunPtr (SIG& fun)
{
fun_ = &fun;
funP_ = reinterpret_cast<void*> (&fun);
}
template<typename SIG>
StoreUncheckedFunPtr (SIG *fun)
{
funP_ = reinterpret_cast<void*> (fun);
}
template<typename SIG>
SIG&
getFun ()
{
return reinterpret_cast<SIG*> (&fun_);
return *reinterpret_cast<SIG*> (&funP_);
}
};

View file

@ -213,6 +213,16 @@ out: dtor ~TargetObj\(12\) successful
END
PLANNED "FunctionClosure_test" FunctionClosure_test <<END
return: 0
END
PLANNED "FunctionErasure_test" FunctionErasure_test <<END
return: 0
END
TEST "HashIndexed_test" HashIndexed_test <<END
return: 0
END

View file

@ -0,0 +1,60 @@
/*
DUMMY-FUNCTIONS - mock functions for checking functor utils
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.
* *****************************************************/
#ifndef LIB_META_DUMMY_FUNCTIONS_H
#define LIB_META_DUMMY_FUNCTIONS_H
//#include <boost/format.hpp>
//#include <string>
//using std::string;
//using boost::format;
namespace lumiera {
namespace typelist{
namespace test {
namespace { // test helpers
int _sum_ =0; ///< used to verify the effect of testFunc
void
testFunc (int i, char c)
{
_sum_ += i + c;
}
int
returnIt ()
{
return _sum_;
}
}
}}} // namespace lumiera::typelist::test
#endif

View file

@ -22,7 +22,8 @@
/** @file function-closure-test.cpp
** bla
**
** @todo define function-closure-test
**
** @see function-closure.hpp
** @see control::CmdClosure real world usage example
@ -32,7 +33,8 @@
#include "lib/test/run.hpp"
#include "lib/meta/function-closure.hpp"
#include "lib/util.hpp"
#include "meta/dummy-functions.hpp"
//#include "lib/util.hpp"
//#include <boost/format.hpp>
#include <iostream>
@ -47,13 +49,6 @@ namespace typelist{
namespace test {
namespace { // test data
} // (End) test data
/*************************************************************************
@ -62,23 +57,11 @@ namespace test {
*/
class FunctionClosure_test : public Test
{
virtual void run(Arg arg)
virtual void
run (Arg)
{
check_distribute();
check_combine();
UNIMPLEMENTED ("verify function closure utils");
}
void check_distribute()
{
}
void check_combine()
{
}
};

View file

@ -21,20 +21,13 @@
* *****************************************************/
/** @file function-erasure-test.cpp
** bla
**
** @see control::FunErasure
** @see command-mutation.hpp real world usage example
**
*/
#include "lib/test/run.hpp"
#include "lib/meta/function-erasure.hpp"
#include "lib/util.hpp"
#include "meta/dummy-functions.hpp"
//#include "lib/util.hpp"
//#include <boost/format.hpp>
#include <tr1/functional>
#include <iostream>
using ::test::Test;
@ -46,38 +39,164 @@ namespace lumiera {
namespace typelist{
namespace test {
using std::tr1::function;
using std::tr1::bind;
namespace { // test data
} // (End) test data
typedef FunErasure<StoreFunction> Efun;
typedef FunErasure<StoreFunPtr> Efp;
typedef FunErasure<StoreUncheckedFunPtr> Evoid;
/*************************************************************************
* @test //////////////////////////////////////////
* - building combinations and permutations
* @test Create specifically typed functor objects and then wrap them
* into common holder objects, thereby discarding the specific
* signature type information. Later on, the concrete functor
* can be re-accessed, given the exact and specific type.
*
* @see control::FunErasure
* @see command-mutation.hpp real world usage example
*/
class FunctionErasure_test : public Test
{
virtual void run(Arg arg)
{
check_distribute();
check_combine();
}
void check_distribute()
{
}
void check_combine()
virtual void
run (Arg)
{
function<void(int,char)> bindFunc = bind (testFunc,_1,_2);
function<void(int,char)> pAplFunc = bind (testFunc,_1,'x');
function<void(int,char)> memFunc = bind (&FunctionErasure_test::testMemberFunction,this, _1);
function<int(void)> getterFunc = &returnIt;
check_FunctorContainer( Efun (testFunc)
, Efun (bindFunc)
, Efun (pAplFunc)
, Efun (memFunc)
, Efun (getterFunc)
);
check_FunctPtrHolder(Efp(testFunc)
,Efp(&testFunc)
, Efp(returnIt));
check_VoidPtrHolder(Evoid(testFunc)
,Evoid(&testFunc)
, Evoid(returnIt));
}
void
testMemberFunction (char c)
{
return testFunc('a'-'A', c);
}
void
check_FunctorContainer (Efun f1, Efun f2, Efun f3, Efun f4, Efun f5)
{
typedef void (Sig1) (int,char);
typedef void (Sig2) (int);
typedef void (Sig3) (char);
typedef int (Sig4) ();
_sum_ = 0;
f1.getFun<Sig1>() (-11,'M');
ASSERT (_sum_ == 'M'-11);
_sum_ = 0;
f2.getFun<Sig1>() (-22,'M');
ASSERT (_sum_ == 'M'-22);
_sum_ = 0;
f3.getFun<Sig2>() (-33);
ASSERT (_sum_ == 'x'-22);
_sum_ = 0;
f4.getFun<Sig3>() ('U');
ASSERT (_sum_ == 'u');
ASSERT ( 'u' == f5.getFun<Sig4>() () );
#if false ////////////////////////////////////////////////////////TODO: restore throwing ASSERT
VERIFY_ERROR (ASSERTION, f1.getFun<Sig2>() );
VERIFY_ERROR (ASSERTION, f1.getFun<Sig3>() );
VERIFY_ERROR (ASSERTION, f1.getFun<Sig4>() );
VERIFY_ERROR (ASSERTION, f2.getFun<Sig2>() );
VERIFY_ERROR (ASSERTION, f3.getFun<Sig3>() );
VERIFY_ERROR (ASSERTION, f2.getFun<Sig4>() );
VERIFY_ERROR (ASSERTION, f3.getFun<Sig1>() );
VERIFY_ERROR (ASSERTION, f3.getFun<Sig3>() );
VERIFY_ERROR (ASSERTION, f3.getFun<Sig4>() );
VERIFY_ERROR (ASSERTION, f4.getFun<Sig1>() );
VERIFY_ERROR (ASSERTION, f4.getFun<Sig2>() );
VERIFY_ERROR (ASSERTION, f4.getFun<Sig4>() );
VERIFY_ERROR (ASSERTION, f5.getFun<Sig1>() );
VERIFY_ERROR (ASSERTION, f5.getFun<Sig2>() );
VERIFY_ERROR (ASSERTION, f5.getFun<Sig3>() );
#endif
}
void
check_FunctPtrHolder (Efp f1, Efp f2, Efp f3)
{
typedef void (*FP)(int,char);
typedef void (&FR)(int,char);
FP fun1 = &f1.getFun<void(int,char)>();
FP fun2 = &f2.getFun<void(int,char)>();
FR fun2r = f2.getFun<void(int,char)>();
_sum_ = 0;
(*fun1) (10, 'a');
ASSERT (_sum_ == 10+'a');
(*fun2) (20, 'b');
ASSERT (_sum_ == 10+'a'+20+'b');
fun2r (30, 'c');
ASSERT (_sum_ == 10+'a'+20+'b'+30+'c');
ASSERT (_sum_ == (f3.getFun<int(void)>()) () );
#if false ////////////////////////////////////////////////////////TODO: restore throwing ASSERT
VERIFY_ERROR (ASSERTION, f1.getFun<int(int)>() );
#endif
}
void
check_VoidPtrHolder (Evoid f1, Evoid f2, Evoid f3)
{
typedef void (*FP)(int,char);
typedef void (&FR)(int,char);
FP fun1 = &f1.getFun<void(int,char)>();
FP fun2 = &f2.getFun<void(int,char)>();
FR fun2r = f2.getFun<void(int,char)>();
_sum_ = 0;
(*fun1) (10, 'a');
ASSERT (_sum_ == 10+'a');
(*fun2) (20, 'b');
ASSERT (_sum_ == 10+'a'+20+'b');
fun2r (30, 'c');
ASSERT (_sum_ == 10+'a'+20+'b'+30+'c');
ASSERT (_sum_ == (f3.getFun<int(void)>()) () );
FP bad_fun = &f3.getFun<void(int,char)>();
ASSERT ((void*)bad_fun == &returnIt);
(*bad_fun) (11, 'x'); // SEGV
}
};