diff --git a/src/lib/wrapper.hpp b/src/lib/wrapper.hpp index 7772965ab..76a9f6282 100644 --- a/src/lib/wrapper.hpp +++ b/src/lib/wrapper.hpp @@ -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 -#include ////////////////////////TODO only because of tr1::ref_wrapper, used by AssignableRefWrapper -- can we get rid of this import? +#include 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 @@ -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 inline bool operator!= (ItemWrapper const& w1, ItemWrapper 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 + class FunctionResult + : public function + , public lib::BoolCheckable > + { + typedef typename FunctionSignature >::Ret Res; + typedef ItemWrapper 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 + FunctionResult (FUN targetFunction) + { + using std::tr1::bind; + using std::tr1::placeholders::_1; + using lumiera::typelist::func::chained; + + function doCaptureResult = bind (&FunctionResult::captureResult, this, _1 ); + function chainedWithResCapture = chained (targetFunction, doCaptureResult); + + function::operator= (chainedWithResCapture); // define the function (baseclass) + } + + + Res operator*() const { return *lastResult_; } + bool isValid () const { return lastResult_.isValid(); } + }; + + }} // namespace lib::wrap #endif diff --git a/tests/lib/item-wrapper-test.cpp b/tests/lib/item-wrapper-test.cpp index b7532ef4b..2ed1e3b53 100644 --- a/tests/lib/item-wrapper-test.cpp +++ b/tests/lib/item-wrapper-test.cpp @@ -28,7 +28,6 @@ #include "lib/wrapper.hpp" -//#include #include #include #include @@ -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& vec, size_t idx) + { + return vec[idx]; + } + + function + pickElement_ofVector (vector& 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 funRes (bind (&vector::at, _1 )); + FunctionResult 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); }