[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