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

a 夏天 提交于 2019-12-28 05:40:55

问题


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.


回答1:


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.




回答2:


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



来源:https://stackoverflow.com/questions/15024223/how-to-implement-an-easy-bind-that-automagically-inserts-implied-placeholders

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!