8

I recently found this nifty snippet on the web - it allows you to bind without having to pass in explicit placeholders:

template <typename ReturnType, typename... Args>
std::function<ReturnType(Args...)> 
easy_bind(ReturnType(*MemPtr)(Args...))
{
  return [=]( Args... args ) -> ReturnType { return (*MemPtr)( args... ); };
}

This version works great with no args:

auto f1 = easy_bind( (std::string(*)(A&,A&))&Worker::MyFn );

later invoked with:

std::string s = f1( *p_a1, *p_a2 );

Question

Is it possible to modify the code to work with anything up to n args, filling 2-n (in this case) with placeholders? For example, this one should have one placeholder:

auto f2 = easy_bind( (std::string(*)(A&,A&))&Worker::MyFn, *p_a1 );     

later invoked with:

std::string s = f2( *p_a2 );

Bonus

Ultimately, it would nice to have something like this (which inserts no placeholders since it will use up the last one), but I don't think it's workable with this implementation (can't pattern-match the signature, I think):

auto f3 = easy_bind( f2, *p_a2 );     

later invoked with:

std::string s = f3();

The bottom line is, it would be nice to have a version of bind where I don't need to put in placeholders - it would be quite useful in generic TMP code.

Xeo
  • 129,499
  • 52
  • 291
  • 397
kfmfe04
  • 14,936
  • 14
  • 74
  • 140
  • Why not just `return { MemPtr };`? – Kerrek SB Feb 22 '13 at 12:30
  • @KerrekSB works with nothing passed in - what if I start passing in a *partial* list of arguments? – kfmfe04 Feb 22 '13 at 12:32
  • While there are no _explicit_ placeholders, they are there in the cast (which I personally find less pleasant to read than a placeholder list). – Some programmer dude Feb 22 '13 at 12:33
  • I'm not sure what to make of `(std::string(*)(A&,A&))&Worker::MyFn`. Is that legal? – Kerrek SB Feb 22 '13 at 12:34
  • @KerrekSB it was needed in the test case I had (to resolve overloading - I know, it looks like a mess!) - that part tested out fine. – kfmfe04 Feb 22 '13 at 12:35
  • I'm seeing a distinct lack of `std::forward` in your *forwarding* function. – Nicol Bolas Feb 22 '13 at 12:37
  • @NicolBolas good catch - I wouldn't be surprised if there are other inefficiencies in there: ultimately, I'm looking for an implementation of bind where I don't need to explicitly code in implied placeholders... – kfmfe04 Feb 22 '13 at 12:39
  • The first version of `easy_bind` is absolutely useless. It's exactly the same as writing just `auto f1 = (std::string(*)(A&,A&))&Worker::MyFn;`. The second version is hard to implement, since the standard only gives you `_1`, `_2`, etc and not `placeholder<0>`, `placeholder<1>`, which would make it extremely easy. – Xeo Feb 22 '13 at 12:40
  • @Xeo I think I've seen `std::_Placeholder<1>()` - I don't know if it's portable or not, though... (seems to compile on gcc 4.7.2) – kfmfe04 Feb 22 '13 at 12:42
  • And on MSVC, it's `std::_Ph<1>` etc. Names like `_This` are reserved for the implementation and the standard doesn't guarantee they exist. – Xeo Feb 22 '13 at 12:44
  • @Xeo assuming that you have one or the other, how would you implement this? – kfmfe04 Feb 22 '13 at 12:45
  • [Indices trick.](http://loungecpp.wikidot.com/tips-and-tricks%3aindices) – Xeo Feb 22 '13 at 12:48
  • @Xeo that's going to take me a bit of time to digest - lemme play with it a little to see if I can make the connection - ty – kfmfe04 Feb 22 '13 at 12:53
  • [Here](http://stacked-crooked.com/view?id=bbcca4f15285cee813f701f61aed00f8)'s a prototype. Note that it requires the argument to be of type `std::function` (or convertible to it), so that you can have a definite signature. – Xeo Feb 22 '13 at 12:59
  • This looks like you have a worse `std::mem_fn`. – Luc Danton Feb 23 '13 at 09:13
  • @LucDanton didn't know such a thing existed - ty for letting me know – kfmfe04 Feb 23 '13 at 09:28
  • use [std::bind_front](https://en.cppreference.com/w/cpp/utility/functional/bind_front) since >= c++20 – ridilculous Jan 23 '23 at 18:13

2 Answers2

16

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.

M2tM
  • 4,415
  • 1
  • 34
  • 43
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • +1 woohoo - tyvm - it's even shorter than the last version: v.nice – kfmfe04 Feb 22 '13 at 13:18
  • @kfmfe04: I left the indices out of this one, since you can easily write them yourself (see the link or search SO). – Xeo Feb 22 '13 at 13:21
  • @kfmfe04: Note that I fixed a small problem with how the placeholders were generated just now. – Xeo Feb 22 '13 at 13:39
  • thx for the heads-up : btw, do you have any links to understanding specialising `std` and `detail`? I kind of see why the `std` code is there, but I totally don't understand why the `detail` code is there. Is it to hide implementation/access somehow so it doesn't get called by accident? Is the `detail` namespace a convention? – kfmfe04 Feb 22 '13 at 13:54
  • 2
    @kfmfe04: Yeah, `detail` is nothing more than a convention for implementation details. The code in `std` is there to make `std::bind` aware of our placeholders. – Xeo Feb 22 '13 at 14:18
  • 5
    The "Live example" link is dead. Can you show example usage? – Emile Cormier Dec 16 '15 at 18:49
0

This was troubling me a lot, since I had to bind a function in a situation when I did not know the arguments at the time. (A factory such as shown here How to implement serialization in C++)

For example (assume TSubClass::create is static)

template<typename TFactoryClass, typename TArgs...>
class Factory
{
public:
    template<typename TSubClass>
    void register(int id)
    {
         _map.insert(std::make_pair(id, std::bind(&TClass::create, /*how to give TArgs as placeholders??*/)));
    }
}

instead I was able to replace the std::bind with a lambda expression without having to use all these helper classes!

template<typename TFactoryClass, typename TArgs...>
class Factory
{
public:
    template<typename TSubClass>
    void register(int id)
    {
         _map.insert(std::make_pair(id, [](TArgs... args) { TSubClass::create(args...); }));
    }
}

as a bonus, you can also "bind" to constructors with this mechanism

Community
  • 1
  • 1
yano
  • 4,095
  • 3
  • 35
  • 68