前提
次のインターフェイスを提供する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();
}
これにより、次のリンカーエラーが発生します(OSXではg++(GCC)4.5.1を使用しますが、コンパイラバージョン/プラットフォームの他のほとんどの組み合わせで機能します)。
アーキテクチャx86_64の未定義のシンボル:
"void my_callback<int>(void*)", referenced from: _main in ccYLXc5w.o
私はそれを理解できると思います。
最初の「ソリューション」</h2>
これは、テンプレートを明示的にインスタンス化することで簡単に修正できます。
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();
}
2番目の「ソリューション」</h2>
関数テンプレートは、関数を呼び出すことによって暗黙的にインスタンス化されます。それではそれを実行しましょう。ただし、呼び出しが実際に実行されていないことを確認してください(関数には副作用があります)。
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でコードを実行する(コンパイルするためのキャストが追加されています)