[Orxonox-commit 6318] r10975 - in code/branches/cpp11_v2: src/libraries/core/command test/core/command

muemart at orxonox.net muemart at orxonox.net
Wed Dec 23 19:16:33 CET 2015


Author: muemart
Date: 2015-12-23 19:16:33 +0100 (Wed, 23 Dec 2015)
New Revision: 10975

Modified:
   code/branches/cpp11_v2/src/libraries/core/command/Functor.h
   code/branches/cpp11_v2/test/core/command/FunctorTest.cc
Log:
Extend Functor to handle any callable objects (lambdas!). Had to modify (un)registerObject, because apparently you can't dynamic_cast lambdas. Also wanted to perfect forward the object from createFunctor until obj_, but MSVC didn't like that.
Including new and improved Functor tests.

Modified: code/branches/cpp11_v2/src/libraries/core/command/Functor.h
===================================================================
--- code/branches/cpp11_v2/src/libraries/core/command/Functor.h	2015-12-14 10:31:30 UTC (rev 10974)
+++ code/branches/cpp11_v2/src/libraries/core/command/Functor.h	2015-12-23 18:16:33 UTC (rev 10975)
@@ -117,6 +117,7 @@
 
 #include <array>
 #include <typeindex>
+#include <type_traits>
 #include <tuple>
 
 #include "util/Output.h"
@@ -302,14 +303,23 @@
             }
 
         protected:
-            /// Casts the object and registers as destruction listener.
+            /// Casts the object and registers as destruction listener if the object is a Destroyable.
+            template<bool = std::is_base_of<Destroyable, O>::value>
             inline void registerObject(O* object)
-                { Destroyable* base = dynamic_cast<Destroyable*>(object); if (base) { this->registerAsDestructionListener(base); } }
-            /// Casts the object and unregisters as destruction listener.
+                { this->registerAsDestructionListener(static_cast<Destroyable*>(object)); }
+
+            template<>
+            inline void registerObject<false>(O* object) {}
+
+            /// Casts the object and unregisters as destruction listener if the object is a Destroyable.
+            template<bool = std::is_base_of<Destroyable, O>::value>
             inline void unregisterObject(O* object)
-                { Destroyable* base = dynamic_cast<Destroyable*>(object); if (base) { this->unregisterAsDestructionListener(base); } }
+                { this->unregisterAsDestructionListener(static_cast<Destroyable*>(object)); }
 
-            /// Will be called by Destroyable::~Destroyable() if the stored object is deleted and the Functor is in safe mode.
+            template<>
+            inline void unregisterObject<false>(O* object) {}
+
+            /// Will be called by Destroyable::~Destroyable() if the stored object is a Destroyable and deleted and the Functor is in safe mode.
             virtual inline void objectDeleted() override
                 { this->object_ = nullptr; }
 
@@ -449,11 +459,7 @@
         @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
-        @param P1 The type of the first parameter
-        @param P2 The type of the second parameter
-        @param P3 The type of the third parameter
-        @param P4 The type of the fourth parameter
-        @param P5 The type of the fifth parameter
+        @param Params The types of the parameters
 
         This template has many parameters and is usually not used directly. It is created by
         createFunctor(), but only the base-classes Functor, FunctorMember, and FunctorPointer
@@ -565,6 +571,41 @@
             }
     };
 
+
+    /**
+    @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 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
 
@@ -572,6 +613,8 @@
     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... Params> inline FunctorStaticPtr createFunctor(R (*functionPointer)(Params...)) { return std::make_shared<FunctorTemplate<R, void, false, Params...>>(functionPointer); }   ///< Creates a new Functor 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
 }
 
 #endif /* _Functor_H__ */

Modified: code/branches/cpp11_v2/test/core/command/FunctorTest.cc
===================================================================
--- code/branches/cpp11_v2/test/core/command/FunctorTest.cc	2015-12-14 10:31:30 UTC (rev 10974)
+++ code/branches/cpp11_v2/test/core/command/FunctorTest.cc	2015-12-23 18:16:33 UTC (rev 10975)
@@ -18,6 +18,12 @@
                 }
         };
 
+        class DestroyableClass : public Destroyable
+        {
+	        public:
+	            void method() {};
+        };
+
         void f1(int, double)
         {
         }
@@ -30,6 +36,15 @@
         {
             return 0;
         }
+
+        struct CallableStruct
+        {
+	        public:
+	            int operator()()
+	            {
+	                return 1;
+	            }
+        };
     }
 
     TEST_F(FunctorTest, canCompareHeaderIdentifiers)
@@ -37,13 +52,13 @@
         FunctorPtr fp1 = createFunctor(&f1);
         FunctorPtr fp2 = createFunctor(&f2);
         FunctorPtr fp3 = createFunctor(&f3);
-        ASSERT_TRUE(fp1->getHeaderIdentifier(1) == fp2->getHeaderIdentifier(1));
-        ASSERT_TRUE(fp1->getHeaderIdentifier(2) == fp2->getHeaderIdentifier(2));
-        ASSERT_FALSE(fp1->getHeaderIdentifier(1) == fp2->getHeaderIdentifier(2));
-        ASSERT_FALSE(fp1->getHeaderIdentifier(10) == fp2->getHeaderIdentifier(10));
-        ASSERT_FALSE(fp2->getHeaderIdentifier(2) == fp3->getHeaderIdentifier(2));
-        ASSERT_FALSE(fp1->getHeaderIdentifier() == fp2->getHeaderIdentifier());
-        ASSERT_FALSE(fp2->getHeaderIdentifier() == fp3->getHeaderIdentifier());
+        ASSERT_STREQ(fp1->getHeaderIdentifier(1).name(), fp2->getHeaderIdentifier(1).name());
+        ASSERT_STREQ(fp1->getHeaderIdentifier(2).name(), fp2->getHeaderIdentifier(2).name());
+        ASSERT_STRNE(fp1->getHeaderIdentifier(1).name(), fp2->getHeaderIdentifier(2).name());
+        ASSERT_STRNE(fp1->getHeaderIdentifier(10).name(), fp2->getHeaderIdentifier(10).name());
+        ASSERT_STRNE(fp2->getHeaderIdentifier(2).name(), fp3->getHeaderIdentifier(2).name());
+        ASSERT_STRNE(fp1->getHeaderIdentifier().name(), fp2->getHeaderIdentifier().name());
+        ASSERT_STRNE(fp2->getHeaderIdentifier().name(), fp3->getHeaderIdentifier().name());
     }
 
     TEST_F(FunctorTest, canCompareTypenames)
@@ -51,8 +66,8 @@
         FunctorPtr fp1 = createFunctor(&f1);
         FunctorPtr fp2 = createFunctor(&f2);
         FunctorPtr fp3 = createFunctor(&f3);
-        ASSERT_TRUE(fp1->getTypenameReturnvalue() == fp2->getTypenameReturnvalue());
-        ASSERT_TRUE(fp1->getTypenameParam(0) == fp3->getTypenameParam(0));
+        ASSERT_EQ(fp1->getTypenameReturnvalue(), fp2->getTypenameReturnvalue());
+        ASSERT_EQ(fp1->getTypenameParam(0), fp3->getTypenameParam(0));
         ASSERT_EQ("void", fp1->getTypenameReturnvalue());
         ASSERT_EQ("int", fp3->getTypenameReturnvalue());
         ASSERT_EQ("int", fp2->getTypenameParam(0));
@@ -66,7 +81,7 @@
         FunctorPtr fp2 = createFunctor(&f2);
         FunctorPtr fp3 = createFunctor(&f3);
         ASSERT_EQ(3, fp2->getParamCount());
-        ASSERT_FALSE(fp1->getParamCount() == fp3->getParamCount());
+        ASSERT_NE(fp1->getParamCount(), fp3->getParamCount());
         ASSERT_FALSE(fp2->hasReturnvalue());
         ASSERT_TRUE(fp3->hasReturnvalue());
     }
@@ -85,4 +100,46 @@
         fp1->evaluateArgument(5, mttype);
         ASSERT_TRUE(mttype.null());
     }
+
+    TEST_F(FunctorTest, canUseCallables)
+    {
+        int a = 0;
+        FunctorPtr fp1 = createFunctor(CallableStruct{});
+        FunctorPtr fp2 = createFunctor([](bool val) { return val; });
+        FunctorPtr fp3 = createFunctor([&a]() {return a++; });
+        FunctorPtr fp4 = createFunctor([a]() {return a; });
+        ASSERT_EQ(1, (*fp1)().get<int>());
+        ASSERT_EQ(true, (*fp2)(true).get<bool>());
+        ASSERT_EQ(0, (*fp3)().get<int>());
+        ASSERT_EQ(1, a);
+        ASSERT_EQ(0, (*fp4)().get<int>());
+        ASSERT_STREQ(fp1->getHeaderIdentifier().name(), fp3->getHeaderIdentifier().name());
+    }
+
+    TEST_F(FunctorTest, SafeModeWorks)
+    {
+        DestroyableClass* testclass = new DestroyableClass();
+        DestroyableClass* testclass2 = new DestroyableClass();
+        FunctorPtr fp1 = createFunctor(&DestroyableClass::method, testclass);
+        fp1->setSafeMode(true);
+        FunctorPtr fp2 = createFunctor(&DestroyableClass::method, testclass);
+        fp2->setSafeMode(true);
+        FunctorPtr fp3 = createFunctor(&DestroyableClass::method, testclass);
+        fp2->setRawObjectPointer(testclass2);
+
+        ASSERT_NE(nullptr, fp1->getRawObjectPointer());
+        ASSERT_NE(nullptr, fp2->getRawObjectPointer());
+        ASSERT_NE(nullptr, fp3->getRawObjectPointer());
+        testclass->destroy();
+        ASSERT_EQ(nullptr, fp1->getRawObjectPointer());
+        ASSERT_NE(nullptr, fp2->getRawObjectPointer());
+        ASSERT_NE(nullptr, fp3->getRawObjectPointer());
+
+        fp3->setRawObjectPointer(testclass2);
+        fp3->setSafeMode(true);
+        fp2->setSafeMode(false);
+        testclass2->destroy();
+        ASSERT_NE(nullptr, fp2->getRawObjectPointer());
+        ASSERT_EQ(nullptr, fp3->getRawObjectPointer());
+    }
 }




More information about the Orxonox-commit mailing list