3

Given a class

class Foo {
 public:
  std::shared_ptr<const Bar> quux(const std::string&, std::uint32_t);
}

I can declare an std::function that has the same interface:

std::function<std::shared_ptr<const Bar>(const std::string&, std::uint32_t)> baz = ...

Is there a way of compressing that declaration such that the template arguments to std::function are derived from the declaration of that method, something like:

std::function<functype(X::quux)> baz = ...

where functype is an imaginary C++ operator similar to decltype. Is there a way to do this / does have such a capability?

I do see that the method has a slightly different signature actually as it would also take a reference/pointer to the this object; it would be fine for me to derive such a signature too.

scravy
  • 11,904
  • 14
  • 72
  • 127
  • 1
    strictly speaking, no, this is not the "same" interface. You need an object of the class to call a member method. The `std::function` as you declared it is a free function. small but big difference – 463035818_is_not_an_ai May 07 '19 at 11:52
  • 4
    If you have a free function `quux`, then `decltype(quux)` will give you the function type, so `std::function baz = quux` will work. – Holt May 07 '19 at 11:55
  • @Scheff We have better ways nowadays than the pre-C++11 solutions there... – Max Langhof May 07 '19 at 11:57
  • @Holt Actually, [it is](https://stackoverflow.com/questions/47455800/how-do-you-get-the-type-of-a-member-function). – Passer By May 07 '19 at 12:18
  • @PasserBy Right, my point was that you cannot store `quux` directly inside `std::function(const std::string&, std::uint32_t)>`. – Holt May 07 '19 at 12:58
  • @user463035818 That's what I'm pointing out in my last paragraph. – scravy May 07 '19 at 13:57
  • C++17 class template argument deduction gives you just that: it can infer it's template argument with a given function object or function pointer. – Guillaume Racicot May 07 '19 at 16:38
  • @GuillaumeRacicot I am stuck with C++11 in the project I am working on. The accepted solution worked just fine for me though :-) – scravy May 07 '19 at 16:42

1 Answers1

6

Yes, you can. Adapting How do I get the argument types of a function pointer in a variadic template class? to your request, we get:

template<typename T> 
struct function_traits;  

template<typename R, typename C, typename ...Args> 
struct function_traits<R(C::*)(Args...)>
{
    using type = std::function<R(Args...)>;
};

class Bar;

class Foo {
 public:
  std::shared_ptr<const Bar> quux(const std::string&, std::uint32_t);
};

int main()
{
   std::cout << std::is_same<
     std::function<std::shared_ptr<const Bar>(const std::string&, std::uint32_t)>,
     function_traits<decltype(&Foo::quux)>::type>::value << std::endl;
}

To make it work with constant methods you will need another specialization:

template<typename R, typename C, typename ...Args> 
struct function_traits<R(C::*)(Args...) const>
{
    using type = std::function<R(Args...)>;
};

But you will get problems with overloaded methods, because in order to resolve overloading you will need to specify the arguments anyway.

aparpara
  • 2,171
  • 8
  • 23
  • Works like a charm! I have never seen this `C::*` syntax, the asterisk in particular - what does it do? – scravy May 07 '19 at 16:27
  • @scravy, it is how [pointers to member functions](https://en.cppreference.com/w/cpp/language/pointer#Pointers_to_member_functions) are declared. – aparpara May 07 '19 at 17:44