How to Implement an Easy_Bind() That Automagically Inserts Implied Placeholders

How to implement an easy_bind() that automagically inserts implied placeholders? *With member pointers*

Your code has two key problems:

  1. You keep taking the address of the object you want to call a member function on. Once the entity is a pointer, taking the address of it won't do you much good.
  2. A member function pointer and its first argument obvious have different types. Either you'll need to provide a proper signature of it, you'd extract the first argument type from the member function pointer type, or you leave the corresponding argument type unconstrained and have the type system sort out invalid uses. The latter approach also has the advantage that you could pass reference and smart pointers.

Here is a patched up version of your code which makes it compile:

#include <functional>
#include <type_traits>
#include <utility>

template <std::size_t... Is>
struct indices {};

template <std::size_t N, std::size_t... Is>
struct build_indices
: build_indices<N-1, N-1, Is...> {};

template <std::size_t... Is>
struct build_indices<0, Is...> : indices<Is...> {};

template<int I> struct placeholder{};

namespace std{
template<int I>
struct is_placeholder< ::placeholder<I>> : std::integral_constant<int, I>{};
} // std::
namespace detail{
template<std::size_t... Is, class Ret, class Fn, class... MArgs, class... Args>
auto my_bind(indices<Is...>, Ret (Fn::*f)(MArgs...), Fn *i, Args&&... args)
-> decltype(std::bind(f, i, std::forward<Args>(args)..., placeholder<1 + Is>{}...)){
return std::bind(f, i, std::forward<Args>(args)..., placeholder<1 + Is>{}...);
}
}

template<class Ret, class... FArgs, class Fn, class... MArgs, class... Args>
auto my_bind(std::function<Ret(FArgs...)>, Ret (Fn::*f)(MArgs...), Fn *i, Args&&... args)
-> decltype(detail::my_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, i, std::forward<Args>(args)...)){

return detail::my_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, i, std::forward<Args>(args)...);
}

#include <iostream>
struct tmp{
void testt(int var1, int var2){
std::cout << var1 << " " << var2 << std::endl;
}
};

int main(){

tmp TMP;
auto f3 = my_bind(std::function<void(int, int)>(), &tmp::testt, &TMP);
f3(22, 23);

}

How to implement an easy_bind() that automagically inserts implied placeholders?

With the indices trick and the ability to tell std::bind about your own placeholder types, here's what I came up with:

#include <functional>
#include <type_traits>
#include <utility>

template<int I> struct placeholder{};

namespace std{
template<int I>
struct is_placeholder< ::placeholder<I>> : std::integral_constant<int, I>{};
} // std::

namespace detail{
template<std::size_t... Is, class F, class... Args>
auto easy_bind(indices<Is...>, F const& f, Args&&... args)
-> decltype(std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1>{}...))
{
return std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1>{}...);
}
} // detail::

template<class R, class... FArgs, class... Args>
auto easy_bind(std::function<R(FArgs...)> const& f, Args&&... args)
-> decltype(detail::easy_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, std::forward<Args>(args)...))
{
return detail::easy_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, std::forward<Args>(args)...);
}

Live example.

Take note that I require the function argument to easy_bind to be either of type std::function, or convertible to it, so that I have a definite signature available.

How to implement an easy_bind() that automagically inserts implied placeholders?

With the indices trick and the ability to tell std::bind about your own placeholder types, here's what I came up with:

#include <functional>
#include <type_traits>
#include <utility>

template<int I> struct placeholder{};

namespace std{
template<int I>
struct is_placeholder< ::placeholder<I>> : std::integral_constant<int, I>{};
} // std::

namespace detail{
template<std::size_t... Is, class F, class... Args>
auto easy_bind(indices<Is...>, F const& f, Args&&... args)
-> decltype(std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1>{}...))
{
return std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1>{}...);
}
} // detail::

template<class R, class... FArgs, class... Args>
auto easy_bind(std::function<R(FArgs...)> const& f, Args&&... args)
-> decltype(detail::easy_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, std::forward<Args>(args)...))
{
return detail::easy_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, std::forward<Args>(args)...);
}

Live example.

Take note that I require the function argument to easy_bind to be either of type std::function, or convertible to it, so that I have a definite signature available.

Packing an object, its method to call and arguments to call it with into a single object for invoking it later

That's the classic use case for std::bind and std::function:

#include <functional>
#include <vector>

using namespace std::placeholders; // for _1, _2, ...

std::vector<std::function<int(double, char)>> v;

Foo x;
Bar y;

v.emplace_back(std::bind(&Foo::f, &x, _1, _2)); // int Foo::f(double, char)
v.emplace_back(std::bind(&Bar::g, &y, _2, true, _1)); // int Bar::g(char, bool, double)
v.emplace_bacK(some_free_function); // int some_free_function(double, char)

To use:

for (auto & f : v) { sum += f(1.5, 'a'); }

Nested bind to member, need a pointer, got a reference. What do?

The compiler error is due to the fact that std::map const'ifies the key type, so that the value_type of map_t is not std::pair<long, std::unique_ptr<foo> > but rather std::pair<long const, std::unique_ptr<foo> >. To avoid such errors prefer using map_t::value_type. The following change fixes the error:

... std::bind(&map_t::value_type::second, std::placeholders::_1) ...

In C++11 you can simplify it to:

for(auto const& kv : foo_map_)
bar_.DoWork(&*kv.second);

Or, using std::for_each and C++11 lambda:

for_each(foo_map_.begin(), foo_map_.end(), 
[&bar_](map_t::value_type const& kv) { bar_.DoWork(&*kv.second); });

Syntax sugar for signal slot

Is it possible in modern C++ to implement such functionality in, for example, C# way? [...]

No, that's not possible in C++. The syntax for taking the address of a member function requires qualifying the function name with the class name (i.e. &MyClassName::myMethodName).

If you don't want to specify the class name, one possibility is to use lambdas. In particular, if you can afford a C++14 compiler, generic lambdas allow writing:

mySignal.connect([this] (auto x) -> { myMethodName(x) });

Sadly, you can't get much terser than this. You can use default lambda capture to save some syntactic noise:

mySignal.connect([&] (auto x) -> { myMethodName(x) });

However, Scott Meyers warns against the pitfalls of default lambda capture modes in his new book Effective Modern C++. From the readability point of view, I'm not sure this improves things a lot compared to your first option.

Besides, things soon become awkward if you want your lambda to perfectly forward its argument(s) to myMethodName:

mySignal.connect([&] (auto&& x) -> { myMethodName(std::forward<decltype(x)>(x)) });

If you don't mind macros (I usually do), you can employ a preprocessor-based solution as suggested by Quentin in their answer. However, I would prefer using a perfect-forwarding lambda in that case:

#define SLOT(name) \
[this] (auto&&... args) { name (std::forward<decltype(args)>(args)...); }

Which you could use like so:

e.connect(SLOT(foo));

Here is a live demo on Coliru.

How to implement a std::function with operator= that can check if its rhs has same signature

Cppreference says about operator=:

Sets the target of *this to the callable f, as if by executing function(std::forward(f)).swap(*this);. This operator does not participate in overload resolution unless f is Callable for argument types Args... and return type R.

This can be easily checked with std::is_invocable_r helper:

#include <type_traits>

template <class>
class function {};

template <class R, class... Args>
class function<R(Args...)> {
public:
template <typename F, typename x = std::enable_if_t<
std::is_invocable_r<R, F, Args...>::value>>
function& operator=(F&& f) {
// logic...
return *this;
}
};

#include <functional>
#include <string>

void bar1(std::string) {}
void bar2(int) {}
void bar3(float) {}
int bar4(int) { return 0; }

int main(int argc, char** argv) {
//std::function<void(int)> f;
function<void(int)> f;

// f = bar1; ERROR
f = bar2;
f = bar3;
f = bar4;
return 0;
}

I am using the SFINAE version with an extra template argument as I find it most clear but return-value-based can be used too.
Same as for std::function, this is really forgiving - extra return values in case of R=void are ignored, implicit conversions are enabled. This matches what std::is_invocable_r allows.

Wrapping a non-static method into std::function with this parameter bound using as little code as possible

  1. When calling a member function through ->* operator, you have to use parenthesis:

    (target->*method)(std::forward<Arguments>(args)...);
    ^ ^
  2. When calling a member function through a pointer to const (you do, note const T*), that member function has to be const qualified:

    void print(float a, int b) const
    ^^^^^
  3. Is there a way to specify explicitly that Method is a member method of T in the declaration of Method itself?

    template <class T>
    void connect(const T* target, void (T::*method)(Arguments...) const)

member function pointer with variadic templates

One approach is to use partial specialization:

template<typename> class DelegateManager;

template<typename FuncRetType,typename... FuncParams>
class DelegateManager<DelegateInfoPack<FuncRetType,FuncParams...>>
{
template<typename UserClass>
void BindDelegate(_FuncRetType(UserClass::*fp)(FuncParams...))
{
}
};

Another approach is to have a class which generates the appropriate function type

template <typename FuncRetType,typename FuncParams>
struct FunctionPointer;

template <typename FuncRetType,typename...ARGS>
struct FunctionPointer<FuncRetType,FunctionParamsPack<ARGS...>> {
typedef FuncRetType (Type)(ARGS...);
};

Then use that in your BindDelegate member function:

template<typename UserClass>
void
BindDelegate(
typename FunctionPointer<_FuncRetType,_FuncParams>::Type UserClass::*fp
)
{ ... }

Or maybe even put this into your DelegateInfoPack class:

template<typename FuncRetType,typename... FuncParams>
struct DelegateInfoPack {
.
.
.
typedef FuncRetType (_FuncType)(FuncParams...);
};

and use that in your DelegateManager

template<typename DelegateInfoPack>
struct DelegateManager
{
.
.
.

typedef typename DelegateInfoPack::_FuncType _FuncType;

template<typename UserClass>
void BindDelegate(_FuncType UserClass::*fp)
{
}
};


Related Topics



Leave a reply



Submit