[Orxonox-commit 6329] r10986 - in code/branches/cpp11_v2: src/libraries/core/command test/core/command
landauf at orxonox.net
landauf at orxonox.net
Mon Dec 28 18:57:26 CET 2015
Author: landauf
Date: 2015-12-28 18:57:26 +0100 (Mon, 28 Dec 2015)
New Revision: 10986
Modified:
code/branches/cpp11_v2/src/libraries/core/command/Functor.h
code/branches/cpp11_v2/test/core/command/FunctorTest.cc
Log:
use the existing class 'FunctorPointer' to store the callable object. functors with callable objects (e.g. lambdas) are now treated as static functors.
this also fixes an issue on gcc: because of a bug, gcc was not able to store the function pointer of a lambda's operator() [https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51048]. storing the whole object is apparently a workaround for this issue.
Note: tests currently fail on MSVC because it doesn't accept std::function as a callable object.
Modified: code/branches/cpp11_v2/src/libraries/core/command/Functor.h
===================================================================
--- code/branches/cpp11_v2/src/libraries/core/command/Functor.h 2015-12-27 22:43:12 UTC (rev 10985)
+++ code/branches/cpp11_v2/src/libraries/core/command/Functor.h 2015-12-28 17:57:26 UTC (rev 10986)
@@ -356,14 +356,14 @@
typedef FunctorMember<void> FunctorStatic;
/**
- @brief FunctorPointer is a child class of FunctorMember and expands it with a function-pointer.
- @param F The type of the function-pointer
- @param O The type of the function's class (or void if it's a static function)
+ @brief FunctorPointer is a child class of FunctorMember and extends it with a function-pointer (or a function-object).
+ @param F The type of the function-pointer (or the function-object)
+ @param O The type of the function's class (or void if it's a static function or a function-object)
The template FunctorPointer has an additional template parameter that defines the type
- of the function-pointer. This can be handy if you want to get or set the function-pointer.
- You can then use a static_cast to cast a Functor to FunctorPointer if you know the type
- of the function-pointer.
+ of the function-pointer (or the function-object). This can be handy if you want to get
+ or set the function-pointer (or the function-object). You can then use a static_cast
+ to cast a Functor to FunctorPointer if you know the type of the function.
However FunctorPointer is not aware of the types of the different parameters or the
return value.
@@ -382,26 +382,27 @@
inline F getFunction() const
{ return this->functionPointer_; }
- // see Functor::getFullIdentifier()
- virtual const std::type_info& getFullIdentifier() const override
- { return typeid(F); }
-
protected:
F functionPointer_; ///< The stored function-pointer
};
namespace detail
{
- // Helper class to get the type of the function pointer with the given class, parameters, return-value, and constness
- template <class R, class O, bool isconst, class... Params> struct FunctionPointer { typedef R (O::*Type)(Params...); };
- template <class R, class O, class... Params> struct FunctionPointer<R, O, true, Params...> { typedef R (O::*Type)(Params...) const; };
- template <class R, class... Params> struct FunctionPointer<R, void, false, Params...> { typedef R (*Type)(Params...); };
+ // Helper class to get the type of the function-pointer (or the function-object) with the given class, parameters, return-value, and constness
+ template <class F, class R, class O, bool isconst, class... Params> struct FunctionType /* generic type is undefined */;
+ template < class R, class O, class... Params> struct FunctionType<void, R, O, false, Params...> { typedef R (O::*Type)(Params...); }; // spezialization: non-const member function
+ template < class R, class O, class... Params> struct FunctionType<void, R, O, true, Params...> { typedef R (O::*Type)(Params...) const; }; // spezialization: const member function
+ template < class R, class... Params> struct FunctionType<void, R, void, false, Params...> { typedef R (*Type)(Params...); }; // spezialization: static function
+ template <class F, class R, class... Params> struct FunctionType<F, R, void, false, Params...> { typedef F Type; }; // spezialization: function object
- // Helper class, used to call a function-pointer with a given object and parameters and to return its return-value (if available)
- template <class R, class O, bool isconst, class... Params> struct FunctorCaller { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionPointer<R, O, isconst, Params...>::Type functionPointer, O* object, const Params&... parameters, const UnusedParams&...) { return (object->*functionPointer)(parameters...); } };
- template <class O, bool isconst, class... Params> struct FunctorCaller<void, O, isconst, Params...> { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionPointer<void, O, isconst, Params...>::Type functionPointer, O* object, const Params&... parameters, const UnusedParams&...) { (object->*functionPointer)(parameters...); return MultiType::Null; } };
- template <class R, bool isconst, class... Params> struct FunctorCaller<R, void, isconst, Params...> { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionPointer<R, void, isconst, Params...>::Type functionPointer, void*, const Params&... parameters, const UnusedParams&...) { return (*functionPointer)(parameters...); } };
- template <bool isconst, class... Params> struct FunctorCaller<void, void, isconst, Params...> { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionPointer<void, void, isconst, Params...>::Type functionPointer, void*, const Params&... parameters, const UnusedParams&...) { (*functionPointer)(parameters...); return MultiType::Null; } };
+ // Helper class, used to call a function with a given object and parameters and to return its return-value (if available)
+ template <class F, class R, class O, bool isconst, class... Params> struct FunctorCaller /* generic type is undefined */;
+ template < class R, class O, bool isconst, class... Params> struct FunctorCaller<void, R, O, isconst, Params...> { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionType<void, R, O, isconst, Params...>::Type functionPointer, O* object, const Params&... parameters, const UnusedParams&...) { return (object->*functionPointer)(parameters...); } }; // spezialization: member function with return value
+ template < class O, bool isconst, class... Params> struct FunctorCaller<void, void, O, isconst, Params...> { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionType<void, void, O, isconst, Params...>::Type functionPointer, O* object, const Params&... parameters, const UnusedParams&...) { (object->*functionPointer)(parameters...); return MultiType::Null; } }; // spezialization: member function without return value
+ template < class R, class... Params> struct FunctorCaller<void, R, void, false, Params...> { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionType<void, R, void, false, Params...>::Type functionPointer, void*, const Params&... parameters, const UnusedParams&...) { return (*functionPointer)(parameters...); } }; // spezialization: static function with return value
+ template < class... Params> struct FunctorCaller<void, void, void, false, Params...> { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionType<void, void, void, false, Params...>::Type functionPointer, void*, const Params&... parameters, const UnusedParams&...) { (*functionPointer)(parameters...); return MultiType::Null; } }; // spezialization: static function without return value
+ template <class F, class R, class... Params> struct FunctorCaller<F, R, void, false, Params...> { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionType<F, R, void, false, Params...>::Type functor, void*, const Params&... parameters, const UnusedParams&...) { return functor(parameters...); } }; // spezialization: function object with return value
+ template <class F, class... Params> struct FunctorCaller<F, void, void, false, Params...> { template <class... UnusedParams> static inline MultiType call(typename detail::FunctionType<F, void, void, false, Params...>::Type functor, void*, const Params&... parameters, const UnusedParams&...) { functor(parameters...); return MultiType::Null; } }; // spezialization: function object without return value
// Helper class to determine if a function has a returnvalue
template <class T>
@@ -456,6 +457,7 @@
@brief FunctorTemplate is a child class of FunctorPointer and implements all functions
that need to know the exact types of the parameters, return-value, and class.
+ @param F the type of the function-object (or void if a function-pointer is used).
@param R The type of the return-value of the function
@param O The class of the function
@param isconst True if the function is a const member-function
@@ -468,19 +470,19 @@
All template arguments can be void.
*/
- template <class R, class O, bool isconst, class... Params>
- class FunctorTemplate : public FunctorPointer<typename detail::FunctionPointer<R, O, isconst, Params...>::Type, O>
+ template <class F, class R, class O, bool isconst, class... Params>
+ class FunctorTemplate : public FunctorPointer<typename detail::FunctionType<F, R, O, isconst, Params...>::Type, O>
{
static_assert(sizeof...(Params) <= 5, "Only up to 5 parameters are supported");
public:
/// Constructor: Initializes the base class.
- FunctorTemplate(typename detail::FunctionPointer<R, O, isconst, Params...>::Type functionPointer, O* object = nullptr) : FunctorPointer<typename detail::FunctionPointer<R, O, isconst, Params...>::Type, O>(functionPointer, object) {}
+ FunctorTemplate(typename detail::FunctionType<F, R, O, isconst, Params...>::Type functionPointer, O* object = nullptr) : FunctorPointer<typename detail::FunctionType<F, R, O, isconst, Params...>::Type, O>(functionPointer, object) {}
// see FunctorMember::operator()()
virtual MultiType operator()(O* object, const MultiType& param1 = MultiType::Null, const MultiType& param2 = MultiType::Null, const MultiType& param3 = MultiType::Null, const MultiType& param4 = MultiType::Null, const MultiType& param5 = MultiType::Null) override
{
- return detail::FunctorCaller<R, O, isconst, Params...>::call(this->functionPointer_, object, param1, param2, param3, param4, param5);
+ return detail::FunctorCaller<F, R, O, isconst, Params...>::call(this->functionPointer_, object, param1, param2, param3, param4, param5);
}
// see Functor::clone()
@@ -534,10 +536,16 @@
return typeToString<R>();
}
+ // see Functor::getFullIdentifier()
+ virtual const std::type_info& getFullIdentifier() const override
+ {
+ return typeid(typename detail::FunctionType<void, R, O, isconst, Params...>::Type);
+ }
+
// see Functor::getHeaderIdentifier()
virtual const std::type_info& getHeaderIdentifier() const override
{
- return typeid(typename detail::FunctionPointer<R, void, false, Params...>::Type);
+ return typeid(typename detail::FunctionType<void, R, void, false, Params...>::Type);
}
// see Functor::getHeaderIdentifier(unsigned int)
@@ -559,54 +567,28 @@
template<class... Types>
const std::type_info& getTypelistIdentifier(detail::type_list<Types...>) const
{
- return typeid(typename detail::FunctionPointer<R, void, false, Types...>::Type);
+ return typeid(typename detail::FunctionType<void, R, void, false, Types...>::Type);
}
};
- /**
- @brief FunctorCallable is a child class of FunctorTemplate. It stores a callable
- object (e.g. a lambda or a class with operator()) inside and acts like any
- other functor. Note that it stores a \em copy of the object, not a reference or a pointer.
- Take care that this functor does not outlive objects that have been captured by reference
- in a lambda.
-
- @param F The type of the callable object
- @param R The type of the return-value of the function
- @param isconst True if operator() is const
- @param Params The types of the parameters
-
- This template can not be used directly when using lambdas - the type of a lambda
- is not specified. It can only really be used through the base-class Functor.
- */
- template <class F, class R, bool isconst, class... Params>
- class FunctorCallable : public FunctorTemplate<R, F, isconst, Params...>
- {
- public:
- FunctorCallable(const F& obj): FunctorTemplate<R, F, isconst, Params...>(&F::operator(), &obj_)
- , obj_(obj)
- {}
-
- private:
- F obj_; ///< The callable object
- };
-
namespace detail
{
//Helper functions to deduce types and constness of operator() and return the correct FunctorCallable
- template <class F, class R, class... Params> inline FunctorMemberPtr<F> callableHelper(const F& obj, R(F::*func)(Params...) const) { return std::make_shared<FunctorCallable<F, R, true, Params...>>(obj); }
- template <class F, class R, class... Params> inline FunctorMemberPtr<F> callableHelper(const F& obj, R(F::*func)(Params...)) { return std::make_shared<FunctorCallable<F, R, false, Params...>>(obj); }
+ template <class F, class R, class... Params> inline FunctorStaticPtr callableHelper(const F& functionObject, R(F::*)(Params...)) { return std::make_shared<FunctorTemplate<F, R, void, false, Params...>>(functionObject); }
+ template <class F, class R, class... Params> inline FunctorStaticPtr callableHelper(const F& functionObject, R(F::*)(Params...) const) { return std::make_shared<FunctorTemplate<F, R, void, false, Params...>>(functionObject); } // note: both const and non-const function-objects are treated as static functors with isconst=false.
}
- template <class R, class O, class OO, class... Params> inline FunctorMemberPtr<O> createFunctor(R (O::*functionPointer)(Params...), OO* object) { return std::make_shared<FunctorTemplate<R, O, false, Params...>>(functionPointer, object); } ///< Creates a new FunctorMember with the given function-pointer and an assigned object
- template <class R, class O, class OO, class... Params> inline FunctorMemberPtr<O> createFunctor(R (O::*functionPointer)(Params...) const, OO* object) { return std::make_shared<FunctorTemplate<R, O, true, Params...>>(functionPointer, object); } ///< Creates a new FunctorMember with the given function-pointer and an assigned object
+ template <class R, class O, class OO, class... Params> inline FunctorMemberPtr<O> createFunctor(R (O::*functionPointer)(Params...), OO* object) { return std::make_shared<FunctorTemplate<void, R, O, false, Params...>>(functionPointer, object); } ///< Creates a new FunctorMember with the given function-pointer and an assigned object
+ template <class R, class O, class OO, class... Params> inline FunctorMemberPtr<O> createFunctor(R (O::*functionPointer)(Params...) const, OO* object) { return std::make_shared<FunctorTemplate<void, R, O, true, Params...>>(functionPointer, object); } ///< Creates a new FunctorMember with the given function-pointer and an assigned object
- template <class R, class O, class... Params> inline FunctorMemberPtr<O> createFunctor(R (O::*functionPointer)(Params...)) { return std::make_shared<FunctorTemplate<R, O, false, Params...>>(functionPointer); } ///< Creates a new FunctorMember with the given function-pointer
- template <class R, class O, class... Params> inline FunctorMemberPtr<O> createFunctor(R (O::*functionPointer)(Params...) const) { return std::make_shared<FunctorTemplate<R, O, true, Params...>>(functionPointer); } ///< Creates a new FunctorMember with the given function-pointer
+ template <class R, class O, class... Params> inline FunctorMemberPtr<O> createFunctor(R (O::*functionPointer)(Params...)) { return std::make_shared<FunctorTemplate<void, R, O, false, Params...>>(functionPointer); } ///< Creates a new FunctorMember with the given function-pointer
+ template <class R, class O, class... Params> inline FunctorMemberPtr<O> createFunctor(R (O::*functionPointer)(Params...) const) { return std::make_shared<FunctorTemplate<void, R, O, true, Params...>>(functionPointer); } ///< Creates a new FunctorMember with the given function-pointer
- template <class R, class... Params> inline FunctorStaticPtr createFunctor(R (*functionPointer)(Params...)) { return std::make_shared<FunctorTemplate<R, void, false, Params...>>(functionPointer); } ///< Creates a new FunctorStatic with the given function-pointer
-
- template <class F> inline FunctorMemberPtr<F> createFunctor(const F& obj) { return detail::callableHelper(obj, &F::operator()); } ///< Creates a new Functor with a callable object
+ template <class R, class... Params> inline FunctorStaticPtr createFunctor(R (*functionPointer)(Params...)) { return std::make_shared<FunctorTemplate<void, R, void, false, Params...>>(functionPointer); } ///< Creates a new FunctorStatic with the given function-pointer
+
+ /** Take care that this functor does not outlive objects that have been captured by reference in a lambda. */
+ template <class F> inline FunctorStaticPtr createFunctor(const F& functionObject) { return detail::callableHelper(functionObject, &F::operator()); } ///< Creates a new Functor with a callable object
}
#endif /* _Functor_H__ */
Modified: code/branches/cpp11_v2/test/core/command/FunctorTest.cc
===================================================================
--- code/branches/cpp11_v2/test/core/command/FunctorTest.cc 2015-12-27 22:43:12 UTC (rev 10985)
+++ code/branches/cpp11_v2/test/core/command/FunctorTest.cc 2015-12-28 17:57:26 UTC (rev 10986)
@@ -70,6 +70,7 @@
int f3(int, double, double) { return 0; }
int f4a(int arg) { return arg*2; }
int f4b(int arg) { return arg*3; }
+ int multiply(int a, int b) { return a*b; }
struct CallableStruct
{
@@ -633,6 +634,106 @@
////////////////////////////// Various tests //////////////////////////////
+ TEST_F(FunctorTest, CreateFunctorWithVariousFunctionTypes)
+ {
+ FunctorPtr c1 = createFunctor(&f4a);
+ FunctorPtr c2 = createFunctor(&f4b);
+ FunctorPtr c3 = createFunctor([] (int arg) { return arg*4; });
+ auto lambda = [] (int arg1, int arg2) { return arg1*arg2; };
+ FunctorPtr c4 = createFunctor(lambda);
+
+ std::function<int()> bind0 = std::bind(multiply, 4, 6);
+ std::function<int(int)> bind1 = std::bind(multiply, std::placeholders::_1, 7);
+ std::function<int(int, int)> bind2 = std::bind(multiply, std::placeholders::_1, std::placeholders::_2);
+ std::function<int(int)> function = f4a;
+
+ FunctorPtr c5 = createFunctor(bind0);
+ FunctorPtr c6 = createFunctor(bind1);
+ FunctorPtr c7 = createFunctor(bind2);
+ FunctorPtr c8 = createFunctor(function);
+
+
+ EXPECT_EQ(8, (*c1)(4).get<int>());
+ EXPECT_EQ(12, (*c2)(4).get<int>());
+ EXPECT_EQ(16, (*c3)(4).get<int>());
+ EXPECT_EQ(20, (*c4)(4, 5).get<int>());
+ EXPECT_EQ(24, (*c5)(4).get<int>());
+ EXPECT_EQ(28, (*c6)(4).get<int>());
+ EXPECT_EQ(32, (*c7)(4, 8).get<int>());
+ EXPECT_EQ(8, (*c8)(4).get<int>());
+
+ EXPECT_EQ(1, c1->getParamCount());
+ EXPECT_EQ(1, c2->getParamCount());
+ EXPECT_EQ(1, c3->getParamCount());
+ EXPECT_EQ(2, c4->getParamCount());
+ EXPECT_EQ(0, c5->getParamCount());
+ EXPECT_EQ(1, c6->getParamCount());
+ EXPECT_EQ(2, c7->getParamCount());
+ EXPECT_EQ(1, c8->getParamCount());
+
+ EXPECT_EQ(typeid(int(*)(int)), c1->getFullIdentifier());
+ EXPECT_EQ(typeid(int(*)(int)), c2->getFullIdentifier());
+ EXPECT_EQ(typeid(int(*)(int)), c3->getFullIdentifier());
+ EXPECT_EQ(typeid(int(*)(int, int)), c4->getFullIdentifier());
+ EXPECT_EQ(typeid(int(*)()), c5->getFullIdentifier());
+ EXPECT_EQ(typeid(int(*)(int)), c6->getFullIdentifier());
+ EXPECT_EQ(typeid(int(*)(int, int)), c7->getFullIdentifier());
+ EXPECT_EQ(typeid(int(*)(int)), c8->getFullIdentifier());
+
+ EXPECT_EQ(typeid(int(*)(int)), c1->getHeaderIdentifier());
+ EXPECT_EQ(typeid(int(*)(int)), c2->getHeaderIdentifier());
+ EXPECT_EQ(typeid(int(*)(int)), c3->getHeaderIdentifier());
+ EXPECT_EQ(typeid(int(*)(int, int)), c4->getHeaderIdentifier());
+ EXPECT_EQ(typeid(int(*)()), c5->getHeaderIdentifier());
+ EXPECT_EQ(typeid(int(*)(int)), c6->getHeaderIdentifier());
+ EXPECT_EQ(typeid(int(*)(int, int)), c7->getHeaderIdentifier());
+ EXPECT_EQ(typeid(int(*)(int)), c8->getHeaderIdentifier());
+
+ EXPECT_EQ(typeid(int(*)(int)), c1->getHeaderIdentifier(1));
+ EXPECT_EQ(typeid(int(*)(int)), c2->getHeaderIdentifier(1));
+ EXPECT_EQ(typeid(int(*)(int)), c3->getHeaderIdentifier(1));
+ EXPECT_EQ(typeid(int(*)(int)), c4->getHeaderIdentifier(1));
+ EXPECT_EQ(typeid(int(*)()), c5->getHeaderIdentifier(1));
+ EXPECT_EQ(typeid(int(*)(int)), c6->getHeaderIdentifier(1));
+ EXPECT_EQ(typeid(int(*)(int)), c7->getHeaderIdentifier(1));
+ EXPECT_EQ(typeid(int(*)(int)), c8->getHeaderIdentifier(1));
+
+ EXPECT_EQ(typeid(int(*)()), c1->getHeaderIdentifier(0));
+ EXPECT_EQ(typeid(int(*)()), c2->getHeaderIdentifier(0));
+ EXPECT_EQ(typeid(int(*)()), c3->getHeaderIdentifier(0));
+ EXPECT_EQ(typeid(int(*)()), c4->getHeaderIdentifier(0));
+ EXPECT_EQ(typeid(int(*)()), c5->getHeaderIdentifier(0));
+ EXPECT_EQ(typeid(int(*)()), c6->getHeaderIdentifier(0));
+ EXPECT_EQ(typeid(int(*)()), c7->getHeaderIdentifier(0));
+ EXPECT_EQ(typeid(int(*)()), c8->getHeaderIdentifier(0));
+ }
+
+ TEST_F(FunctorTest, CanCallFunctorWithLambdaWichIsAlreadyOutOfScope)
+ {
+ int var1 = 1;
+ FunctorPtr fp1a = createFunctor([&var1] () { return var1++; });
+ FunctorPtr fp1b = createFunctor([&var1] () { return var1; });
+ FunctorPtr fp2;
+ FunctorPtr fp3;
+
+ {
+ int var2 = 2;
+ fp2 = createFunctor([var2] () { return var2; });
+ }
+ {
+ int var3 = 3;
+ fp3 = createFunctor([var3] () { return var3; });
+ }
+
+ EXPECT_EQ(1, var1);
+ EXPECT_EQ(1, (*fp1a)().get<int>());
+ EXPECT_EQ(2, var1);
+ EXPECT_EQ(2, (*fp1b)().get<int>());
+
+ EXPECT_EQ(2, (*fp2)().get<int>());
+ EXPECT_EQ(3, (*fp3)().get<int>());
+ }
+
TEST_F(FunctorTest, canCompareHeaderIdentifiers)
{
FunctorPtr fp1 = createFunctor(&f1);
More information about the Orxonox-commit
mailing list