Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Dec 23, 2015, 7:16:33 PM (9 years ago)
Author:
muemart
Message:

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.

Location:
code/branches/cpp11_v2
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • code/branches/cpp11_v2/src/libraries/core/command/Functor.h

    r10876 r10975  
    118118#include <array>
    119119#include <typeindex>
     120#include <type_traits>
    120121#include <tuple>
    121122
     
    303304
    304305        protected:
    305             /// Casts the object and registers as destruction listener.
     306            /// Casts the object and registers as destruction listener if the object is a Destroyable.
     307            template<bool = std::is_base_of<Destroyable, O>::value>
    306308            inline void registerObject(O* object)
    307                 { Destroyable* base = dynamic_cast<Destroyable*>(object); if (base) { this->registerAsDestructionListener(base); } }
    308             /// Casts the object and unregisters as destruction listener.
     309                { this->registerAsDestructionListener(static_cast<Destroyable*>(object)); }
     310
     311            template<>
     312            inline void registerObject<false>(O* object) {}
     313
     314            /// Casts the object and unregisters as destruction listener if the object is a Destroyable.
     315            template<bool = std::is_base_of<Destroyable, O>::value>
    309316            inline void unregisterObject(O* object)
    310                 { Destroyable* base = dynamic_cast<Destroyable*>(object); if (base) { this->unregisterAsDestructionListener(base); } }
    311 
    312             /// Will be called by Destroyable::~Destroyable() if the stored object is deleted and the Functor is in safe mode.
     317                { this->unregisterAsDestructionListener(static_cast<Destroyable*>(object)); }
     318
     319            template<>
     320            inline void unregisterObject<false>(O* object) {}
     321
     322            /// Will be called by Destroyable::~Destroyable() if the stored object is a Destroyable and deleted and the Functor is in safe mode.
    313323            virtual inline void objectDeleted() override
    314324                { this->object_ = nullptr; }
     
    450460        @param O The class of the function
    451461        @param isconst True if the function is a const member-function
    452         @param P1 The type of the first parameter
    453         @param P2 The type of the second parameter
    454         @param P3 The type of the third parameter
    455         @param P4 The type of the fourth parameter
    456         @param P5 The type of the fifth parameter
     462        @param Params The types of the parameters
    457463
    458464        This template has many parameters and is usually not used directly. It is created by
     
    566572    };
    567573
     574
     575    /**
     576    @brief FunctorCallable is a child class of FunctorTemplate. It stores a callable
     577    object (e.g. a lambda or a class with operator()) inside and acts like any
     578    other functor. Note that it stores a \em copy of the object, not a reference or a pointer.
     579    Take care that this functor does not outlive objects that have been captured by reference
     580    in a lambda.
     581
     582    @param F The type of the callable object
     583    @param R The type of the return-value of the function
     584    @param isconst True if operator() is const
     585    @param Params The types of the parameters
     586
     587    This template can not be used directly when using lambdas - the type of a lambda
     588    is not specified. It can only really be used through the base-class Functor.
     589    */
     590    template <class F, class R, bool isconst, class... Params>
     591    class FunctorCallable : public FunctorTemplate<R, F, isconst, Params...>
     592    {
     593    public:
     594        FunctorCallable(const F& obj): FunctorTemplate<R, F, isconst, Params...>(&F::operator(), &obj_)
     595            , obj_(obj)
     596        {}
     597
     598    private:
     599        F obj_; ///< The callable object
     600    };
     601
     602    namespace detail
     603    {
     604        //Helper functions to deduce types and constness of operator() and return the correct FunctorCallable
     605        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); }
     606        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); }
     607    }
     608
    568609    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
    569610    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
     
    573614
    574615    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
     616   
     617    template <class F> inline FunctorMemberPtr<F> createFunctor(const F& obj) { return detail::callableHelper(obj, &F::operator()); } ///< Creates a new Functor with a callable object
    575618}
    576619
  • code/branches/cpp11_v2/test/core/command/FunctorTest.cc

    r10922 r10975  
    1919        };
    2020
     21        class DestroyableClass : public Destroyable
     22        {
     23                public:
     24                    void method() {};
     25        };
     26
    2127        void f1(int, double)
    2228        {
     
    3137            return 0;
    3238        }
     39
     40        struct CallableStruct
     41        {
     42                public:
     43                    int operator()()
     44                    {
     45                        return 1;
     46                    }
     47        };
    3348    }
    3449
     
    3853        FunctorPtr fp2 = createFunctor(&f2);
    3954        FunctorPtr fp3 = createFunctor(&f3);
    40         ASSERT_TRUE(fp1->getHeaderIdentifier(1) == fp2->getHeaderIdentifier(1));
    41         ASSERT_TRUE(fp1->getHeaderIdentifier(2) == fp2->getHeaderIdentifier(2));
    42         ASSERT_FALSE(fp1->getHeaderIdentifier(1) == fp2->getHeaderIdentifier(2));
    43         ASSERT_FALSE(fp1->getHeaderIdentifier(10) == fp2->getHeaderIdentifier(10));
    44         ASSERT_FALSE(fp2->getHeaderIdentifier(2) == fp3->getHeaderIdentifier(2));
    45         ASSERT_FALSE(fp1->getHeaderIdentifier() == fp2->getHeaderIdentifier());
    46         ASSERT_FALSE(fp2->getHeaderIdentifier() == fp3->getHeaderIdentifier());
     55        ASSERT_STREQ(fp1->getHeaderIdentifier(1).name(), fp2->getHeaderIdentifier(1).name());
     56        ASSERT_STREQ(fp1->getHeaderIdentifier(2).name(), fp2->getHeaderIdentifier(2).name());
     57        ASSERT_STRNE(fp1->getHeaderIdentifier(1).name(), fp2->getHeaderIdentifier(2).name());
     58        ASSERT_STRNE(fp1->getHeaderIdentifier(10).name(), fp2->getHeaderIdentifier(10).name());
     59        ASSERT_STRNE(fp2->getHeaderIdentifier(2).name(), fp3->getHeaderIdentifier(2).name());
     60        ASSERT_STRNE(fp1->getHeaderIdentifier().name(), fp2->getHeaderIdentifier().name());
     61        ASSERT_STRNE(fp2->getHeaderIdentifier().name(), fp3->getHeaderIdentifier().name());
    4762    }
    4863
     
    5267        FunctorPtr fp2 = createFunctor(&f2);
    5368        FunctorPtr fp3 = createFunctor(&f3);
    54         ASSERT_TRUE(fp1->getTypenameReturnvalue() == fp2->getTypenameReturnvalue());
    55         ASSERT_TRUE(fp1->getTypenameParam(0) == fp3->getTypenameParam(0));
     69        ASSERT_EQ(fp1->getTypenameReturnvalue(), fp2->getTypenameReturnvalue());
     70        ASSERT_EQ(fp1->getTypenameParam(0), fp3->getTypenameParam(0));
    5671        ASSERT_EQ("void", fp1->getTypenameReturnvalue());
    5772        ASSERT_EQ("int", fp3->getTypenameReturnvalue());
     
    6782        FunctorPtr fp3 = createFunctor(&f3);
    6883        ASSERT_EQ(3, fp2->getParamCount());
    69         ASSERT_FALSE(fp1->getParamCount() == fp3->getParamCount());
     84        ASSERT_NE(fp1->getParamCount(), fp3->getParamCount());
    7085        ASSERT_FALSE(fp2->hasReturnvalue());
    7186        ASSERT_TRUE(fp3->hasReturnvalue());
     
    86101        ASSERT_TRUE(mttype.null());
    87102    }
     103
     104    TEST_F(FunctorTest, canUseCallables)
     105    {
     106        int a = 0;
     107        FunctorPtr fp1 = createFunctor(CallableStruct{});
     108        FunctorPtr fp2 = createFunctor([](bool val) { return val; });
     109        FunctorPtr fp3 = createFunctor([&a]() {return a++; });
     110        FunctorPtr fp4 = createFunctor([a]() {return a; });
     111        ASSERT_EQ(1, (*fp1)().get<int>());
     112        ASSERT_EQ(true, (*fp2)(true).get<bool>());
     113        ASSERT_EQ(0, (*fp3)().get<int>());
     114        ASSERT_EQ(1, a);
     115        ASSERT_EQ(0, (*fp4)().get<int>());
     116        ASSERT_STREQ(fp1->getHeaderIdentifier().name(), fp3->getHeaderIdentifier().name());
     117    }
     118
     119    TEST_F(FunctorTest, SafeModeWorks)
     120    {
     121        DestroyableClass* testclass = new DestroyableClass();
     122        DestroyableClass* testclass2 = new DestroyableClass();
     123        FunctorPtr fp1 = createFunctor(&DestroyableClass::method, testclass);
     124        fp1->setSafeMode(true);
     125        FunctorPtr fp2 = createFunctor(&DestroyableClass::method, testclass);
     126        fp2->setSafeMode(true);
     127        FunctorPtr fp3 = createFunctor(&DestroyableClass::method, testclass);
     128        fp2->setRawObjectPointer(testclass2);
     129
     130        ASSERT_NE(nullptr, fp1->getRawObjectPointer());
     131        ASSERT_NE(nullptr, fp2->getRawObjectPointer());
     132        ASSERT_NE(nullptr, fp3->getRawObjectPointer());
     133        testclass->destroy();
     134        ASSERT_EQ(nullptr, fp1->getRawObjectPointer());
     135        ASSERT_NE(nullptr, fp2->getRawObjectPointer());
     136        ASSERT_NE(nullptr, fp3->getRawObjectPointer());
     137
     138        fp3->setRawObjectPointer(testclass2);
     139        fp3->setSafeMode(true);
     140        fp2->setSafeMode(false);
     141        testclass2->destroy();
     142        ASSERT_NE(nullptr, fp2->getRawObjectPointer());
     143        ASSERT_EQ(nullptr, fp3->getRawObjectPointer());
     144    }
    88145}
Note: See TracChangeset for help on using the changeset viewer.