C-callback к шаблону функции: явно создайте шаблон

Предпосылка

Im, используя библиотеку 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 , но работает с большинством других комбинаций версии/платформы компилятора):

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

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

который я считаю понятным.

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

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

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

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

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 С++ запрещает кастинг между указателем на функцию и указателем на объект

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

Запуск кода на ideone (с добавленным приложением для его компиляции)

Ответы

Ответ 1

POSIX рекомендует следующий способ металирования между типами указателей функций и типами указателей объектов (который является undefined в 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;

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

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

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

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

EDIT: Позвольте мне добавить еще один вариант в этот микс. Создайте 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);
}

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