/* * ORXONOX - the hottest 3D action shooter ever to exist * > www.orxonox.net < * * * License notice: * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Author: * Fabian 'x3n' Landau * Co-authors: * ... * */ /** @file @ingroup Command FunctorExecutor @brief Definition of orxonox::Functor and its specialized subclasses, as well as the createFunctor() functions. @anchor FunctorExample Functors can be used to wrap function-pointers. While function-pointers have a very complicated syntax in C++, Functors are always the same and you can call the wrapped function-pointer independently of its parameter with arguments of type MultiType. These arguments are then automatically converted to the right type. To create a Functor, the helper function createFunctor() is used. It returns an instance of orxonox::FunctorPtr which is simply a typedef of "std::shared_ptr". This means you don't have to delete the Functor after using it, because it is managed by the std::shared_ptr. Example: @code int myStaticFunction(int value) // Definition of a static function { return (value * 2); // Return the double of the value } FunctorPtr functor = createFunctor(&myStaticFunction); // Create a Functor int result = (*functor)(5); // Calls the functor with value = 5, result == 10 int result = (*functor)("7"); // Calls the functor with a string which is converted to an integer, result == 14 @endcode Functors can also be used if you work with member-functions. In this case createFunctor() returns an instance of orxonox::FunctorMemberPtr - this allows you to define the object that will be used to call the function. Example: @code class MyClass // Define a class { public: MyClass(const std::string& text) // Constructor { this->text_ = text; } bool contains(const std::string& word) // Function that searches for "word" in "text" { return (this->text_.find(word) != std::string::npos); } private: std::string text_; // Member variable }; MyClass* object = new MyClass("Hello World"); // Create an object of type MyClass and set its text to "Hello World" FunctorPtr functor = createFunctor(&MyClass:contains, object); // Create a Functor (note the object!) bool result = (*functor)("World"); // result == true bool result = (*functor)("test"); // result == false @endcode Instead of assigning the object directly to the functor when creating it, you can also define it at any later point or when you call the functor. Note however that this works only with orxonox::FunctorMember. @code MyClass* object1 = new MyClass("Hello World"); // Create an object MyClass* object2 = new MyClass("this is a test"); // Create another object FunctorMemberPtr functor = createFunctor(&MyClass:contains); // Create a FunctorMember (note: no object this time) bool result = (*functor)("World"); // result == false and an error: "Error: Can't execute FunctorMember, no object set." bool result = (*functor)(object1, "World"); // result == true bool result = (*functor)(object1, "test"); // result == false bool result = (*functor)(object2, "test"); // result == true functor->setObject(object1); // Assign an object to the FunctorMember bool result = (*functor)("World"); // result == true (no error this time, because the object was set using setObject()) @endcode */ #ifndef _Functor_H__ #define _Functor_H__ #include "core/CorePrereqs.h" #include #include "util/Output.h" #include "util/MultiType.h" #include "core/object/Destroyable.h" #include "FunctorPtr.h" namespace orxonox { const unsigned int MAX_FUNCTOR_ARGUMENTS = 5; ///< The maximum number of parameters of a function that is supported by Functor namespace detail { template inline std::string _typeToString() { return "unknown"; } template <> inline std::string _typeToString() { return "void"; } template <> inline std::string _typeToString() { return "int"; } template <> inline std::string _typeToString() { return "uint"; } template <> inline std::string _typeToString() { return "char"; } template <> inline std::string _typeToString() { return "uchar"; } template <> inline std::string _typeToString() { return "short"; } template <> inline std::string _typeToString() { return "ushort"; } template <> inline std::string _typeToString() { return "long"; } template <> inline std::string _typeToString() { return "ulong"; } template <> inline std::string _typeToString() { return "longlong"; } template <> inline std::string _typeToString() { return "ulonglong"; } template <> inline std::string _typeToString() { return "float"; } template <> inline std::string _typeToString() { return "double"; } template <> inline std::string _typeToString() { return "longdouble"; } template <> inline std::string _typeToString() { return "bool"; } template <> inline std::string _typeToString() { return "string"; } template <> inline std::string _typeToString() { return "Vector2"; } template <> inline std::string _typeToString() { return "Vector3"; } template <> inline std::string _typeToString() { return "Quaternion"; } template <> inline std::string _typeToString() { return "ColourValue"; } template <> inline std::string _typeToString() { return "Radian"; } template <> inline std::string _typeToString() { return "Degree"; } } /// Returns the name of type @a T as string. template inline std::string typeToString() { return detail::_typeToString::UnqualifiedReferredType>(); } /** @brief The Functor classes are used to wrap function pointers. Function-pointers in C++ have a pretty complicated syntax and you can't store and call them unless you know the exact type. A Functor can be used to wrap a function-pointer and to store it independent of its type. You can also call it independently of its parameters by passing the arguments as MultiType. They are converted automatically to the right type. Functor is a pure virtual base class. @see See @ref FunctorExample "Functor.h" for some examples. */ class _CoreExport Functor { public: /// Defines the type of a function (static or member) enum class Type { Static, Member }; public: virtual ~Functor() = default; /// Calls the function-pointer with up to five arguments. In case of a member-function, the assigned object-pointer is used to call the function. @return Returns the return-value of the function (if any; MultiType::Null otherwise) virtual MultiType operator()(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) = 0; /// Creates a new instance of Functor with the same values like this (used instead of a copy-constructor) virtual FunctorPtr clone() = 0; /// Returns the type of the function: static or member. virtual Type getType() const = 0; /// Returns the number of parameters of the function. virtual unsigned int getParamCount() const = 0; /// Returns true if the function has a return-value. virtual bool hasReturnvalue() const = 0; /// Returns the type-name of the parameter with given index (the first parameter has index 0). virtual std::string getTypenameParam(unsigned int index) const = 0; /// Returns the type-name of the return-value. virtual std::string getTypenameReturnvalue() const = 0; /// Converts a given argument to the type of the parameter with given index (the first parameter has index 0). virtual void evaluateArgument(unsigned int index, MultiType& argument) const = 0; /// Assigns an object-pointer to the functor which is used to execute a member-function. virtual void setRawObjectPointer(void* object) = 0; /// Returns the object-pointer. virtual void* getRawObjectPointer() const = 0; /// Enables or disables the safe mode which causes the functor to change the object pointer to nullptr if the object is deleted (only member functors). virtual void setSafeMode(bool bSafeMode) = 0; /// Returns the full identifier of the function-pointer which is defined as typeid(@a F), where @a F is the type of the stored function-pointer. Used to compare functors. virtual const std::type_info& getFullIdentifier() const = 0; /// Returns an identifier of the header of the function (doesn't include the function's class). Used to compare functors. virtual const std::type_info& getHeaderIdentifier() const = 0; /// Returns an identifier of the header of the function (doesn't include the function's class), but regards only the first @a params parameters. Used to compare functions if an Executor provides default-values for the other parameters. virtual const std::type_info& getHeaderIdentifier(unsigned int params) const = 0; }; /** @brief FunctorMember is a child class of Functor and expands it with an object-pointer, that is used for member-functions, as well as an overloaded execution operator. @param O The type of the function's class (or void if it's a static function) Note that FunctorMember is also used for static functions, but with T = void. FunctorStatic is a typedef of FunctorMember. The void* object-pointer is ignored in this case. @see See @ref FunctorExample "Functor.h" for some examples. */ template class FunctorMember : public Functor, public DestructionListener { public: /// Constructor: Stores the object-pointer. FunctorMember(O* object = nullptr) : object_(object), bSafeMode_(false) {} virtual ~FunctorMember() { if (this->bSafeMode_) { this->unregisterObject(this->object_); } } /// Calls the function-pointer with up to five arguments and an object. In case of a static-function, the object can be nullptr. @return Returns the return-value of the function (if any; MultiType::Null otherwise) 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) = 0; // see Functor::operator()() virtual MultiType operator()(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 { // call the function if an object was assigned if (this->object_) return (*this)(this->object_, param1, param2, param3, param4, param5); else { orxout(internal_error) << "Can't execute FunctorMember, no object set." << endl; return MultiType::Null; } } // see Functor::getType() virtual inline Functor::Type getType() const override { return Functor::Type::Member; } /// Assigns an object-pointer to the functor which is used to execute a member-function. inline void setObject(O* object) { if (this->bSafeMode_ && object != this->object_) { this->unregisterObject(this->object_); this->registerObject(object); } this->object_ = object; } /// Returns the object-pointer. inline O* getObject() const { return this->object_; } // see Functor::setRawObjectPointer() virtual inline void setRawObjectPointer(void* object) override { this->setObject((O*)object); } // see Functor::getRawObjectPointer() virtual inline void* getRawObjectPointer() const override { return this->object_; } // see Functor::setSafeMode() virtual inline void setSafeMode(bool bSafeMode) override { if (bSafeMode == this->bSafeMode_) return; this->bSafeMode_ = bSafeMode; if (bSafeMode) this->registerObject(this->object_); else this->unregisterObject(this->object_); } protected: /// Casts the object and registers as destruction listener if the object is a Destroyable. inline void registerObject(Destroyable* object) { this->registerAsDestructionListener(object); } inline void registerObject(void* object) {} /// Casts the object and unregisters as destruction listener if the object is a Destroyable. inline void unregisterObject(Destroyable* object) { this->unregisterAsDestructionListener(object); } inline void unregisterObject(void* 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; } O* object_; ///< The stored object-pointer, used to execute a member-function (or nullptr for static functions) bool bSafeMode_; ///< If true, the functor is in safe mode and registers itself as listener at the object and changes the pointer to nullptr if the object is deleted }; /// Specialization of FunctorMember with @a T = void. template <> class FunctorMember : public Functor { public: /// Constructor: Stores the object-pointer. FunctorMember(void* object = nullptr) {} /// Calls the function-pointer with up to five arguments and an object. In case of a static-function, the object can be nullptr. @return Returns the return-value of the function (if any; MultiType::Null otherwise) virtual MultiType operator()(void* 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) = 0; // see Functor::operator()() virtual MultiType operator()(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 (*this)((void*)nullptr, param1, param2, param3, param4, param5); } // see Functor::getType() virtual inline Functor::Type getType() const override { return Functor::Type::Static; } // see Functor::setRawObjectPointer() virtual inline void setRawObjectPointer(void*) override { orxout(internal_warning) << "Can't assign an object pointer to a static functor" << endl; } // see Functor::getRawObjectPointer() virtual inline void* getRawObjectPointer() const override { return nullptr; } // see Functor::setSafeMode() virtual inline void setSafeMode(bool) override {} }; /// FunctorStatic is just a typedef of FunctorMember with @a T = void. typedef FunctorMember FunctorStatic; /** @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 (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. */ template class FunctorPointer : public FunctorMember { public: /// Constructor: Initializes the base class and stores the function-pointer. FunctorPointer(F functionPointer, O* object = nullptr) : FunctorMember(object), functionPointer_(functionPointer) {} /// Changes the function-pointer. inline void setFunction(F functionPointer) { this->functionPointer_ = functionPointer; } /// Returns the function-pointer. inline F getFunction() const { return this->functionPointer_; } protected: F functionPointer_; ///< The stored function-pointer }; namespace detail { // Helper class to get the type of the function-pointer (or the function-object) with the given class, parameters, return-value, and constness template struct FunctionType /* generic type is undefined */; template < class R, class O, class... Params> struct FunctionType { typedef R (O::*Type)(Params...); }; // spezialization: non-const member function template < class R, class O, class... Params> struct FunctionType { typedef R (O::*Type)(Params...) const; }; // spezialization: const member function template < class R, class... Params> struct FunctionType { typedef R (*Type)(Params...); }; // spezialization: static function template struct FunctionType { typedef F Type; }; // spezialization: function object // Helper class, used to call a function with a given object and parameters and to return its return-value (if available) template struct FunctorCaller /* generic type is undefined */; template < class R, class O, bool isconst, class... Params> struct FunctorCaller { template static inline MultiType call(typename detail::FunctionType::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 { template static inline MultiType call(typename detail::FunctionType::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 { template static inline MultiType call(typename detail::FunctionType::Type functionPointer, void*, const Params&... parameters, const UnusedParams&...) { return (*functionPointer)(parameters...); } }; // spezialization: static function with return value template < class... Params> struct FunctorCaller { template static inline MultiType call(typename detail::FunctionType::Type functionPointer, void*, const Params&... parameters, const UnusedParams&...) { (*functionPointer)(parameters...); return MultiType::Null; } }; // spezialization: static function without return value template struct FunctorCaller { template static inline MultiType call(typename detail::FunctionType::Type functor, void*, const Params&... parameters, const UnusedParams&...) { return functor(parameters...); } }; // spezialization: function object with return value template struct FunctorCaller { template static inline MultiType call(typename detail::FunctionType::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 struct FunctorHasReturnvalue { enum { result = true }; }; template <> struct FunctorHasReturnvalue { enum { result = false }; }; // Helper class to determine the N-th parameter of a variadic template (with 0 being the index of the first parameter) template struct GetNthParamType { typedef typename GetNthParamType::Type Type; }; template struct GetNthParamType<0, T, Other...> { typedef T Type; }; // Helper structs to deduce the first N types of a parameter pack template struct type_list {}; template struct make_type_list_helper { template struct make_type_list_impl : make_type_list_helper::template make_type_list_impl {}; template struct make_type_list_impl<1u, Types...> : type_list {}; }; template struct make_type_list_helper { template struct make_type_list_impl : type_list {}; }; template struct make_type_list : make_type_list_helper::template make_type_list_impl {}; template struct make_type_list<0u, Types...> : type_list<> {}; template struct make_type_list : type_list<> {}; template <> struct make_type_list<0u> : type_list<> {}; } /** @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 @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 are used directly. It implements all the virtual functions that are declared by its base classes. All template arguments can be void. */ template class FunctorTemplate : public FunctorPointer::Type, O> { static_assert(sizeof...(Params) <= 5, "Only up to 5 parameters are supported"); public: /// Constructor: Initializes the base class. FunctorTemplate(typename detail::FunctionType::Type functionPointer, O* object = nullptr) : FunctorPointer::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::call(this->functionPointer_, object, param1, param2, param3, param4, param5); } // see Functor::clone() virtual FunctorPtr clone() override { return std::make_shared(*this); } // see Functor::evaluateArgument() virtual void evaluateArgument(unsigned int index, MultiType& argument) const override { switch (index) { case 0: argument.convert::Type>(); break; case 1: argument.convert::Type>(); break; case 2: argument.convert::Type>(); break; case 3: argument.convert::Type>(); break; case 4: argument.convert::Type>(); break; } } // see Functor::getParamCount() virtual unsigned int getParamCount() const override { return sizeof...(Params); } // see Functor::hasReturnvalue() virtual bool hasReturnvalue() const override { return detail::FunctorHasReturnvalue::result; } // see Functor::getTypenameParam() virtual std::string getTypenameParam(unsigned int index) const override { switch (index) { case 0: return typeToString::Type>(); case 1: return typeToString::Type>(); case 2: return typeToString::Type>(); case 3: return typeToString::Type>(); case 4: return typeToString::Type>(); default: return ""; } } // see Functor::getTypenameReturnvalue() virtual std::string getTypenameReturnvalue() const override { return typeToString(); } // see Functor::getFullIdentifier() virtual const std::type_info& getFullIdentifier() const override { return typeid(typename detail::FunctionType::Type); } // see Functor::getHeaderIdentifier() virtual const std::type_info& getHeaderIdentifier() const override { return typeid(typename detail::FunctionType::Type); } // see Functor::getHeaderIdentifier(unsigned int) virtual const std::type_info& getHeaderIdentifier(unsigned int params) const override { switch (params) { case 0: return this->getTypelistIdentifier(detail::make_type_list<0, Params...>{}); case 1: return this->getTypelistIdentifier(detail::make_type_list<1, Params...>{}); case 2: return this->getTypelistIdentifier(detail::make_type_list<2, Params...>{}); case 3: return this->getTypelistIdentifier(detail::make_type_list<3, Params...>{}); case 4: return this->getTypelistIdentifier(detail::make_type_list<4, Params...>{}); default: return this->getTypelistIdentifier(detail::make_type_list<5, Params...>{}); } } private: /// Helper function that deduces a parameter pack of types and returns the corresponding identifier template const std::type_info& getTypelistIdentifier(detail::type_list) const { return typeid(typename detail::FunctionType::Type); } }; namespace detail { // Helper functions to deduce types and constness of operator() and return the correct FunctorTemplate template struct CallableHelper { template static inline FunctorStaticPtr create(const F& functionObject, R(F::*)(Params...)) { return std::make_shared>(functionObject); } template static inline FunctorStaticPtr create(const F& functionObject, R(F::*)(Params...) const) { return std::make_shared>(functionObject); } // note: both const and non-const function-objects are treated as static functors with isconst=false. }; } template inline FunctorMemberPtr createFunctor(R (O::*functionPointer)(Params...), OO* object) { return std::make_shared>(functionPointer, object); } ///< Creates a new FunctorMember with the given function-pointer and an assigned object template inline FunctorMemberPtr createFunctor(R (O::*functionPointer)(Params...) const, OO* object) { return std::make_shared>(functionPointer, object); } ///< Creates a new FunctorMember with the given function-pointer and an assigned object template inline FunctorMemberPtr createFunctor(R (O::*functionPointer)(Params...)) { return std::make_shared>(functionPointer); } ///< Creates a new FunctorMember with the given function-pointer template inline FunctorMemberPtr createFunctor(R (O::*functionPointer)(Params...) const) { return std::make_shared>(functionPointer); } ///< Creates a new FunctorMember with the given function-pointer template inline FunctorStaticPtr createFunctor(R (*functionPointer)(Params...)) { return std::make_shared>(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 inline FunctorStaticPtr createFunctor(const F& functionObject) { return detail::CallableHelper::create(functionObject, &F::operator()); } ///< Creates a new Functor with a callable object } #endif /* _Functor_H__ */