From ad8ba94b98f566ab8ca4194cd83f20a51570fa5f Mon Sep 17 00:00:00 2001 From: Christoph Lehmann <christoph.lehmann@ufz.de> Date: Wed, 6 Jul 2016 12:36:48 +0200 Subject: [PATCH] [BL] added wrapper for std::bind --- BaseLib/Functional.cpp | 33 +++++++ BaseLib/Functional.h | 211 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 BaseLib/Functional.cpp create mode 100644 BaseLib/Functional.h diff --git a/BaseLib/Functional.cpp b/BaseLib/Functional.cpp new file mode 100644 index 00000000000..e2f71555c36 --- /dev/null +++ b/BaseLib/Functional.cpp @@ -0,0 +1,33 @@ +/** + * \copyright + * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#include "Functional.h" + +namespace BaseLib +{ +namespace detail +{ +#define DEFINE_INDEXEDPLACEHOLDER_MEMBER(INDEX, INDEX_P_1) \ + const decltype(std::placeholders::_##INDEX_P_1) \ + IndexedPlacedPlaceholder<(INDEX)>::value = \ + std::placeholders::_##INDEX_P_1 + +DEFINE_INDEXEDPLACEHOLDER_MEMBER(0, 1); +DEFINE_INDEXEDPLACEHOLDER_MEMBER(1, 2); +DEFINE_INDEXEDPLACEHOLDER_MEMBER(2, 3); +DEFINE_INDEXEDPLACEHOLDER_MEMBER(3, 4); +DEFINE_INDEXEDPLACEHOLDER_MEMBER(4, 5); +DEFINE_INDEXEDPLACEHOLDER_MEMBER(5, 6); +DEFINE_INDEXEDPLACEHOLDER_MEMBER(6, 7); +DEFINE_INDEXEDPLACEHOLDER_MEMBER(7, 8); +DEFINE_INDEXEDPLACEHOLDER_MEMBER(8, 9); +DEFINE_INDEXEDPLACEHOLDER_MEMBER(9, 10); +} + +} // namespace BaseLib diff --git a/BaseLib/Functional.h b/BaseLib/Functional.h new file mode 100644 index 00000000000..6f453c8c9af --- /dev/null +++ b/BaseLib/Functional.h @@ -0,0 +1,211 @@ +/** + * \copyright + * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#ifndef BASELIB_FUNCTIONAL_H +#define BASELIB_FUNCTIONAL_H + +#include <functional> +#include "BaseLib/TMPUtil.h" + +namespace BaseLib +{ +namespace detail +{ +//! Helper struct used to make std::placeholders::_1, ... accessible via a +//! compile-time computed index (the template parameter). +template <int> +struct IndexedPlacedPlaceholder; + +//! Creates specializations of IndexedPlacedPlaceholder. +//! \param INDEX the integer value which is specialized +//! \param INDEX_P_1 "index plus one"; if INDEX_P_1 equals 1, then the member +//! value will be std::placeholders::_1, etc. +#define SPECIALIZE_INDEXEDPLACEHOLDER(INDEX, INDEX_P_1) \ + template <> \ + struct IndexedPlacedPlaceholder<(INDEX)> { \ + static std::enable_if< \ + std::is_placeholder<decltype( \ + std::placeholders::_##INDEX_P_1)>::value, \ + const decltype(std::placeholders::_##INDEX_P_1)>::type value; \ + } + +// Create specializations up to the tenth placeholder +SPECIALIZE_INDEXEDPLACEHOLDER(0, 1); +SPECIALIZE_INDEXEDPLACEHOLDER(1, 2); +SPECIALIZE_INDEXEDPLACEHOLDER(2, 3); +SPECIALIZE_INDEXEDPLACEHOLDER(3, 4); +SPECIALIZE_INDEXEDPLACEHOLDER(4, 5); +SPECIALIZE_INDEXEDPLACEHOLDER(5, 6); +SPECIALIZE_INDEXEDPLACEHOLDER(6, 7); +SPECIALIZE_INDEXEDPLACEHOLDER(7, 8); +SPECIALIZE_INDEXEDPLACEHOLDER(8, 9); +SPECIALIZE_INDEXEDPLACEHOLDER(9, 10); + +#undef SPECIALIZE_INDEXEDPLACEHOLDER + +// Note: The call sequence is easyBind() -> easyBind_inner() -> +// easyBind_innermost(). + +template <int... Indices, typename Object, typename MethodClass, + typename ReturnType, typename... Args> +std::function<ReturnType(Args...)> easyBind_innermost( + ReturnType (MethodClass::*method)(Args...), Object& obj) +{ + // std::ref makes sure that obj is not copied. + return std::bind(method, std::ref(obj), + IndexedPlacedPlaceholder<Indices>::value...); +} + +template <int... Indices, typename Object, typename MethodClass, + typename ReturnType, typename... Args> +std::function<ReturnType(Args...)> easyBind_innermost( + ReturnType (MethodClass::*method)(Args...) const, Object const& obj) +{ + // std::cref makes sure that obj is not copied. + return std::bind(method, std::cref(obj), + IndexedPlacedPlaceholder<Indices>::value...); +} + +template <int... Indices, typename Object, typename MethodClass, + typename ReturnType, typename... Args> +std::function<ReturnType(Args...)> easyBind_innermost( + ReturnType (MethodClass::*method)(Args...) const, Object& obj) +{ + // std::cref makes sure that obj is not copied. + return std::bind(method, std::cref(obj), + IndexedPlacedPlaceholder<Indices>::value...); +} + +template <int... Indices, typename Object, typename MethodClass, + typename ReturnType, typename... Args> +std::function<ReturnType(Args...)> easyBind_innermost( + ReturnType (MethodClass::*method)(Args...), Object&& obj) +{ + return std::bind(method, std::forward<Object>(obj), + IndexedPlacedPlaceholder<Indices>::value...); +} + +template <int... Indices, typename Object, typename MethodClass, + typename ReturnType, typename... Args> +std::function<ReturnType(Args...)> easyBind_innermost( + ReturnType (MethodClass::*method)(Args...) const, Object&& obj) +{ + return std::bind(method, std::forward<Object>(obj), + IndexedPlacedPlaceholder<Indices>::value...); +} + +template <int... Indices, typename Object, typename MethodClass, + typename ReturnType, typename... Args> +std::function<ReturnType(Args...)> easyBind_inner( + ReturnType (MethodClass::*method)(Args...), Object&& obj, + IntegerSequence<Indices...>) +{ + return easyBind_innermost<Indices...>(method, std::forward<Object>(obj)); +} + +template <int... Indices, typename Object, typename MethodClass, + typename ReturnType, typename... Args> +std::function<ReturnType(Args...)> easyBind_inner( + ReturnType (MethodClass::*method)(Args...) const, Object&& obj, + IntegerSequence<Indices...>) +{ + return easyBind_innermost<Indices...>(method, std::forward<Object>(obj)); +} + +/*! Deduces the signature of the call operator of class \c T. + * + * The matching type of std::function is provided as the member type + * \c FunctionType. + * + * \see http://stackoverflow.com/a/7943765 + */ +template <typename T> +struct FunctionTraits + : public FunctionTraits<decltype(&std::decay<T>::type::operator())> { +}; + +template <typename Object, typename ReturnType, typename... Args> +struct FunctionTraits<ReturnType (Object::*)(Args...)> { + using FunctionType = std::function<ReturnType(Args...)>; +}; + +template <typename Object, typename ReturnType, typename... Args> +struct FunctionTraits<ReturnType (Object::*)(Args...) const> { + using FunctionType = std::function<ReturnType(Args...)>; +}; + +} // namespace detail + +/*! Convenience wrapper for std::bind(). + * + * This function binds the member function pointer \c member of class \c Object + * to the instance \c obj of this class and wraps the result in a std::function + * with matching signature. + * + * The result of this function can be used, e.g., to deduce the signature of the + * \c method (which is not possible with the result of std::bind). + * + * Example: + * \code{.cpp} + * using std::placeholders; + * Object some_object; + * + * auto f_bind = std::function<ReturnType(Arg1, Arg2, Arg3>( + * std::bind(&Object::methodWithThreeArguments, + * std::ref(some_object), _1, _2, _3); + * + * auto f_easy = easyBind(&Object::methodWithThreeArguments, some_object); + * \endcode + * + * In the example the expressions creating \c f_bind and \c f_easy are + * equivalent. + * + * \note + * There is one difference between the behaviour of std::bind and the one of + * easyBind: In easyBind \c obj is never copied, instead it will be referenced. + * This is in contrast to the behaviour of std::bind, and has been chosen in + * order to prevent accidental copies. + */ +template <typename Object, typename ReturnType, typename... Args> +std::function<ReturnType(Args...)> easyBind( + ReturnType (std::remove_pointer<typename std::decay<Object>::type>::type::* + method)(Args...), + Object&& obj) +{ + return detail::easyBind_inner( + method, std::forward<Object>(obj), + typename GenerateIntegerSequence<sizeof...(Args)>::type{}); +} + +//! \overload +template <typename Object, typename ReturnType, typename... Args> +std::function<ReturnType(Args...)> easyBind( + ReturnType (std::remove_pointer<typename std::decay<Object>::type>::type::* + method)(Args...) const, + Object&& obj) +{ + return detail::easyBind_inner( + method, std::forward<Object>(obj), + typename GenerateIntegerSequence<sizeof...(Args)>::type{}); +} + +//! Wraps a callable object in a std::function. +//! +//! This method is provided for convenience since it automatically deduces the +//! correct type of std::function. +template <typename Object> +typename detail::FunctionTraits<Object>::FunctionType easyBind(Object&& obj) +{ + return BaseLib::easyBind(&std::decay<Object>::type::operator(), + std::forward<Object>(obj)); +} + +} // namespace BaseLib + +#endif // BASELIB_FUNCTIONAL_H -- GitLab