Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Changeset 10986


Ignore:
Timestamp:
Dec 28, 2015, 6:57:26 PM (8 years ago)
Author:
landauf
Message:

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.

Location:
code/branches/cpp11_v2
Files:
2 edited

Legend:

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

    r10985 r10986  
    357357
    358358    /**
    359         @brief FunctorPointer is a child class of FunctorMember and expands it with a function-pointer.
    360         @param F The type of the function-pointer
    361         @param O The type of the function's class (or void if it's a static function)
     359        @brief FunctorPointer is a child class of FunctorMember and extends it with a function-pointer (or a function-object).
     360        @param F The type of the function-pointer (or the function-object)
     361        @param O The type of the function's class (or void if it's a static function or a function-object)
    362362
    363363        The template FunctorPointer has an additional template parameter that defines the type
    364         of the function-pointer. This can be handy if you want to get or set the function-pointer.
    365         You can then use a static_cast to cast a Functor to FunctorPointer if you know the type
    366         of the function-pointer.
     364        of the function-pointer (or the function-object). This can be handy if you want to get
     365        or set the function-pointer (or the function-object). You can then use a static_cast
     366        to cast a Functor to FunctorPointer if you know the type of the function.
    367367
    368368        However FunctorPointer is not aware of the types of the different parameters or the
     
    383383                { return this->functionPointer_; }
    384384
    385             // see Functor::getFullIdentifier()
    386             virtual const std::type_info& getFullIdentifier() const override
    387                 { return typeid(F); }
    388 
    389385        protected:
    390386            F functionPointer_;     ///< The stored function-pointer
     
    393389    namespace detail
    394390    {
    395         // Helper class to get the type of the function pointer with the given class, parameters, return-value, and constness
    396         template <class R, class O, bool isconst, class... Params> struct FunctionPointer                            { typedef R (O::*Type)(Params...); };
    397         template <class R, class O, class... Params>               struct FunctionPointer<R, O, true, Params...>     { typedef R (O::*Type)(Params...) const; };
    398         template <class R, class... Params>                        struct FunctionPointer<R, void, false, Params...> { typedef R (*Type)(Params...); };
    399 
    400         // Helper class, used to call a function-pointer with a given object and parameters and to return its return-value (if available)
    401         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...); } };
    402         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; } };
    403         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...); } };
    404         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; } };
     391        // Helper class to get the type of the function-pointer (or the function-object) with the given class, parameters, return-value, and constness
     392        template <class F, class R, class O, bool isconst, class... Params> struct FunctionType /* generic type is undefined */;
     393        template <         class R, class O,               class... Params> struct FunctionType<void, R, O,    false, Params...> { typedef R (O::*Type)(Params...); };       // spezialization: non-const member function
     394        template <         class R, class O,               class... Params> struct FunctionType<void, R, O,    true,  Params...> { typedef R (O::*Type)(Params...) const; }; // spezialization: const member function
     395        template <         class R,                        class... Params> struct FunctionType<void, R, void, false, Params...> { typedef R (*Type)(Params...); };          // spezialization: static function
     396        template <class F, class R,                        class... Params> struct FunctionType<F,    R, void, false, Params...> { typedef F Type; };                        // spezialization: function object
     397
     398        // Helper class, used to call a function with a given object and parameters and to return its return-value (if available)
     399        template <class F, class R, class O, bool isconst, class... Params> struct FunctorCaller /* generic type is undefined */;
     400        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
     401        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
     402        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
     403        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
     404        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
     405        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
    405406
    406407        // Helper class to determine if a function has a returnvalue
     
    457458        that need to know the exact types of the parameters, return-value, and class.
    458459
     460        @param F the type of the function-object (or void if a function-pointer is used).
    459461        @param R The type of the return-value of the function
    460462        @param O The class of the function
     
    469471        All template arguments can be void.
    470472    */
    471     template <class R, class O, bool isconst, class... Params>
    472     class FunctorTemplate : public FunctorPointer<typename detail::FunctionPointer<R, O, isconst, Params...>::Type, O>
     473    template <class F, class R, class O, bool isconst, class... Params>
     474    class FunctorTemplate : public FunctorPointer<typename detail::FunctionType<F, R, O, isconst, Params...>::Type, O>
    473475    {
    474476        static_assert(sizeof...(Params) <= 5, "Only up to 5 parameters are supported");
     
    476478        public:
    477479            /// Constructor: Initializes the base class.
    478             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) {}
     480            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) {}
    479481
    480482            // see FunctorMember::operator()()
    481483            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
    482484            {
    483                 return detail::FunctorCaller<R, O, isconst, Params...>::call(this->functionPointer_, object, param1, param2, param3, param4, param5);
     485                return detail::FunctorCaller<F, R, O, isconst, Params...>::call(this->functionPointer_, object, param1, param2, param3, param4, param5);
    484486            }
    485487
     
    535537            }
    536538
     539            // see Functor::getFullIdentifier()
     540            virtual const std::type_info& getFullIdentifier() const override
     541            {
     542                return typeid(typename detail::FunctionType<void, R, O, isconst, Params...>::Type);
     543            }
     544
    537545            // see Functor::getHeaderIdentifier()
    538546            virtual const std::type_info& getHeaderIdentifier() const override
    539547            {
    540                 return typeid(typename detail::FunctionPointer<R, void, false, Params...>::Type);
     548                return typeid(typename detail::FunctionType<void, R, void, false, Params...>::Type);
    541549            }
    542550
     
    560568            const std::type_info& getTypelistIdentifier(detail::type_list<Types...>) const
    561569            {
    562                 return typeid(typename detail::FunctionPointer<R, void, false, Types...>::Type);
     570                return typeid(typename detail::FunctionType<void, R, void, false, Types...>::Type);
    563571            }
    564572    };
    565573
    566574
    567     /**
    568     @brief FunctorCallable is a child class of FunctorTemplate. It stores a callable
    569     object (e.g. a lambda or a class with operator()) inside and acts like any
    570     other functor. Note that it stores a \em copy of the object, not a reference or a pointer.
    571     Take care that this functor does not outlive objects that have been captured by reference
    572     in a lambda.
    573 
    574     @param F The type of the callable object
    575     @param R The type of the return-value of the function
    576     @param isconst True if operator() is const
    577     @param Params The types of the parameters
    578 
    579     This template can not be used directly when using lambdas - the type of a lambda
    580     is not specified. It can only really be used through the base-class Functor.
    581     */
    582     template <class F, class R, bool isconst, class... Params>
    583     class FunctorCallable : public FunctorTemplate<R, F, isconst, Params...>
    584     {
    585     public:
    586         FunctorCallable(const F& obj): FunctorTemplate<R, F, isconst, Params...>(&F::operator(), &obj_)
    587             , obj_(obj)
    588         {}
    589 
    590     private:
    591         F obj_; ///< The callable object
    592     };
    593 
    594575    namespace detail
    595576    {
    596577        //Helper functions to deduce types and constness of operator() and return the correct FunctorCallable
    597         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); }
    598         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); }
     578        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); }
     579        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.
    599580    }
    600581
    601     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
    602     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
    603 
    604     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
    605     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
    606 
    607     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
    608    
    609     template <class F> inline FunctorMemberPtr<F> createFunctor(const F& obj) { return detail::callableHelper(obj, &F::operator()); } ///< Creates a new Functor with a callable object
     582    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
     583    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
     584
     585    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
     586    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
     587
     588    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
     589
     590    /** Take care that this functor does not outlive objects that have been captured by reference in a lambda. */
     591    template <class F> inline FunctorStaticPtr createFunctor(const F& functionObject) { return detail::callableHelper(functionObject, &F::operator()); } ///< Creates a new Functor with a callable object
    610592}
    611593
  • code/branches/cpp11_v2/test/core/command/FunctorTest.cc

    r10984 r10986  
    7171        int f4a(int arg) { return arg*2; }
    7272        int f4b(int arg) { return arg*3; }
     73        int multiply(int a, int b) { return a*b; }
    7374
    7475        struct CallableStruct
     
    634635    ////////////////////////////// Various tests //////////////////////////////
    635636
     637    TEST_F(FunctorTest, CreateFunctorWithVariousFunctionTypes)
     638    {
     639        FunctorPtr c1 = createFunctor(&f4a);
     640        FunctorPtr c2 = createFunctor(&f4b);
     641        FunctorPtr c3 = createFunctor([] (int arg) { return arg*4; });
     642        auto lambda = [] (int arg1, int arg2) { return arg1*arg2; };
     643        FunctorPtr c4 = createFunctor(lambda);
     644
     645        std::function<int()> bind0 = std::bind(multiply, 4, 6);
     646        std::function<int(int)> bind1 = std::bind(multiply, std::placeholders::_1, 7);
     647        std::function<int(int, int)> bind2 = std::bind(multiply, std::placeholders::_1, std::placeholders::_2);
     648        std::function<int(int)> function = f4a;
     649
     650        FunctorPtr c5 = createFunctor(bind0);
     651        FunctorPtr c6 = createFunctor(bind1);
     652        FunctorPtr c7 = createFunctor(bind2);
     653        FunctorPtr c8 = createFunctor(function);
     654
     655
     656        EXPECT_EQ(8,  (*c1)(4).get<int>());
     657        EXPECT_EQ(12, (*c2)(4).get<int>());
     658        EXPECT_EQ(16, (*c3)(4).get<int>());
     659        EXPECT_EQ(20, (*c4)(4, 5).get<int>());
     660        EXPECT_EQ(24, (*c5)(4).get<int>());
     661        EXPECT_EQ(28, (*c6)(4).get<int>());
     662        EXPECT_EQ(32, (*c7)(4, 8).get<int>());
     663        EXPECT_EQ(8,  (*c8)(4).get<int>());
     664
     665        EXPECT_EQ(1, c1->getParamCount());
     666        EXPECT_EQ(1, c2->getParamCount());
     667        EXPECT_EQ(1, c3->getParamCount());
     668        EXPECT_EQ(2, c4->getParamCount());
     669        EXPECT_EQ(0, c5->getParamCount());
     670        EXPECT_EQ(1, c6->getParamCount());
     671        EXPECT_EQ(2, c7->getParamCount());
     672        EXPECT_EQ(1, c8->getParamCount());
     673
     674        EXPECT_EQ(typeid(int(*)(int)), c1->getFullIdentifier());
     675        EXPECT_EQ(typeid(int(*)(int)), c2->getFullIdentifier());
     676        EXPECT_EQ(typeid(int(*)(int)), c3->getFullIdentifier());
     677        EXPECT_EQ(typeid(int(*)(int, int)), c4->getFullIdentifier());
     678        EXPECT_EQ(typeid(int(*)()), c5->getFullIdentifier());
     679        EXPECT_EQ(typeid(int(*)(int)), c6->getFullIdentifier());
     680        EXPECT_EQ(typeid(int(*)(int, int)), c7->getFullIdentifier());
     681        EXPECT_EQ(typeid(int(*)(int)), c8->getFullIdentifier());
     682
     683        EXPECT_EQ(typeid(int(*)(int)), c1->getHeaderIdentifier());
     684        EXPECT_EQ(typeid(int(*)(int)), c2->getHeaderIdentifier());
     685        EXPECT_EQ(typeid(int(*)(int)), c3->getHeaderIdentifier());
     686        EXPECT_EQ(typeid(int(*)(int, int)), c4->getHeaderIdentifier());
     687        EXPECT_EQ(typeid(int(*)()), c5->getHeaderIdentifier());
     688        EXPECT_EQ(typeid(int(*)(int)), c6->getHeaderIdentifier());
     689        EXPECT_EQ(typeid(int(*)(int, int)), c7->getHeaderIdentifier());
     690        EXPECT_EQ(typeid(int(*)(int)), c8->getHeaderIdentifier());
     691
     692        EXPECT_EQ(typeid(int(*)(int)), c1->getHeaderIdentifier(1));
     693        EXPECT_EQ(typeid(int(*)(int)), c2->getHeaderIdentifier(1));
     694        EXPECT_EQ(typeid(int(*)(int)), c3->getHeaderIdentifier(1));
     695        EXPECT_EQ(typeid(int(*)(int)), c4->getHeaderIdentifier(1));
     696        EXPECT_EQ(typeid(int(*)()), c5->getHeaderIdentifier(1));
     697        EXPECT_EQ(typeid(int(*)(int)), c6->getHeaderIdentifier(1));
     698        EXPECT_EQ(typeid(int(*)(int)), c7->getHeaderIdentifier(1));
     699        EXPECT_EQ(typeid(int(*)(int)), c8->getHeaderIdentifier(1));
     700
     701        EXPECT_EQ(typeid(int(*)()), c1->getHeaderIdentifier(0));
     702        EXPECT_EQ(typeid(int(*)()), c2->getHeaderIdentifier(0));
     703        EXPECT_EQ(typeid(int(*)()), c3->getHeaderIdentifier(0));
     704        EXPECT_EQ(typeid(int(*)()), c4->getHeaderIdentifier(0));
     705        EXPECT_EQ(typeid(int(*)()), c5->getHeaderIdentifier(0));
     706        EXPECT_EQ(typeid(int(*)()), c6->getHeaderIdentifier(0));
     707        EXPECT_EQ(typeid(int(*)()), c7->getHeaderIdentifier(0));
     708        EXPECT_EQ(typeid(int(*)()), c8->getHeaderIdentifier(0));
     709    }
     710
     711    TEST_F(FunctorTest, CanCallFunctorWithLambdaWichIsAlreadyOutOfScope)
     712    {
     713        int var1 = 1;
     714        FunctorPtr fp1a = createFunctor([&var1] () { return var1++; });
     715        FunctorPtr fp1b = createFunctor([&var1] () { return var1; });
     716        FunctorPtr fp2;
     717        FunctorPtr fp3;
     718
     719        {
     720            int var2 = 2;
     721            fp2 = createFunctor([var2] () { return var2; });
     722        }
     723        {
     724            int var3 = 3;
     725            fp3 = createFunctor([var3] () { return var3; });
     726        }
     727
     728        EXPECT_EQ(1, var1);
     729        EXPECT_EQ(1, (*fp1a)().get<int>());
     730        EXPECT_EQ(2, var1);
     731        EXPECT_EQ(2, (*fp1b)().get<int>());
     732
     733        EXPECT_EQ(2, (*fp2)().get<int>());
     734        EXPECT_EQ(3, (*fp3)().get<int>());
     735    }
     736
    636737    TEST_F(FunctorTest, canCompareHeaderIdentifiers)
    637738    {
Note: See TracChangeset for help on using the changeset viewer.