3

I have a several classes with templated member functions and a predefined list of types they will be used with (Wandbox link:

// main.cpp:
#include "class.h"

int main(int argc, char** argv) {
    A a;
    a.foo(5);
    a.foo(5.);
    B b;
    //b.bar(1,2,3,4); b.bar(1,2,3.,4);
    return 0;
}

// class.h
#pragma once

struct A {
    template<typename T> void foo(T x);
};
struct B {
    template<typename T> void bar(int p1, int p2, T x, int p3);
};


// class.cpp
#include <iostream>
#include "class.h"

template<typename T> void A::foo(T x) {
    std::cout << x << std::endl;
}

// explicit, but very verbose
// template void A::foo(int);
// ...

template<typename T> void ignore(T fn) {/* Use fn? */}

template<class Class>
void instantiate(Class) {
    // List all types the function should be instantiated for here
    ignore(&Class::template foo<int>);
    ignore(&Class::template foo<double>);
}

// works, instantiates A::foo<int> and A::foo<double>
template void instantiate(A);
// How to pass B::foo and additional parameters?
// template void instantiate(B);

Typing out every combination of member function and type to be instantiated with works, but it has several drawbacks:

  • it's tedious
  • the whole function signature with every parameter has to typed out, and
  • function signature changes have to be made in several places
  • adding a single type to the list has to be made for every member function

My workaround as above works on most older compilers I've tested (C++03 compatibility would be a huge plus), but I'm not sure if a smart compiler would be allowed to remove the unused parameter and the function instantiations.

For regular functions, there are workarounds, but as far as I understood, they don't work for member functions.

How can I change my instantiate function to also accept a member function and additional parameters?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
tstenner
  • 10,080
  • 10
  • 57
  • 92
  • Instead of `ignore(...)` you can just do `static_cast(...)`. – Maxim Egorushkin Sep 05 '18 at 09:37
  • 1
    @MaximEgorushkin Not with member function pointers, see https://stackoverflow.com/a/1307379/73299 – tstenner Sep 05 '18 at 09:48
  • I believe you need to do some form recursive template / template metaprogramming. Boost has some good assisting libraries for helping with this, for example in boost MPL / type tuples and perhaps boost variant for some higher level utilization. Unfortunately, it usually requires a good amount of work to make right and it is far from trivial, but in the end i believe you can get to the scaleable solution you are looking after. – darune Sep 05 '18 at 10:05

1 Answers1

1

After much trying and erroring I found something that works even with optimizations enabled (Wandbox):


// main.cpp as above

// class.h
#pragma once

// Create instantiations for templated functions by keeping their addresses
// and therefore forcing the compiler to keep their object code
// attribute((unused)) silences the clang/gcc warning
template <typename T> void ignore(T t) {static __attribute__((used)) T x = t;}

struct A {
    template<typename T> void foo(T x);
    template<typename T> friend void instantiate(T); // let instantiate call the helper function in case it's private
    // helper function that instantiates all member functions
    template<typename T> void helper() { ignore(&A::foo<T>); }
};
struct B {
    template<typename T> void bar(int p1, int p2, T x, int p3);
    // same procedure as above
    template<typename T> friend void instantiate(T);
    template<typename T> void helper() { ignore(&B::bar<T>); }
};


// class.cpp
#include 
#include "class.h"

template void A::foo(T x) {
    std::cout  void B::bar(int, int, T, int) {}

template
void instantiate(Class) {
    // List all types the function should be instantiated for here
    ignore(&Class::template helper);
    ignore(&Class::template helper);
}

template void instantiate(A);
template void instantiate(B);

To avoid more complicated template magic, I added a single template function (template<typename T> void helper()) that gets instantiated by the instantiate function where all desired types for the functions to be instantiated with are listed.

Afterwards, template void instantiate(A) will instantiate all member functions listed in A::helper.

tstenner
  • 10,080
  • 10
  • 57
  • 92