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

помещения

я использую библиотеку C (из C++), которая предоставляет следующий интерфейс:

void register_callback(void* f, void* data);
void invoke_callback();

теперь мне нужно зарегистрировать шаблон функции обратного вызова, и это вызывает у меня проблемы. Рассмотрим следующий код:

template <typename T> void my_callback(void* data) { … }

int main() {
    int ft = 42;
    register_callback(reinterpret_cast<void*>(&my_callback<int>), &ft);
    invoke_callback();
}

это дает мне следующую ошибку компоновщика (используя g++ (GCC) 4.5.1 на OS X но работает на большинстве других комбинаций версии компилятора / платформы):

неопределенные символы для архитектуры x86_64:

"void my_callback<int>(void*)", referenced from:  
  _main in ccYLXc5w.o

что я нахожу понятным.

первое "решение"

это легко исправить, явно создав экземпляр шаблона:

template void my_callback<int>(void* data);

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

template <typename T>
void do_register_callback(T& value) {
    register_callback(reinterpret_cast<void*>(my_callback<T>), &value);
    // Other things …
}

int main() {
    int ft = 42;
    do_register_callback(ft);
    invoke_callback();
}

второе "решение"

шаблон функции неявно создается путем вызова функции. Итак, давайте сделаем это, но убедитесь, что вызов фактически не выполняется (функция имеет побочные эффекты):

template <typename T>
void do_register_callback(T& value) {
    if (false) { my_callback<T>(0); }
    register_callback(reinterpret_cast<void*>(my_callback<T>), &value);
}

этой кажется для работы, даже с включенными оптимизациями (так, что мертвая ветвь будет удалена компилятор.) Но я не уверен, что это когда-нибудь не сломается. Я также нахожу это очень уродливым решением, которое требует пояснительного комментария длины, чтобы какой-то будущий сопровождающий не удалил этот явно ненужный код.

вопрос

как создать экземпляр шаблона, для которого я не знаю аргументов шаблона? этот вопрос, очевидно, нонсенс: я не могу. - Но есть ли хитрый способ обойти это?

кроме этого, мой решение гарантирует успеха?

бонус вопрос

код (в частности, тот факт, что я бросил указатель на функцию к void*) также выдает следующее предупреждение:

ISO C++ запрещает кастинг между указателем на функцию и указателем на объект

при компиляции с -pedantic. Могу ли я как-то избавиться от предупреждения, без написания строго типизированной оболочки C для библиотеки (который невозможно в моей ситуации)?

запуск кода на ideone (с добавленным приведением, чтобы сделать его компиляцией)

3 ответов


POSIX рекомендует следующий способ приведения между типами указателей функций и типами указателей объектов (который не определен в C99):

typedef void function_type(void*);
function_type *p_to_function = &my_callback<T>;
void* p_to_data = *(void**)&p_to_function;

// Undefined:
// void* p_to_data = (void*)p_to_function;

обратите внимание, что в C++-land это будет выполнять reinterpret_cast<void**>(&p_to_function) С function_type**. Это не неопределенно, а вместо этого определено реализацией, в отличие от reinterpret_cast<void*>(p_to_function). Поэтому, вероятно, лучше всего написать c++-совместимый код, который полагается на реализацию.


по-видимому, реальной проблемой было отсутствие static_cast в моем исходном коде:

register_callback(reinterpret_cast<void*>(&my_callback<int>), &ft);

этой compiles отлично, но вызывает ошибку liker при использовании GCC 4.5. Он даже не компилируется при использовании GCC 4.2, вместо этого давая следующую ошибку компиляции:

недостаточно контекстной информации для определения типа

как только эта" контекстная информация " предоставляется, код компилируется и ссылки:

register_callback(reinterpret_cast<void*>(
    static_cast<void(*)(void*)>(my_callback<int>)), &value);

Я понятия не имею, действительно ли требуется приведение и (если да), почему GCC 4.5 позволяет мне оставить его, а затем не создает экземпляр шаблона. Но, по крайней мере, я получил код для компиляции, не прибегая к хаки.


это должно работать:

template <typename T>
void do_register_callback(T& value) {
   void (*callback)(void*) = my_callback<T>;
   register_callback(reinterpret_cast<void*>(callback), &value);
}

первая строка заставляет компилятор создавать экземпляр этой функции для создания адреса, который вы можете с радостью передать.

EDIT: позвольте мне добавить еще один вариант в этот микс. Make my_callback статический член шаблона класса - что-то вроде следующего:

template <typename T>
struct foo
{
static void my_callback(void* data) {
    T& x = *static_cast<T*>(data);
    std:: cout << "Call[T] with " << x << std::endl;
}
};

теперь, в вашем регистре, вам даже не нужен "бросок".

template <typename T>
void do_register_callback(T& value) {
   register_callback(reinterpret_cast<void*>(&foo<int>::my_callback), &value);
}

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