1

I'm experimenting with using C++'s template template features to reduce code duplication in a small unit test segment in my code, to no success. I've seen these answers to similar questions, but still can't figure out what my compiler is telling me.

I deal with some classes that do numerical processing at different precisions, and so I thought that I could generalize the duplicate code to a templated function, so it could be easily called by the class tests, as in:

template<typename T, size_t S>
void CompareArrays(
    std::array<T, S> const &input,
    std::array<T, S> const &output) {...}

template <typename T>
void SomeClassTest::SomeClassIdentity() const {
    SomeClass<T> scZero;
    std::array<T, 1> const input = { 1 };
    auto output = scZero.Process(input);
    CompareArrays(input, output); // does the actual printing
}

And then, test a lot of operations similar to SomeClassTest::SomeClassIdentity with a template template function:

template<template <typename> typename F>
void CheckAgainstNumericTypes() {
    std::cerr << "Testing with char...";
    F<char>();
    std::cerr << "Testing with short...";
    F<short>();
    std::cerr << "Testing with int...";
    F<int>();
    std::cerr << "Testing with float...";
    F<float>();
    std::cerr << "Testing with double...";
    F<double>();
}

The problem is, every time I try to invoke CheckAgainstNumericTypes, the compiler will refuse with the error message "Invalid Template Argument for 'F', type expected", as in the example below:

void SomeClassTest::Test() const {
    std::cerr << "Some Class Tests #1 - base/identity case" << std::endl;
    CheckAgainstNumericTypes<SomeClassIdentity>();
    ...

I tried making CheckAgainstNumericTypes a member function of SomeClass, prepending the template argument with SomeClass::, adding () to the end of it, and even replacing the inner typedef by void(*F)(void); all to no avail.

I have two questions, then:

  • How can I transform my member function into a type so it is accepted by the template?
  • Is there any other way of accomplishing the same desired syntactic result in SomeClassTest::Tests() without using template templates?
max66
  • 65,235
  • 10
  • 71
  • 111
MVittiS
  • 434
  • 3
  • 13

1 Answers1

3

I'm experimenting with using C++'s template template features to reduce code duplication in a small unit test segment in my code, to no success

Well... it seems to me that you haven't understand what a template-template is.

If I understand correctly, you think that when you write

template <template <typename> typename F>
void CheckAgainstNumericTypes() {
    F<char>();
}

you're calling a function F<char>().

Wrong.

That F<char>() is the creation of a temporary object of type F<char>, default initialized.

Instead of F<char>(), you can write F<char>{}, so it's more clear that this isn't a call of a template function.

At this point, I don't know if make sense to respond to your following answer but...

How can I transform my member function into a type so it is accepted by the template?

You can't. Not as type.

You can pass a function - or a static member of a class/struct, in a not-type template argument (see first answer you linked).

But a non static method (a non static member function) is another type of beast and require a object of the class to call it.

The best I can imagine is something as follows (caution: code not testes)

template <typename T, void (T::*M)()>
void foo (T & d)
 { d.*M(); }

and you can call it

foo<SomeClassTest, &SomeClassTest::SomeClassIdentity>(someClassTestObject);

As you can see, you can pass the class and the pointer to the method as template parameters (a type template parameters the first one, a value the second one) but you need an object of type SomeClassTest as argument (someClassTestObject).

If you want to works only with members of a specific class (SomeClassTest, in you case) you can avoid the type template argument and simplify as follows

template <void (SomeClassTest::*M)()>
void foo (SomeClassTest & d)
 { d.*M(); }

// ...

foo<&SomeClassTest::SomeClassIdentity>(someClassTestObject);

Is there any other way of accomplishing the same desired syntactic result in SomeClassTest::Tests() without using template templates?

You can't using template-template parameters.

max66
  • 65,235
  • 10
  • 71
  • 111
  • My understanding was that I was instantiating a nameless, templated object with `F()` (which happens to call some code), and that seems to be the case - treating it as a function is just syntactic sugar. The problem, however, is the one you've pointed out - there's no way to correctly call a method without an attached object... even if said object has no instance variables, which is my case; I naively though I could get around this because the caller is from the same object. Making the methods static will likely make the most sense, then. – MVittiS Oct 24 '18 at 00:48
  • @MVittiS - " (which happens to call some code)," - which call the default (without parameter) constructor. – max66 Oct 24 '18 at 02:12
  • Just an update - I tried making the members static, and it still didn't work. I had to pack them into structs with an `operator()` overload so they would work as I intended, like the linked answers suggest. – MVittiS Oct 24 '18 at 14:12
  • @MVittiS - a member static is managed in a different way; see the first answer you linked; about `operator()`, you have to write something as `F{}()` (or `F()()`, if you prefer). Not a great problem (I suppose) but, this way, you have to instantiate an object that is, substantially, usefulness. I propose another way: write a static `func()` function and call it as `F::func()`; this way you can pass `F` as template-template parameter and you can call a method in `F`. without instantiate an `F` object. – max66 Oct 24 '18 at 16:14