Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing template function pointer to template as template parameter is too verbose

template<class T, class... TA> Uptr<T, TA> makeUniquePtr(/*...*/) { /*...*/ };
template<class T, class... TA> Sptr<T, TA> makeSharedPtr(/*...*/) { /*...*/ };

template<typename TFPType, TFPType TFP> void func(/*...*/) { /*...*/ }

template<class T, class... TA> void implementation1(/*...*/)
{
    // Can this be reduced to func<&makeUniquePtr, T, TA...>?
    func<decltype(&makeUniquePtr<T, TA...>), &makeUniquePtr<T, TA...>>(/*...*/);
}

template<class T, class... TA> void implementation2(/*...*/)
{
    // Can this be reduced to func<&makeSharedPtr, T, TA...>?
    func<decltype(&makeSharedPtr<T, TA...>), &makeSharedPtr<T, TA...>>(/*...*/);
}

Calling func</*...*/>(/*...*/) is extremely verbose. Is there a way to simply call

func<&makeSharedPtr, T, TA...>(/*...*/)

and internally use

&makeSharedPtr<T, TA...>

without having the user specify it twice?

like image 949
Vittorio Romeo Avatar asked May 29 '14 10:05

Vittorio Romeo


2 Answers

Create a shared maker stateless function object class, like std::less, but with no template parameters itself: instead operator() perfect forwards to your template function.

Pass it instead of your function pointer.

If you need template arguments passed to it, either create a static method to it, or make the entire template class take the arguments and use them in the operator().

Here is an example of late-bound template arguments (live example):

#include <iostream>

// test stubs:
template<typename... Ts>
using UPtr = int;
template<typename... Ts>
using SPtr = double;

template<typename T, typename...TA>
UPtr<T,TA...> makeUniquePtr() {
  std::cout << "makeUniquePtr\n";
  return {};
}
template<typename T, typename...TA>
SPtr<T,TA...> makeSharedPtr() {
  std::cout << "makeSharedPtr\n";
  return {};
}

// perfect forwarding make static functions passed by class name:
struct unique_ptr_maker {
  template<typename T, typename...TA, typename...Args>
  static UPtr<T, TA...> make(Args&&...args) {
    return makeUniquePtr<T, TA...>( std::forward<Args>(args)... );
  }
};
struct shared_ptr_maker {
  template<typename T, typename...TA, typename...Args>
  static SPtr<T, TA...> make(Args&&...args) {
    return makeSharedPtr<T, TA...>( std::forward<Args>(args)... );
  }
};

// your `func`.  It can take args or whatever:    
template<typename maker, class T, class... TA> void func() {
  std::cout << "func\n";
  maker::template make<T, TA...>();
}

// a sample of implementation 1 and 2:
template<class T, class... TA> void implementation1()
{
  func<unique_ptr_maker, T, TA...>();
}
template<class T, class... TA> void implementation2()
{
  func<shared_ptr_maker, T, TA...>();
}
// and, to test, always instantiate:
int main() {
  implementation1<int, double>();
  implementation2<int, char>();
  return 0;
}

with much of the functionality stubbed out, as you did not detail what it was supposed to do in your question.

like image 73
Yakk - Adam Nevraumont Avatar answered Nov 10 '22 02:11

Yakk - Adam Nevraumont


Making a few assumptions on intended use from your names, I would try something like this:

template<class T>
struct makeUniquePtr {
    template<class... TA>
    Uptr<T, TA> operator()(TA&&...) const { /*...*/ }
};

template<class T>
struct makeSharedPtr {
    template<class... TA>
    Sptr<T, TA> operator()(TA&&...) const { /*...*/ }
};

template<class F> void func(/*...*/) { /*...*/ }

template<class T, class... TA> void implementation1(/*...*/)
{
    func<makeUniquePtr<T>>(/*...*/);
}

template<class T, class... TA> void implementation2(/*...*/)
{
    func<makeSharedPtr<T>>(/*...*/);
}

Unlike Yakk's solution, here parameters TA... are automatically deduced, as is the case for std::make_unique etc.

EDIT My reference to Yakk's solution was made before that solution was edited. I now see it is expanded.

like image 22
iavr Avatar answered Nov 10 '22 03:11

iavr