Создание экземпляров шаблонов функций-членов для нескольких типов

У меня есть несколько классов с шаблонными функциями-членами и предопределенный список типов, с которыми они будут использоваться (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);

Ввод каждой комбинации функции-члена и типа для создания экземпляра работает, но у этого есть несколько недостатков:

  • это утомительно
  • должна быть напечатана вся сигнатура функции с каждым параметром, и
  • изменения сигнатуры функции должны быть сделаны в нескольких местах
  • добавление одного типа в список должно выполняться для каждой функции-члена

Мой обходной путь, как указано выше, работает на большинстве старых компиляторов, которые я тестировал (совместимость с C ++ 03 была бы огромным плюсом), но я не уверен, будет ли разрешено умному компилятору удалить неиспользуемый параметр и экземпляры функций.

Для обычных функций существуют обходные пути, но, насколько я понял, они не работают для функций-членов.

Как я могу изменить свою функцию instantiate, чтобы она также принимала функцию-член и дополнительные параметры?


person tstenner    schedule 05.09.2018    source источник
comment
Вместо ignore(...) вы можете просто сделать static_cast<void>(...).   -  person Maxim Egorushkin    schedule 05.09.2018
comment
@MaximEgorushkin Не с указателями на функции-члены, см. stackoverflow.com/a/1307379/73299   -  person tstenner    schedule 05.09.2018
comment
Я считаю, что вам нужно выполнить рекурсивное метапрограммирование шаблона / шаблона. У Boost есть несколько хороших вспомогательных библиотек, которые могут помочь с этим, например, для ускорения кортежей MPL / type и, возможно, варианта повышения для более высокого уровня использования. К сожалению, для исправления этого обычно требуется большой объем работы, и это далеко не тривиально, но в конечном итоге я считаю, что вы можете получить масштабируемое решение, которое вам нужно.   -  person darune    schedule 05.09.2018


Ответы (1)


После долгих попыток и ошибок я нашел то, что работает даже при включенной оптимизации (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);

Чтобы избежать более сложной магии шаблонов, я добавил одну функцию-шаблон (template<typename T> void helper()), которая создается функцией instantiate, где перечислены все желаемые типы для функций, которые будут созданы.

После этого template void instantiate(A) создаст экземпляры всех функций-членов, перечисленных в A::helper.

person tstenner    schedule 05.09.2018