C++ Single function pointer for all template insta

2019-07-08 05:46发布

问题:

Is there a concise way to point to all instances of a templated function without using macros?

I have several templated functions that I want to test across a variety of types:

template<typename T>
void function1() {
  return;
}

template<typename T>
void function2() {
  return;
}

template<typename T>
void function3() {
  return;
}

I can do this with a macro:

#define TEST_ACROSS_TYPES(fn) \
fn<int>();  \
fn<bool>(); \
fn<char>(); \
fn<double>(); \

TEST_ACROSS_TYPES(function1);
TEST_ACROSS_TYPES(function2);

But, (1) Macros are ugly and hard for others to follow, and (2) I'm using CATCH, which doesn't play nice when using macros to set up test cases.

Is there a way to do something like this:

void testAcrossTypes(SomeType f) {
  f<int> ();
  f<bool> ();
  f<char> ();
  f<double> ();
}

which seems much cleaner, except for the problem of defining SomeType. This question (How to define typedef of function pointer which has template arguments) explains how to define a pointer to a templated function; but, requires that the template arguments be specified.


For clarification: Imagine function1, function2, and function3 each test a different templated function. Each function needs to be tested for int, byte, char, double, etc. I want to avoid having to explicitly set up many (i.e. num_functions * num_types) tests for each function. Instead, I want to have a single method that points to the test function (function1, function2, etc.) and runs it for each template type, thus consolidating

function1<int>();
function1<byte>();
function1<char>();
function1<double();
...
function2<int>();
function2<byte>();
function2<char>();
function2<double();
...
function3<int>();
function3<byte>();
function3<char>();
function3<double();
...

into just one call per test function

testAcrossTypes(function1);
testAcrossTypes(function2);
testAcrossTypes(function3);

回答1:

What you are trying to accomplish with

void testAcrossTypes(SomeType f) {
  f<int> ();
  f<bool> ();
  f<char> ();
  f<double> ();
}

would be possible if SomeType could be a template template argument. However, the standard does not allow function templates as template template argument.

From the C++11 Standard:

14.3.3 Template template arguments

1 A template-argument for a template template-parameter shall be the name of a class template or an alias template, expressed as id-expression.

Your best option is to use functors instead of functions. Example:

template<typename T>
struct function1
{
   void operator()() {
      return;
   }
};

template<typename T>
struct function2
{
   void operator()() {
      return;
   }
};

template < template <typename> class F>
void testAcrossTypes() {
  F<int>()();
  F<bool>()();
  F<char>()();
  F<double>()();
}

int main()
{
   testAcrossTypes<function1>();
   testAcrossTypes<function2>();
}


回答2:

You can accomplish it by means of a type-erased functor, like the one in the following example:

#include<vector>

template<typename T>
void function1() { }

template<typename T>
void function2() { }

template<typename T>
void function3() { }

struct Test {
    template<typename T>
    static void proto() {
        function1<T>();
        function2<T>();
        function3<T>();
    }

    void operator()() {
        for(auto &f: vec) f();
    }

    template<typename... T>
    static Test create() {
        Test test;
        int arr[] = { (test.vec.emplace_back(&proto<T>), 0)... };
        (void)arr;
        return test;
    }

    using func = void(*)(void);
    std::vector<func> vec;
};

void testAcrossTypes(Test test) {
    test();
}

int main() {
    testAcrossTypes(Test::create<int, bool, char, double>());
}

It's easy to modify in both cases:

  • New functions require to be added to the proto static member method and that's all

  • Adding a new type is a matter of using it when call create, as shown in the above example

The functor will keep in charge of creating the N*M calls to be executed.
Moreover, you don't need to move your functions in a bunch of structs to be able to use them.