Skip to content
Snippets Groups Projects
Commit 8cdb7791 authored by Christoph Lehmann's avatar Christoph Lehmann Committed by GitHub
Browse files

Merge pull request #1312 from chleh/easybind

Added a wrapper for std::bind
parents 762bacca 3a593469
No related branches found
No related tags found
No related merge requests found
/**
* \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
/**
* \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 const decltype(std::placeholders::_##INDEX_P_1) 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 ReturnType,
typename... Args>
std::function<ReturnType(Args...)> easyBind_innermost(
ReturnType (Object::*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 ReturnType,
typename... Args>
std::function<ReturnType(Args...)> easyBind_innermost(
ReturnType (Object::*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 ReturnType,
typename... Args>
std::function<ReturnType(Args...)> easyBind_innermost(
ReturnType (Object::*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 MethodClass, typename ReturnType,
typename... Args>
typename std::enable_if<
std::is_same<MethodClass,
/* Note: All of remove_cv, remove_pointer and decay is
* necessary, e.g. if method is a member function pointer. */
typename std::remove_cv<typename std::remove_pointer<
typename std::decay<Object>::type>::type>::type>::value,
std::function<ReturnType(Args...)>>::type
easyBind(ReturnType (MethodClass::*method)(Args...), Object&& obj)
{
return detail::easyBind_inner(
method, std::forward<Object>(obj),
typename GenerateIntegerSequence<sizeof...(Args)>::type{});
}
//! \overload
template <typename Object, typename MethodClass, typename ReturnType,
typename... Args>
typename std::enable_if<
std::is_same<MethodClass,
typename std::remove_cv<typename std::remove_pointer<
typename std::decay<Object>::type>::type>::type>::value,
std::function<ReturnType(Args...)>>::type
easyBind(ReturnType (MethodClass::*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
/**
* \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_TMPUTIL_H
#define BASELIB_TMPUTIL_H
namespace BaseLib
{
//! Has sequence of integers as template parameters
template <int...>
struct IntegerSequence {
};
//! Generates an IntegerSequence.
//!
//! \see http://stackoverflow.com/a/7858971
template <int N, int... S>
struct GenerateIntegerSequence {
// effectively pushes N-1 from the left to the list int... S of integers.
typedef typename GenerateIntegerSequence<N - 1, N - 1, S...>::type type;
};
template <int... S>
struct GenerateIntegerSequence<0, S...> {
typedef IntegerSequence<S...> type;
};
/* The template metaprogram proceeds in the following way:
*
* GenerateIntegerSequence<sizeof...(Args)>::type
*
* Assume sizeof...(Args) == 3. Let GIS := GenerateIntegerSequence
* GIS<3, []>
* -> GIS<2, [2]>
* -> GIS<1, [1, 2]>
* -> GIS<0, [0, 1, 2], which has member typedef IntegerSequence<0, 1, 2>
*/
} // namespace BaseLib
#endif // BASELIB_TMPUTIL_H
/**
* \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 <gtest/gtest.h>
#include <logog/include/logog.hpp>
#include "BaseLib/Functional.h"
class InstanceCounter
{
public:
InstanceCounter() {
++_num_constructed;
}
InstanceCounter(InstanceCounter const&) {
++_num_copied;
}
InstanceCounter(InstanceCounter&&) {
++_num_moved;
}
virtual ~InstanceCounter() {
++_num_destroyed;
}
static int getNumberOfConstructions() { return _num_constructed; }
static int getNumberOfCopies() { return _num_copied; }
static int getNumberOfMoves() { return _num_moved; }
static int getNumberOfInstances()
{
return _num_constructed + _num_moved + _num_copied - _num_destroyed;
}
static void update(int& num_const, int& num_move, int& num_copy, int& num_inst)
{
num_const = getNumberOfConstructions();
num_move = getNumberOfMoves();
num_copy = getNumberOfCopies();
num_inst = getNumberOfInstances();
}
private:
static int _num_constructed;
static int _num_copied;
static int _num_moved;
static int _num_destroyed;
};
int InstanceCounter::_num_constructed = 0;
int InstanceCounter::_num_copied = 0;
int InstanceCounter::_num_moved = 0;
int InstanceCounter::_num_destroyed = 0;
class A : public InstanceCounter
{
public:
A(const double value) : _value(value) {}
A(A const& other) : InstanceCounter(other), _value(other._value) {}
A(A&& other) : InstanceCounter(std::move(other)), _value(other._value) {}
A& operator=(A const& other) { _value = other._value; return *this; }
A& operator=(A&& other) { _value = other._value; return *this; }
void add(A const& other) { _value += other._value; }
// pass by value intended.
void multiply(A other) {
_value *= other._value;
other._value = 0.0;
}
double getValue() const { return _value; }
double& getValueRef() { return _value; }
double operator()(double const x) { return _value*x; }
private:
double _value;
};
#define EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst) \
EXPECT_EQ((num_const), InstanceCounter::getNumberOfConstructions()); \
EXPECT_EQ((num_move), InstanceCounter::getNumberOfMoves()); \
EXPECT_EQ((num_copy), InstanceCounter::getNumberOfCopies()); \
EXPECT_EQ((num_inst), InstanceCounter::getNumberOfInstances())
TEST(BaseLib, Functional)
{
auto num_const = InstanceCounter::getNumberOfConstructions();
auto num_move = InstanceCounter::getNumberOfMoves();
auto num_copy = InstanceCounter::getNumberOfCopies();
auto num_inst = InstanceCounter::getNumberOfInstances();
// Base line: measure how many copies and moves
// std::function<>(std::bind(...)) needs.
A a_base(0.0);
// move the object to std::bind()
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
std::function<void(A)> fct_mult(
std::bind(&A::multiply, std::move(a_base), std::placeholders::_1));
auto const num_copy_base_move =
InstanceCounter::getNumberOfCopies() - num_copy;
auto const num_move_base_move =
InstanceCounter::getNumberOfMoves() - num_move;
// call std::function using pass-by-value
A a_base2(0.0);
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
fct_mult(a_base2);
auto const num_copy_base_pass =
InstanceCounter::getNumberOfCopies() - num_copy;
auto const num_move_base_pass =
InstanceCounter::getNumberOfMoves() - num_move;
// end base line
// self test
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
A a1(3.0);
A a2(a1);
EXPECT_INSTANCES(num_const+1, num_move, num_copy+1, num_inst+2);
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
auto f1_get = BaseLib::easyBind(&A::getValue, a1);
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(3.0, f1_get());
// check that really a reference is returned
{
auto f2_getRef = BaseLib::easyBind(&A::getValueRef, a2);
auto& value_ref = f2_getRef();
EXPECT_EQ(3.0, value_ref);
value_ref = 4.0;
EXPECT_EQ(4.0, a2.getValue());
}
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
// test binding to pointers
{
A* ap = &a1;
auto fp_get = BaseLib::easyBind(&A::getValue, ap);
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(3.0, fp_get());
A const* apc = &a1;
auto fpc_get = BaseLib::easyBind(&A::getValue, apc);
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(3.0, fpc_get());
}
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
// check that referenced objects are not copied
{
A& a3 = a2;
auto f3_get = BaseLib::easyBind(&A::getValue, a3);
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(4.0, f3_get());
}
{
A const& a3 = a2;
auto f3_get = BaseLib::easyBind(&A::getValue, a3);
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(4.0, f3_get());
}
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
// temporaries must be moved
{
auto ftemp_get = BaseLib::easyBind(&A::getValue, A(5.0));
EXPECT_INSTANCES(num_const + 1, num_move + num_move_base_move,
num_copy + num_copy_base_move, num_inst + 1);
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(5.0, ftemp_get());
}
// ftemp_get destroyed
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst-1);
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
// testing explicit move
{
A a_move(5.0);
EXPECT_INSTANCES(num_const+1, num_move, num_copy, num_inst+1);
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
auto ftemp_get = BaseLib::easyBind(&A::getValue, std::move(a_move));
EXPECT_INSTANCES(num_const, num_move + num_move_base_move,
num_copy + num_copy_base_move, num_inst+1);
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(5.0, ftemp_get());
}
// ftemp_get destroyed and a_move
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst-2);
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
// test binding a callable object
{
auto f1_op = BaseLib::easyBind(a1);
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(21.0, f1_op(7.0));
}
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
// test binding a lambda
{
double value = 2.0;
auto f_op = BaseLib::easyBind([&value](const double x) {
value *= x;
return value;
});
EXPECT_EQ(6.0, f_op(3.0));
EXPECT_EQ(6.0, value);
}
// check that parameters passed by reference are not copied
{
auto f1_add = BaseLib::easyBind(&A::add, a1);
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
f1_add(a2);
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(7.0, f1_get());
}
// check that parameters passed by value are copied
{
auto f1_mult = BaseLib::easyBind(&A::multiply, a1);
EXPECT_INSTANCES(num_const, num_move, num_copy, num_inst);
f1_mult(a2);
EXPECT_INSTANCES(num_const, num_move + num_move_base_pass,
num_copy + num_copy_base_pass, num_inst);
InstanceCounter::update(num_const, num_move, num_copy, num_inst);
EXPECT_EQ(28.0, f1_get());
EXPECT_EQ(4.0, a2.getValue());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment