一応いい感じに動かすことができたので解決にします。
以下は実装とサンプルコードです。
コード:
#include <iostream>
#include <type_traits>
//関数が定義されているかの判定
#define IS_DEFINED_FUNCTION(Function, Name)\
struct is_defined_function_##Name {\
template <class __Type>\
static auto tester(__Type) -> decltype(static_cast<decltype(Function)*>(&Function), std::true_type()) {}\
static std::false_type tester(...) {}\
static constexpr bool value = decltype(tester(nullptr))::value;\
}
//Function1が存在するならばそれを実行し、存在しないならばFunction2を実行
#define CALL_DEFINED_FUNCTION(Name, Function1, Function2)\
struct call_definded_function_##Name {\
IS_DEFINED_FUNCTION(Function1, Name);\
template <bool flag = is_defined_function_##Name::value>\
struct call_struct {\
template <class... __Args>\
static auto __call_function(__Args... args) {\
return Function1(std::forward<__Args>(args)...);\
}\
};\
template <>\
struct call_struct<false> {\
template <class... __Args>\
static auto __call_function(__Args... args) {\
return Function2(std::forward<__Args>(args)...);\
}\
};\
template <class... __Args>\
static auto call_function(__Args... args) {\
return call_struct<>::__call_function(std::forward<__Args>(args)...);\
}\
}
//以下サンプルコード
//デフォルトで呼び出されるべき関数の定義されている構造体
template <class T>
struct default_function {
using value_type = T;
template <int N>
static void __function1(value_type x) {
std::cout << "called function1 of default_function." << std::endl;
std::cout << "stream output for " << x << " and " << N << "." << std::endl << std::endl;
}
static void __function2(value_type x) {
std::cout << "called function2 of default_function." << std::endl;
std::cout << "stream output for " << x << "." << std::endl << std::endl;
}
static void __function3(value_type x) {
std::cout << "called function3 of default_function." << std::endl;
std::cout << "stream output for " << x << "." << std::endl << std::endl;
}
//実際にはたくさんの関数が定義されている
};
//特定の場合に呼び出される関数の定義されている構造体
template <class T>
struct expert_function {};
template <>
struct expert_function<int> {
using value_type = int;
template <int N>
static void __function1(value_type x) {
std::cout << "called function1 of expert_function<int>." << std::endl;
std::cout << "stream output for " << x << " and " << N << "." << std::endl << std::endl;
}
static void __function3(value_type x) {
std::cout << "called function3 of expert_function<int>." << std::endl;
std::cout << "stream output for " << x << "." << std::endl << std::endl;
}
};
namespace _temp {
//関数を呼び出すための中継点
//function1用
template <class T, int N>
struct call_function1 {
CALL_DEFINED_FUNCTION(f, expert_function<T>::__function1<N>, default_function<T>::__function1<N>);
//分岐させた関数を呼び出す
static void __function1(T x) {
return call_definded_function_f::call_function(x);
}
};
//function2用
template <class T>
struct call_function2 {
CALL_DEFINED_FUNCTION(f, expert_function<T>::__function2, default_function<T>::__function2);
//分岐させた関数を呼び出す
static void __function2(T x) {
return call_definded_function_f::call_function(x);
}
};
//function3用
template <class T>
struct call_function3 {
CALL_DEFINED_FUNCTION(f, expert_function<T>::__function3, default_function<T>::__function3);
//分岐させた関数を呼び出す
static void __function3(T x) {
return call_definded_function_f::call_function(x);
}
};
}
//expert_functionで定義されていればそちらを呼び出す
template <int N, class T>
void function1(T x) {
return _temp::call_function1<T, N>::__function1(x);
}
template <class T>
void function2(T x) {
return _temp::call_function2<T>::__function2(x);
}
template <class T>
void function3(T x) {
return _temp::call_function3<T>::__function3(x);
}
int main() {
//expert_functionのfunctionが呼び出されるはず
function1<5>(int(1));
//default_functionのfunctionが呼び出されるはず
function1<5>(double(1.5));
//default_functionのfunctionが呼び出されるはず
function2(int(1));
//default_functionのfunctionが呼び出されるはず
function2(double(1.5));
//expert_functionのfunctionが呼び出されるはず
function3(int(1));
//default_functionのfunctionが呼び出されるはず
function3(double(1.5));
return 0;
}
注意すべき点としては、defineで定義されているクラスのテンプレート引数名と同名のものをマクロの引数に入れてはいけないこととです。また、グローバル領域の汚染は適当な名前空間を用意することで暫定的に解決したことにしています。
普通にソフトウェアを開発するにはまず必要ないことですが、ライブラリ開発で関数の実体が存在するときとしないときで処理を分けることによって、開発の拡張性を生み出そうという考えの元、作成しました。
恐らくSFINAEをうまく活用できればもう少し綺麗なコードをかけるような気がしますが、今回はこれで解決ということにします。
もし、改善が可能という方や、別の方法で同じことができるという方がいましたら是非お願いします。