[C++]関数が存在しないときに他の関数を実行させる方法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
いろは
記事: 11
登録日時: 2年前

[C++]関数が存在しないときに他の関数を実行させる方法

#1

投稿記事 by いろは » 4ヶ月前

こんばんは。
C++で関数が存在するしているときはその関数を実行し、存在しないときは他の関数を実行できるようにするという処理を考えています。現在は以下のようなコードを用いて処理を分岐させています。

コード:

#include <iostream>
#include <type_traits>

//テスト関数
//void function1() { std::cout << "check function1." << std::endl; }
void function2() { std::cout << "check function2." << std::endl; }

//Function1が存在するならばそれを実行し、存在しないならばFunction2を実行
#define call_definded_function(Name, Function1, Function2)\
struct call_definded_function_##Name {\
	template <class T>\
	static auto tester(T) -> decltype(static_cast<decltype(Function1)*>(&Function1), std::true_type()) {}\
	static std::false_type tester(...) {}\
	template <bool flag = decltype(tester(nullptr))::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)...);\
	}\
}
call_definded_function(test, function1, function2);

int main() {
	//function2が実行される
	call_definded_function_test::call_function();
	return 0;
}
実際はこれでもいいのですが、これは逐一グローバル領域でcall_definded_functionを書く必要が出てきてしまします。
ライブラリ開発の都合上、なるべく似たような名前のクラスの量産は避けたいものです。なので、目標としてはこのマクロの多用による似たような名前のクラスの量産の対策、もしくは上記コードの修正案をだしていただけるとありがたいです。

開発環境はwindows8.1でvisual studio 2015を使っています。

いろは
記事: 11
登録日時: 2年前

Re: [C++]関数が存在しないときに他の関数を実行させる方法

#2

投稿記事 by いろは » 4ヶ月前

一応いい感じに動かすことができたので解決にします。
以下は実装とサンプルコードです。

コード:

#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をうまく活用できればもう少し綺麗なコードをかけるような気がしますが、今回はこれで解決ということにします。

もし、改善が可能という方や、別の方法で同じことができるという方がいましたら是非お願いします。

返信

“C言語何でも質問掲示板” へ戻る