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