Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template argument deduction for pointer to member function?

I am trying to build a statically bound delegate class, where the member function is bound at compile time, thereby aiding optimisation.

I have the following code which works exactly how I want it to:

#include <iostream>

namespace thr {

template<typename T, T func>
struct delegate;

template<typename R,
         typename C,
         typename... A,
         R  (C::* mem_fun)(A...)>
struct delegate<R(C::*)(A...), mem_fun>
{ 
  delegate(C* obj_)
    : _obj(obj_)
  {}

  R operator()(A... a)
  {
    return (_obj->*mem_fun)(a...);
  }
 private:
  C* _obj;
};

} // namespace thr

struct foo
{
  double bar(int i, int j)
  { 
    return (double)i / (double)j;
  } 
};

int main()
{
  foo f;

  typedef thr::delegate<decltype(&foo::bar), &foo::bar> cb;      
  cb c(&f);

  std::cout << c(4, 3);
  return 0;
}

However, the usage is not very elegant:

thr::delegate<decltype(&foo::bar), &foo::bar>

I would like to use a function template which deduces the template parameters and returns a delegate instance; something along the lines of (this code does not compile):

template<typename C, typename T, T func>
thr::delegate<T, func> bind(T func, C* obj)
{
  return thr::delegate<decltype(func), func>(obj);
} 

This would allow for more elegant syntax:

auto cb = bind(&foo::bar, &f);

Is it possible to deduce a non-type parameter in a function template?

Is what I'm trying to achieve even possible?

like image 210
Steve Lorimer Avatar asked Jan 30 '12 18:01

Steve Lorimer


2 Answers

Would std::function help? http://www2.research.att.com/~bs/C++0xFAQ.html#std-function Your example looks quite close.

I think the compiler supplied STL does pretty horrible things to make it work smoothly. You may want to have a look at as an example before giving up.

Edit: I went out and tried what you try to accomplish. My conclusion is a compile error:

  • The return type of the bind (delegate) must name the pointer to member because it is your own requirement.
  • bind should accept the name of the pointer to member to be elegant (i.e. your requirement)
  • Compiler requires you to not shadow the template parameter with a function parameter or use the name in both parameters and return type.

Therefore one of your requirements must go.

Edit 2: I took the liberty of changing your delegate so bind works as you wish. bind might not be your priority though.

#include <iostream>

namespace thr {


template<typename C,typename R,typename... A>
struct delegate
{ 
 private:
  C* _obj;
  R(C::*_f)(A...);
  public:
  delegate(C* obj_,R(C::*f)(A...))
    : _obj(obj_),_f(f)
  {}

  R operator()(A... a)
  {
    return (_obj->*_f)(a...);
  }

};

} // namespace thr

template<class C,typename R,typename... A> thr::delegate<C,R,A...> bind(R(C::*f)(A...),C* obj){
    return thr::delegate<C,R,A...>(obj,f);
}

struct foo
{
  double bar(int i, int j)
  { 
    return (double)i / (double)j;
  }
};

int main()
{
  foo f;
  auto c = bind(&foo::bar, &f);
  std::cout << c(4, 6);
  return 0;
}
like image 181
artificialidiot Avatar answered Sep 27 '22 16:09

artificialidiot


It is possible to deduce other entities than types in a function signature, but function parameters themselves cannot then be used as template parameters.

Given:

template <size_t I> struct Integral { static size_t const value = I; };

You can have:

template <size_t N>
Integral<N> foo(char const (&)[N]);

But you cannot have:

Integral<N> bar(size_t N);

In the former case, N as the size of the array is part of the type of the argument, in the latter case, N is the argument itself. It can be noticed that in the former case, N appeared in the template parameters list of the type signature.

Therefore, if indeed what you want is possible, the member pointer value would have to appear as part of the template parameter list of the function signature.

There may be a saving grace using constexpr, which can turn a regular value into a constant fit for template parameters:

constexpr size_t fib(size_t N) { return N <= 1 ? 1 : fib(N-1) + fib(N-2); }

Integral<fib(4)> works;

But I am not savvy enough to go down that road...

I do however have a simple question: why do you think this will speed things up ? Compilers are very good at constant propagation and inlining, to the point of being able to inline calls to virtual functions when they can assess the dynamic type of variables at compilation. Are you sure it's worth sweating over this ?

like image 34
Matthieu M. Avatar answered Sep 27 '22 16:09

Matthieu M.