/* * 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 #include #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: struct Type { /// Defines the type of a function (static or member) enum Enum { Static, Member }; }; public: virtual ~Functor() {} /// 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::Enum 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_index 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_index 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_index 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::Enum 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::Enum 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 expands it with a function-pointer. @param F The type of the function-pointer @param O The type of the function's class (or void if it's a static function) The template FunctorPointer has an additional template parameter that defines the type of the function-pointer. This can be handy if you want to get or set the function-pointer. You can then use a static_cast to cast a Functor to FunctorPointer if you know the type of the function-pointer. 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_; } // see Functor::getFullIdentifier() const std::type_index getFullIdentifier() const { return typeid(F); } protected: F functionPointer_; ///< The stored function-pointer }; namespace detail { // Helper class to get the type of the function pointer with the given class, parameters, return-value, and constness template struct FunctionPointer { typedef R (O::*Type)(Params...); }; template struct FunctionPointer { typedef R (O::*Type)(Params...) const; }; template struct FunctionPointer { typedef R (*Type)(Params...); }; // Helper class, used to call a function-pointer with a given object and parameters and to return its return-value (if available) template struct FunctorCaller { static inline MultiType call(typename detail::FunctionPointer::Type functionPointer, O* object, const Params&... parameters) { return (object->*functionPointer)(parameters...); } }; template struct FunctorCaller { static inline MultiType call(typename detail::FunctionPointer::Type functionPointer, O* object, const Params&... parameters) { (object->*functionPointer)(parameters...); return MultiType::Null; } }; template struct FunctorCaller { static inline MultiType call(typename detail::FunctionPointer::Type functionPointer, void*, const Params&... parameters) { return (*functionPointer)(parameters...); } }; template struct FunctorCaller { static inline MultiType call(typename detail::FunctionPointer::Type functionPointer, void*, const Params&... parameters) { (*functionPointer)(parameters...); return MultiType::Null; } }; // Helper class to determine if a function has a returnvalue template struct FunctorHasReturnvalue { enum { result = true }; }; template <> struct FunctorHasReturnvalue { enum { result = false }; }; //Barebones implementation of (make_)index_sequence for C++11 template struct index_sequence {}; template struct make_index_sequence : make_index_sequence {}; template struct make_index_sequence<0u, Is...> : index_sequence {}; //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 : make_type_list_helper::template make_type_list_impl {}; template struct make_type_list<0u, Types...> : 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 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::FunctionPointer::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 { auto multis = std::make_tuple(param1, param2, param3, param4, param5); return callHelper(object, multis, detail::make_index_sequence{}); } // 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 { static const std::array, sizeof...(Params)> funcs = {&MultiType::convert...}; if (index < funcs.size()) { funcs[index](argument); } } // 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 { static const std::array names = { typeToString()... }; if (index >= names.size()) { return ""; } return names[index]; } // see Functor::getTypenameReturnvalue() virtual std::string getTypenameReturnvalue() const override { return typeToString(); } // see Functor::getHeaderIdentifier() virtual const std::type_index getHeaderIdentifier() const override { return typeid(typename detail::FunctionPointer::Type); } // see Functor::getHeaderIdentifier(unsigned int) virtual const std::type_index getHeaderIdentifier(unsigned int params) const override { //+1 because e.g. for two parameters, we want typeids for zero, one, or two parameters return getHeaderIdentifierHelper(params, detail::make_index_sequence{}); } private: /// Helper function that extracts index numbers of parameters from a tuple. Needed to call the function pointer with the correct amount of arguments. template MultiType callHelper(O* object, Tup&& tup, detail::index_sequence) { return detail::FunctorCaller::call(this->functionPointer_, object, std::get(std::forward(tup))...); } /// Helper function to extract all identifiers of the function pointer using a deduced index sequence template const std::type_index& getHeaderIdentifierHelper(unsigned int params, detail::index_sequence) const { static const std::array typeinfos = { getTypelistIdentifier(detail::make_type_list{})... }; if (params >= typeinfos.size()) { return typeinfos.back(); } return typeinfos[params]; } ///Helper function that deduces a parameter pack of types and returns the corresponding identifier template const std::type_index getTypelistIdentifier(detail::type_list) const { return typeid(typename detail::FunctionPointer::Type); } }; /** @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 FunctorCallable : public FunctorTemplate { public: FunctorCallable(const F& obj): FunctorTemplate(&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 inline FunctorMemberPtr callableHelper(const F& obj, R(F::*func)(Params...) const) { return std::make_shared>(obj); } template inline FunctorMemberPtr callableHelper(const F& obj, R(F::*func)(Params...)) { return std::make_shared>(obj); } } 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 Functor with the given function-pointer template inline FunctorMemberPtr createFunctor(const F& obj) { return detail::callableHelper(obj, &F::operator()); } ///< Creates a new Functor with a callable object } #endif /* _Functor_H__ */