Michael Bullock
Michael Bullock

Reputation: 994

Using the type of the function in std::function to declare multiple functions of that type

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.

Upvotes: 16

Views: 2811

Answers (4)

Potatoswatter
Potatoswatter

Reputation: 137850

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;

Upvotes: 9

skypjack
skypjack

Reputation: 50550

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;
}

Upvotes: 0

Vittorio Romeo
Vittorio Romeo

Reputation: 93304

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

Upvotes: 4

Kerrek SB
Kerrek SB

Reputation: 477150

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".

Upvotes: 19

Related Questions