15

Suppose I have

typedef std::function<
    double(
        int,
        long
    )
> FooType;

and I want to declare function prototypes for a series of functions that I can slot into a std::function of this type. I know I can write

double foo1(int, long);
double foo2(int, long);

etc., but is there a way I can use FooType somehow when declaring the function prototypes? Something like

FooType::type foo1, foo2;

Perhaps I might have to use (*foo1) or similar? Naturally in the implementation of the function I'd need to spell it out long hand so I can put in some parameters, but writing it as above would keep my header file cleaner.

Michael Bullock
  • 976
  • 6
  • 13

4 Answers4

18

Sure you can, just as always*, using partial specialization:

template <typename> struct fn_sig;
template <typename T> struct fn_sig<std::function<T>> { using type = T; };

Usage:

fn_sig<FooType>::type f;

double f(int a, long b) { return double(a) /  b; }

(You'll obviously need to spell the underlying function type out for the function definition.)



*) Meaning that this is the same answer for any question of the form "can I get the template parameter from a template specializaton".

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
9

writing it as above would keep my header file cleaner

It would be cleaner (with less metaprogramming) to go the other way around: Start with the foo() declaration and create FooType from it.

double foo(int, long);

typedef std::function< decltype(foo) > FooType;

Since there are presumably several such functions, two typedefs might be the optimal factoring:

typedef double FooFn(int, long);
typedef std::function< FooFn > FooType;

FooFn foo1, foo2, foo3;

Of course, in the implementation (.cpp) file the signature needs to be written longhand.

One more thing, note that std::function is a heavyweight generalization of function pointers. If you'll only ever assign ordinary functions into it, a function pointer might be better, and this would be a drop-in replacement:

typedef FooFn * FooType;
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • This is a great idea but something that I omitted from the question is that I have many functions `foo` with the same signature. But I will upvote this, with my apologies. – Michael Bullock Jan 12 '17 at 12:37
  • 1
    @MichaelBullock Got ya covered, see the edit I'm about to post. – Potatoswatter Jan 12 '17 at 12:38
  • 1
    `If you'll only ever assign ordinary functions into it, a function pointer might be better` +1, with the addition that captureless lambdas can also be represented by plain funcptrs. – underscore_d Jan 12 '17 at 18:04
4

You can easily do this by creating a template struct which destructures the type of FooType, matching T in std::function< T >:

template <typename T>
struct destructure;

template <typename T>
struct destructure<std::function<T>>
{
    using type = T;
};

template <typename T>
using destructure_t = typename destructure<T>::type;

After that, you can use destructure_t to declare your function:

destructure_t<FooType> foo;

int main()
{
    foo(1, 20l);
}

Then, you can define it with the regular function syntax:

double foo(int i, long l)
{
    std::cout << i << " " << l << "\n";
    return 0;
}

Your main will print "1 20".

wandbox example

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
0

As an alternative solution to the working one presented by @KerrekSB, you can use a function declaration and an alias as it follows if you want to get a pointer type:

#include <functional>
#include <utility>

template<typename F> F * get(std::function<F>);
template<typename F> using FPtr = decltype(get(std::declval<F>()));

double f(int, long) { return {}; }

int main() {
    using FooType = std::function<double(int, long)>;
    FPtr<FooType> myF = f;
    (void)myF;
}

Or a slightly modified version if you want only the actual type:

#include <type_traits>
#include <functional>
#include <utility>

template<typename F> F * get(std::function<F>);
template<typename F> using MyType = typename std::remove_pointer<decltype(get(std::declval<F>()))>::type;

double f(int, long) { return {}; }

int main() {
    using FooType = std::function<double(int, long)>;
    MyType<FooType> *myF = f;
    (void)myF;
}
Community
  • 1
  • 1
skypjack
  • 49,335
  • 19
  • 95
  • 187