Yet another chainsaw massacre. One of the most obnoxious annoyances with C++ metaprogramming is the need to insert `typename` and `template` qualifiers into most definitions, to help the compiler to cope with the syntax, which is not context-free. The recent standards adds several clarifications, so that most of these qualifiers are redundant now, at least at places where it is unambiguously clear that only a type can be given. GCC already supports most of these relaxing rules (Clang unfortunately lags way behind with support of newer language features...)
228 lines
7.5 KiB
C++
228 lines
7.5 KiB
C++
/*
|
||
FunctionErasure(Test) - verify the wrapping of functor object with type erasure
|
||
|
||
Copyright (C)
|
||
2009, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** 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. See the file COPYING for further details.
|
||
|
||
* *****************************************************************/
|
||
|
||
/** @file function-erasure-test.cpp
|
||
** unit test \ref FunctionErasure_test
|
||
*/
|
||
|
||
|
||
#include "lib/test/run.hpp"
|
||
#include "lib/test/test-helper.hpp"
|
||
#include "lib/meta/function-erasure.hpp"
|
||
#include "lib/error.hpp"
|
||
#include "meta/dummy-functions.hpp"
|
||
|
||
#include <functional>
|
||
|
||
using ::test::Test;
|
||
using lumiera::error::LUMIERA_ERROR_ASSERTION;
|
||
|
||
|
||
|
||
namespace lib {
|
||
namespace meta {
|
||
namespace test {
|
||
|
||
using std::function;
|
||
using std::placeholders::_1;
|
||
using std::placeholders::_2;
|
||
using std::bind;
|
||
|
||
|
||
typedef FunErasure<StoreFunction> Efun;
|
||
typedef FunErasure<StoreFunPtr> Efp;
|
||
typedef FunErasure<StoreUncheckedFunPtr> Evoid;
|
||
|
||
template<class HOL>
|
||
struct BuildEmptyFunctor { typedef long (*Type)(int,char); };
|
||
template<>
|
||
struct BuildEmptyFunctor<Efun> { typedef function<long(int,char)> Type; };
|
||
|
||
|
||
/*******************************************************************//**
|
||
* @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)
|
||
{
|
||
function<void(int,char)> bindFunc = bind (testFunc,_1,_2);
|
||
function<void(int )> pAplFunc = bind (testFunc,_1,'x');
|
||
function<void( char)> membFunc = bind (&FunctionErasure_test::testMemberFunction,this, _1);
|
||
|
||
function<int(void)> getterFunc = &returnIt;
|
||
|
||
check_FunctorContainer( Efun (testFunc)
|
||
, Efun (bindFunc)
|
||
, Efun (pAplFunc)
|
||
, Efun (membFunc)
|
||
, Efun (getterFunc)
|
||
);
|
||
|
||
check_FunctPtrHolder(Efp(testFunc),Efp(&testFunc), Efp(returnIt));
|
||
check_VoidPtrHolder(Evoid(testFunc),Evoid(&testFunc),Evoid(returnIt));
|
||
|
||
detect_unboundFunctor(Efun(testFunc), Efun(getterFunc), Efun(membFunc));
|
||
detect_unboundFunctor(Efp(testFunc),Efp(&testFunc), Efp(returnIt));
|
||
detect_unboundFunctor(Evoid(testFunc),Evoid(&testFunc),Evoid(returnIt));
|
||
}
|
||
|
||
void
|
||
testMemberFunction (char c) ///< for checking bind-to member function
|
||
{
|
||
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'); // invoke stored std::function...
|
||
CHECK (_sum_ == 'M'-11);
|
||
|
||
_sum_ = 0;
|
||
f2.getFun<Sig1>() (-22,'M');
|
||
CHECK (_sum_ == 'M'-22);
|
||
|
||
_sum_ = 0;
|
||
f3.getFun<Sig2>() (-33);
|
||
CHECK (_sum_ == 'x'-33);
|
||
|
||
_sum_ = 0;
|
||
f4.getFun<Sig3>() ('U');
|
||
CHECK (_sum_ == 'u');
|
||
|
||
CHECK ( 'u' == f5.getFun<Sig4>() () );
|
||
CHECK (INSTANCEOF (function<Sig4>, &f5.getFun<Sig4>()));
|
||
|
||
|
||
#if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : 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 ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
|
||
}
|
||
|
||
|
||
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'); // invoke retrieved function pointer
|
||
CHECK (_sum_ == 10+'a');
|
||
|
||
(*fun2) (20, 'b');
|
||
CHECK (_sum_ == 10+'a'+20+'b');
|
||
|
||
fun2r (30, 'c');
|
||
CHECK (_sum_ == 10+'a'+20+'b'+30+'c');
|
||
|
||
CHECK (_sum_ == (f3.getFun<int(void)>()) () );
|
||
}
|
||
|
||
|
||
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');
|
||
CHECK (_sum_ == 10+'a');
|
||
|
||
(*fun2) (20, 'b');
|
||
CHECK (_sum_ == 10+'a'+20+'b');
|
||
|
||
fun2r (30, 'c');
|
||
CHECK (_sum_ == 10+'a'+20+'b'+30+'c');
|
||
|
||
CHECK (_sum_ == (f3.getFun<int(void)>()) () );
|
||
|
||
FP bad_fun = &f3.getFun<void(int,char)>();
|
||
CHECK ((void*)bad_fun == &returnIt); // got wrong function!
|
||
|
||
//(*bad_fun) (11, 'x'); // The compiler would accept this line!
|
||
} // likely to result in heap corruption or SEGV
|
||
|
||
|
||
template<class HOL>
|
||
void
|
||
detect_unboundFunctor (HOL h1, HOL h2, HOL h3)
|
||
{
|
||
// fabricate a suitable, unbound functor to wrap...
|
||
using NoFunc = BuildEmptyFunctor<HOL>::Type;
|
||
NoFunc noFunction = NoFunc();
|
||
|
||
// wrap this (actually empty) functor into the holder type
|
||
HOL emptyHolder (noFunction);
|
||
|
||
// verify the older detects that the wrapped functor is empty
|
||
CHECK (!emptyHolder);
|
||
|
||
// cross-verify that non-empty functors are not flagged as empty
|
||
CHECK ( h1 );
|
||
CHECK ( h2 );
|
||
CHECK ( h3 );
|
||
}
|
||
|
||
|
||
};
|
||
|
||
|
||
/** Register this test class... */
|
||
LAUNCHER (FunctionErasure_test, "unit common");
|
||
|
||
|
||
|
||
}}} // namespace lib::meta::test
|