12

Given the following templated function, how can I change it to take advantage of variadic templates? That is to say, to replace std::bind placeholders with a variadic parameter instead of P1 and P2? At the moment I have one of these functions per arity, with arity zero having no P parameter, up to arity 9 having P1 to P9 parameters. I was hoping to collapse this into a single function if possible.

template<typename R, typename T, typename U, typename P1, typename P2>
void Attach(R (T::*f)(P1, P2), U p)
{
    AttachInternal(p, std::bind(f, 
                                p.get(), 
                                std::placeholders::_1, 
                                std::placeholders::_2));
}
Robinson
  • 9,666
  • 16
  • 71
  • 115

2 Answers2

21

You can (partially) specialize std::is_placeholder for specializations of a custom template. This way, you can introduce a placeholder generator via the usual int_sequence technique.

From [func.bind.isplace]/2

The implementation shall provide a definition that has the BaseCharacteristic of integral_constant<int, J> if T is the type of std::placeholders::_J, otherwise it shall have a BaseCharacteristic of integral_constant<int, 0>. A program may specialize this template for a user-defined type T to have a BaseCharacteristic of integral_constant<int, N> with N > 0 to indicate that T should be treated as a placeholder type.

The usual int_sequence:

#include <cstddef>

template<int...> struct int_sequence {};

template<int N, int... Is> struct make_int_sequence
    : make_int_sequence<N-1, N-1, Is...> {};
template<int... Is> struct make_int_sequence<0, Is...>
    : int_sequence<Is...> {};

The custom placeholder template and specialization of is_placeholder:

template<int> // begin with 0 here!
struct placeholder_template
{};

#include <functional>
#include <type_traits>

namespace std
{
    template<int N>
    struct is_placeholder< placeholder_template<N> >
        : integral_constant<int, N+1> // the one is important
    {};
}

I'm not sure where to introduce the 1; the places I considered are all not optimal.

Using it to write some binder:

template<class Ret, class... Args, int... Is>
void my_bind(Ret (*p)(Args...), int_sequence<Is...>)
{
    auto x = std::bind(p, placeholder_template<Is>{}...);
    x( Args(42)... );
}

template<class Ret, class... Args>
void my_bind(Ret (*p)(Args...))
{
    my_bind(p, make_int_sequence< sizeof...(Args) >{});
}

Usage example of the binder:

#include <iostream>

void foo(double, char, int) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
void bar(bool, short) { std::cout << __PRETTY_FUNCTION__ << "\n"; }

int main()
{
    my_bind(foo);
    my_bind(bar);
}
dyp
  • 38,334
  • 13
  • 112
  • 177
  • I wonder why such a template `placeholder_template` isn't in the Standard. – dyp Jan 17 '14 at 18:42
  • Maybe because `std::integer_sequence` is only C++14 ? – Matthieu M. Jan 17 '14 at 19:01
  • @MatthieuM. There is only an implementation-defined number of (standard) placeholders. I'd guess that a placeholder template could generally be useful, as you can reliably create a certain number of placeholders (even w/o the integer sequence technique). – dyp Jan 17 '14 at 19:07
  • I completely agree, and besides the issue of explicitly naming them there is also the issue that even small pack expansions are more easily realized with a real pack expansion than simulating it via successive specializations or an attempt at recursion. – Matthieu M. Jan 17 '14 at 19:40
  • OK, thanks for that. It's interesting from a technical perspective, but I'm not sure I'm going to use it because it's a bit too advanced and it'll make my code harder to maintain for others. – Robinson Jan 20 '14 at 10:12
  • I was just wondering: Why the placeholders use `int` instead of `std::size_t`? I think the latter is more appropiate. – Manu343726 Feb 09 '14 at 18:37
  • @Manu343726 *shrug* There's no need to have (at least) 65535 possible arguments instead of 32767. Some committee members are also unhappy with the usage of unsigned types for anything but bit-twiddling. (Maybe you could look into a StdLib implementation or ask it as a separate question; I don't have a definite answer.) – dyp Feb 09 '14 at 18:43
  • This is extremely helpful, thank you. – neal Dec 30 '21 at 19:58
4

I would like to propose a more simple solution to the problem to bind a member function to a variable number of placeholders:

template<typename R, typename T, typename U, typename... Args>
std::function<R(Args...)> Attach(R (T::*f)(Args...), U p)
{
    return [p,f](Args... args)->R { return (p->*f)(args...); };
};

A simple example of the usage looks like that

class CrazyAdd
{
public:

    CrazyAdd(double val)
    : m_crazyPart(val)
    {}

    double add(double a, double b)
    {
        return a+b+m_crazyPart;
    }

private:
    double m_crazyPart;
};

void main() {

    CrazyAdd myAdd(39);

    // Create a function object that is bound to myAdd
    auto f = Attach(&CrazyAdd::add, &myAdd);

    // Call the function with the parameters    
    std::cout << f(1,2) << std::endl;   // outputs 42
}

Personally I think that's another good example why Scott Meyer recommends lambdas instead of std::bind.

sebgr
  • 41
  • 3
  • Finally, after a few days, full of searching I finally found a working solution! Thank you!!! – Espeon May 01 '21 at 13:12