implemented this FunctionResult template, needed for Ticket #175
This commit is contained in:
parent
4cb32047ae
commit
534ae16605
2 changed files with 115 additions and 32 deletions
|
|
@ -25,7 +25,15 @@
|
|||
** This is (intended to become) a loose collection of the various small helper templates
|
||||
** for wrapping, containing, placing or handling a somewhat \em problematic other object.
|
||||
** Mostly these are implemented to suit a specific need and then factored out later on.
|
||||
**
|
||||
** - AssignableRefWrapper is not used anymore as of 12/09
|
||||
** - ItemWrapper is a similar concept, but more like a smart-ptr. Moreover,
|
||||
** it can be instantiated with a value type, a pointer or a reference type,
|
||||
** yielding the same behaviour in all cases (useful for building templates)
|
||||
** - FunctionResult is the combination of ItemWrapper with a functor object
|
||||
** to cache the function result value. It is used to implement a transforming
|
||||
** iterator, especially supporting the case when the transformation function
|
||||
** is to return a reference
|
||||
**
|
||||
** @see lib::TransformIter
|
||||
**
|
||||
*/
|
||||
|
|
@ -36,28 +44,28 @@
|
|||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/bool-checkable.hpp"
|
||||
#include "lib/meta/function.hpp"
|
||||
#include "lib/meta/function-closure.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
//#include <tr1/memory>
|
||||
#include <tr1/functional> ////////////////////////TODO only because of tr1::ref_wrapper, used by AssignableRefWrapper -- can we get rid of this import?
|
||||
#include <tr1/functional>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace wrapper {
|
||||
|
||||
//using std::tr1::shared_ptr;
|
||||
//using std::tr1::weak_ptr;
|
||||
using util::unConst;
|
||||
using util::isSameObject;
|
||||
using lumiera::typelist::FunctionSignature;
|
||||
using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE;
|
||||
|
||||
using std::tr1::function;
|
||||
|
||||
|
||||
/**
|
||||
* Extension to boost::reference_wrapper:
|
||||
* Allows additionally to re-bind to another reference,
|
||||
* almost like a pointer. For example this allows to cache
|
||||
* results returned from an API call by reference.
|
||||
* almost like a pointer. Helpful for building templates.
|
||||
* @warning potentially dangerous
|
||||
*/
|
||||
template<typename TY>
|
||||
|
|
@ -87,7 +95,7 @@ namespace wrapper {
|
|||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Universal value/ref wrapper behaving like a pointer.
|
||||
|
|
@ -278,7 +286,6 @@ namespace wrapper {
|
|||
return (!w1 && !w2)
|
||||
|| ( w1 && w2 && (*w1)==(*w2));
|
||||
}
|
||||
|
||||
template<typename TY>
|
||||
inline bool
|
||||
operator!= (ItemWrapper<TY> const& w1, ItemWrapper<TY> const& w2)
|
||||
|
|
@ -288,5 +295,61 @@ namespace wrapper {
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Extension of ItemWrapper: a function remembering
|
||||
* the result of the last invocation. Initially, the "value"
|
||||
* is bottom (undefined, NIL), until the function is invoked
|
||||
* for the first time. After that, the result of the last
|
||||
* invocation can be accessed by \c operator*()
|
||||
*
|
||||
* @see TransformIter usage example
|
||||
*/
|
||||
template<typename SIG>
|
||||
class FunctionResult
|
||||
: public function<SIG>
|
||||
, public lib::BoolCheckable<FunctionResult<SIG> >
|
||||
{
|
||||
typedef typename FunctionSignature<function<SIG> >::Ret Res;
|
||||
typedef ItemWrapper<Res> ResWrapper;
|
||||
|
||||
ResWrapper lastResult_;
|
||||
|
||||
|
||||
Res
|
||||
captureResult (Res res)
|
||||
{
|
||||
lastResult_ = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
/** Explanation:
|
||||
* - *this is a \em function
|
||||
* - initially it is defined as invalid
|
||||
* - then we build the function composition of
|
||||
* the target function, and a function storing
|
||||
* the result value into the ResWrapper member
|
||||
* - define ourselves by assigning the resulting
|
||||
* composite function
|
||||
*/
|
||||
template<typename FUN>
|
||||
FunctionResult (FUN targetFunction)
|
||||
{
|
||||
using std::tr1::bind;
|
||||
using std::tr1::placeholders::_1;
|
||||
using lumiera::typelist::func::chained;
|
||||
|
||||
function<Res(Res)> doCaptureResult = bind (&FunctionResult::captureResult, this, _1 );
|
||||
function<SIG> chainedWithResCapture = chained (targetFunction, doCaptureResult);
|
||||
|
||||
function<SIG>::operator= (chainedWithResCapture); // define the function (baseclass)
|
||||
}
|
||||
|
||||
|
||||
Res operator*() const { return *lastResult_; }
|
||||
bool isValid () const { return lastResult_.isValid(); }
|
||||
};
|
||||
|
||||
|
||||
}} // namespace lib::wrap
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
#include "lib/wrapper.hpp"
|
||||
|
||||
//#include <boost/lexical_cast.hpp>
|
||||
#include <tr1/functional>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
|
@ -42,15 +41,14 @@ namespace wrapper {
|
|||
namespace test{
|
||||
|
||||
using ::Test;
|
||||
// using boost::lexical_cast;
|
||||
using util::isSameObject;
|
||||
// using util::for_each;
|
||||
// using util::isnil;
|
||||
using std::tr1::bind;
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using lib::test::randStr;
|
||||
using lib::test::showSizeof;
|
||||
using util::isSameObject;
|
||||
|
||||
using std::tr1::placeholders::_1;
|
||||
using std::tr1::ref;
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
|
|
@ -59,7 +57,7 @@ namespace test{
|
|||
namespace { // Test helper: yet another ctor/dtor counting class
|
||||
|
||||
long cntTracker = 0;
|
||||
|
||||
|
||||
struct Tracker
|
||||
{
|
||||
uint i_;
|
||||
|
|
@ -72,7 +70,22 @@ namespace test{
|
|||
bool operator== (Tracker const& t1, Tracker const& t2) { return t1.i_ == t2.i_; }
|
||||
bool operator!= (Tracker const& t1, Tracker const& t2) { return t1.i_ != t2.i_; }
|
||||
|
||||
} // (END) Test data
|
||||
|
||||
/// to be bound as test function....
|
||||
int&
|
||||
pickElement (vector<int>& vec, size_t idx)
|
||||
{
|
||||
return vec[idx];
|
||||
}
|
||||
|
||||
function<int&(size_t)>
|
||||
pickElement_ofVector (vector<int>& vec)
|
||||
{
|
||||
return std::tr1::bind (pickElement, ref(vec), _1 );
|
||||
}
|
||||
|
||||
|
||||
} // (END) Test helpers
|
||||
|
||||
|
||||
|
||||
|
|
@ -81,10 +94,11 @@ namespace test{
|
|||
|
||||
|
||||
/*******************************************************************************
|
||||
* @test use the ItemWrapper to define inline-storage holding values,
|
||||
* pointers and references. Verify correct behaviour in each case,
|
||||
* including (self)assignment, empty check, invalid dereferentiation.
|
||||
*
|
||||
* @test use the ItemWrapper to define inline-storage holding values,
|
||||
* pointers and references. Verify correct behaviour in each case,
|
||||
* including (self)assignment, empty check, invalid dereferentiation.
|
||||
*
|
||||
* @see wrapper.hpp
|
||||
*/
|
||||
class ItemWrapper_test : public Test
|
||||
{
|
||||
|
|
@ -162,16 +176,16 @@ namespace test{
|
|||
ASSERT (otherVal == *copy2);
|
||||
ASSERT (wrap != copy1);
|
||||
ASSERT (wrap != copy2);
|
||||
|
||||
|
||||
copy1 = empty; // assign empty to discard value
|
||||
copy1 = copy1; // self-assign empty
|
||||
copy1 = copy1; // self-assign empty value
|
||||
ASSERT (!copy1);
|
||||
|
||||
copy1 = copy2;
|
||||
ASSERT (otherVal == *copy1);
|
||||
copy1 = copy1; // self-assign (will be suppressed)
|
||||
ASSERT (otherVal == *copy1);
|
||||
copy1 = *copy1; // self-assign also detected in this case
|
||||
copy1 = *copy1; // self-assign also detected in this case
|
||||
ASSERT (otherVal == *copy2);
|
||||
|
||||
ASSERT (copy1);
|
||||
|
|
@ -230,7 +244,13 @@ namespace test{
|
|||
|
||||
|
||||
/** @test verify an extension built on top of the ItemWrapper:
|
||||
* a function which remembers the last result */
|
||||
* a function which remembers the last result. We use a
|
||||
* test function, which picks a member of an vector and
|
||||
* returns a \em reference to it. Thus the cached "result"
|
||||
* can be used to access and change the values within the
|
||||
* original vector. In a real world usage scenario, such a
|
||||
* function could be an (expensive) data structure access.
|
||||
*/
|
||||
void
|
||||
verifyFunctionResult()
|
||||
{
|
||||
|
|
@ -238,7 +258,7 @@ namespace test{
|
|||
for (uint i=0; i<10; ++i)
|
||||
testVec.push_back(i);
|
||||
|
||||
FunctionResult<int&(size_t)> funRes (bind (&vector<int>::at, _1 ));
|
||||
FunctionResult<int&(size_t)> funRes (pickElement_ofVector(testVec));
|
||||
|
||||
// function was never invoked, thus the remembered result is NIL
|
||||
ASSERT (!funRes);
|
||||
|
|
@ -248,17 +268,17 @@ namespace test{
|
|||
ASSERT (5 == r5);
|
||||
ASSERT (isSameObject (r5, testVec[5]));
|
||||
|
||||
int r5x = *funRes;
|
||||
int& r5x = *funRes;
|
||||
ASSERT (isSameObject (r5, r5x));
|
||||
|
||||
ASSERT ( isSameObject (r5, *funRes));
|
||||
int r7 = funRes (7);
|
||||
int& r7 = funRes (7);
|
||||
ASSERT (!isSameObject (r5, *funRes));
|
||||
ASSERT (!isSameObject (r7, *funRes));
|
||||
ASSERT ( isSameObject (r7, *funRes));
|
||||
|
||||
-- r5x;
|
||||
++ *funRes;
|
||||
ASSERT (5+1 == testVec[5]);
|
||||
ASSERT (5-1 == testVec[5]);
|
||||
ASSERT (7+1 == testVec[7]);
|
||||
ASSERT (7+1 == r7);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue