[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