LUMIERA.clone/src/lib/meta/function-erasure.hpp

241 lines
6.9 KiB
C++
Raw Normal View History

2009-06-20 01:28:47 +02:00
/*
FUNCTION-ERASURE.hpp - wrapping a functor object for inline storage while hiding the signature
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 function-erasure.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.
2009-06-20 01:28:47 +02:00
**
** 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
2009-06-20 01:28:47 +02:00
**
*/
#ifndef LUMIERA_META_FUNCTION_ERASURE_H
#define LUMIERA_META_FUNCTION_ERASURE_H
#include "lib/util.hpp"
#include "lib/error.hpp"
#include "lib/bool-checkable.hpp"
#include "lib/opaque-holder.hpp"
2009-06-20 01:28:47 +02:00
#include <tr1/functional>
namespace lumiera {
namespace typelist{
2009-06-20 06:11:09 +02:00
2009-06-20 01:28:47 +02:00
using std::tr1::function;
using util::unConst;
2009-06-20 01:28:47 +02:00
2009-06-20 06:11:09 +02:00
/******************************************************
2009-06-20 01:28:47 +02:00
* Generic wrapper carrying a function object
* while hiding the actual function signature
2009-06-20 06:11:09 +02:00
* - create it using a function ref or pointer
* - the StoreFunction-policy also allows
* creation based on an existing function object
* - re-access the functor or function ref
* using the templated \c getFun()
*
* @param FH policy to control the implementation.
* In most cases, you should use "StoreFunction"
2009-06-20 01:28:47 +02:00
* @note not statically typesafe. Depending on
2009-06-20 06:11:09 +02:00
* the specified policy, it \em might be
* run-time typesafe.
2009-06-20 01:28:47 +02:00
*/
template<class FH>
struct FunErasure
: FH
{
template<typename FUN>
FunErasure (FUN const& functor)
: FH(functor)
{ }
};
2009-06-20 06:11:09 +02:00
2009-06-20 06:11:09 +02:00
/* ====== Policy classes ====== */
typedef function<void(void)> FunVoid;
typedef lib::InPlaceAnyHolder< sizeof(FunVoid) // same size for all function objects
, lib::InPlaceAnyHolder_unrelatedTypes // no common base class!
> FunHolder;
/**
2009-06-20 01:28:47 +02:00
* 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.
2009-06-20 01:28:47 +02:00
*/
class StoreFunction
: public FunHolder
2009-06-20 01:28:47 +02:00
{
public:
template<typename SIG>
StoreFunction (SIG& fun)
: FunHolder(function<SIG>(fun))
{ }
template<typename SIG>
StoreFunction (function<SIG> const& fun)
: FunHolder(fun)
{ }
2009-06-20 01:28:47 +02:00
template<typename SIG>
function<SIG>&
getFun ()
{
return get<function<SIG> >();
}
2009-06-20 01:28:47 +02:00
};
2009-06-20 06:11:09 +02:00
/**
2009-06-20 01:28:47 +02:00
* Policy for FunErasure: store a bare function pointer.
* Using this policy allows to store a conventional function ptr,
2009-06-20 06:11:09 +02:00
* while still being able to re-access it later with run-time type check.
2009-06-20 01:28:47 +02:00
* The price to pay is vtable access.
*/
class StoreFunPtr
: public lib::BoolCheckable<StoreFunPtr>
2009-06-20 01:28:47 +02:00
{
/** Helper: type erasure */
struct Holder
{
void *fP_;
2009-06-20 01:28:47 +02:00
virtual ~Holder() {}
};
/** storing and retrieving concrete function ptr */
template<typename SIG>
struct FunctionHolder : Holder
{
FunctionHolder (SIG *fun)
2009-06-20 01:28:47 +02:00
{
REQUIRE (fun);
fP_ = reinterpret_cast<void*> (fun);
2009-06-20 01:28:47 +02:00
}
SIG&
get()
{
2009-06-20 06:11:09 +02:00
return *reinterpret_cast<SIG*> (fP_);
2009-06-20 01:28:47 +02:00
}
};
/** embedded container holding the pointer */
Holder holder_;
public:
template<typename SIG>
StoreFunPtr (SIG& fun)
{
new(&holder_) FunctionHolder<SIG> (&fun);
}
template<typename SIG>
StoreFunPtr (SIG *fun)
2009-06-20 01:28:47 +02:00
{
new(&holder_) FunctionHolder<SIG> (fun);
}
template<typename SIG>
SIG&
2009-06-20 01:28:47 +02:00
getFun ()
{
REQUIRE (INSTANCEOF (FunctionHolder<SIG>, &holder_));
return static_cast<FunctionHolder<SIG>&> (holder_).get();
}
bool
isValid() const
{
return holder_.fP_;
}
2009-06-20 01:28:47 +02:00
};
2009-06-20 06:11:09 +02:00
/**
2009-06-20 01:28:47 +02:00
* 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
: public lib::BoolCheckable<StoreUncheckedFunPtr>
2009-06-20 01:28:47 +02:00
{
void *funP_;
2009-06-20 01:28:47 +02:00
public:
template<typename SIG>
StoreUncheckedFunPtr (SIG& fun)
{
funP_ = reinterpret_cast<void*> (&fun);
}
template<typename SIG>
StoreUncheckedFunPtr (SIG *fun)
{
funP_ = reinterpret_cast<void*> (fun);
2009-06-20 01:28:47 +02:00
}
template<typename SIG>
SIG&
getFun ()
{
2009-06-20 06:11:09 +02:00
return *reinterpret_cast<SIG*> (funP_);
2009-06-20 01:28:47 +02:00
}
bool
isValid() const
{
return funP_;
}
2009-06-20 01:28:47 +02:00
};
}} // namespace lumiera::typelist
#endif