помещение
Я использую библиотеку 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);
К сожалению, это неприменимо в моем реальном коде, так как обратный вызов регистрируется внутри шаблона функции, и я не знаю, для какого набора аргументов шаблона будет вызываться эта функция, поэтому я могу t предоставить явные экземпляры для всех из них (я программирую библиотеку). Итак, мой реальный код выглядит примерно так:
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 (с добавлением приведения для компиляции)
if(false)
является избыточным - так какstatic_cast
все равно вызывает создание экземпляра. - person Nim   schedule 18.07.2011void (*)(void*)
в обратном вызове регистра? - person David Rodríguez - dribeas   schedule 18.07.2011if(false) { my_callback<T>(value); }
вместо предположения, что все обратные вызовы будут использовать значения указателя? По крайней мере, это немного менее уродливо. - person Chris Lutz   schedule 18.07.2011void*
, я думаю, он всегда будет использовать аргумент-указатель. - person Matthieu M.   schedule 18.07.2011(void *)
вместоreinterpret_cast<void *>
может избавиться от предупреждения. Если это не так, вы всегда можете использовать союз. - person Chris Lutz   schedule 18.07.2011NULL
вместо необработанного 0. Можно передать число как указатель, интерпретируя значение указателя как целое число, но поскольку OP показан код, передающий poInter вint
, это кажется маловероятным. Ну да, все иногда ошибаются. - person Chris Lutz   schedule 18.07.2011static_cast
илиreinterpret_cast
(или что-то еще, что я забыл, но что здесь не применяется). Актерский состав еще исполняется. - person Konrad Rudolph   schedule 18.07.2011