tstenner
tstenner

Reputation: 10291

Instantiate member function templates for multiple types

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:

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?

Upvotes: 3

Views: 2042

Answers (1)

tstenner
tstenner

Reputation: 10291

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.

Upvotes: 1

Related Questions