implemented this FunctionResult template, needed for Ticket #175

This commit is contained in:
Fischlurch 2009-12-23 04:46:09 +01:00
parent 4cb32047ae
commit 534ae16605
2 changed files with 115 additions and 32 deletions

View file

@ -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

View file

@ -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);
}