sample01.cpp
# include <string>
# include <iostream>
std::string get_functype(int) {
return "You have returned an int value.";
}
std::string get_functype(double) {
return "You have returned a double value.";
}
int int_func(void) {
auto ret = 100;
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
double double_func(void) {
auto ret = 3.14;
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
int main(void) {
std::cout << get_functype(int_func()) << std::endl;
std::cout << get_functype(double_func()) << std::endl;
return 0;
}
これは普通にコンパイルして実行できる。
ありふれた関数のオーバーロード。
I will return 100.
You have returned an int value.
I will return 3.14.
You have returned a double value.
ではこれは?
sample02.cpp
# include <string>
# include <iostream>
std::string get_functype(int) {
return "You have returned an int value.";
}
std::string get_functype(double) {
return "You have returned a double value.";
}
std::string get_functype(void) {
return "You have returned nothing.";
}
int int_func(void) {
auto ret = 100;
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
double double_func(void) {
auto ret = 3.14;
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
void void_func(void) {
std::cout << "I will return nothing." << std::endl;
}
int main(void) {
std::cout << get_functype(int_func()) << std::endl;
std::cout << get_functype(double_func()) << std::endl;
std::cout << get_functype(void_func()) << std::endl;
return 0;
}
コンパイルできない。
g++ -std=c++1y sample02.cpp -o sample02
sample02.cpp: 関数 ‘int main()’ 内:
sample02.cpp:35:42: エラー: void 式の無効な使用法です
std::cout << get_functype(void_func()) << std::endl;
^
void
を返す関数は、普通の関数みたいに戻り値の型で推論できない。
でも、これを対等に扱いたい。この(あるいは、これに近いコードを)コンパイルして実行できるようにしたいの。
で、
sample03.cpp
# include <string>
# include <iostream>
std::string get_functype(int) {
return "You have returned an int value.";
}
std::string get_functype(double) {
return "You have returned a double value.";
}
std::string get_functype(void) {
return "You have returned nothing.";
}
int int_func(void) {
auto ret = 100;
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
double double_func(void) {
auto ret = 3.14;
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
void void_func(void) {
std::cout << "I will return nothing." << std::endl;
}
template<typename RET>
inline auto get_functype_switch_helper(RET(*func)(void)) {
return get_functype(func());
}
template<>
inline auto get_functype_switch_helper<void>(void(*func)(void)) {
func();
return get_functype();
}
template<typename FUNC>
inline auto get_functype_switch(FUNC func) {
return get_functype_switch_helper<decltype(func())>(func);
}
int main(void) {
std::cout << get_functype_switch(int_func) << std::endl;
std::cout << get_functype_switch(double_func) << std::endl;
std::cout << get_functype_switch(void_func) << std::endl;
return 0;
}
2つのテンプレート関数を間に挟んで、めでたく解決。
I will return 100.
You have returned an int value.
I will return 3.14.
You have returned a double value.
I will return nothing.
You have returned nothing.
ちょっと醜いけどww
インライン関数にしとけば、おそらく実行時のオーバーヘッドは極小か完全にゼロ。
ついでに、引数も自由に付けられるようにしたいよね。
sample04.cpp
# include <string>
# include <iostream>
std::string get_functype(int) {
return "You have returned an int value.";
}
std::string get_functype(double) {
return "You have returned a double value.";
}
std::string get_functype(void) {
return "You have returned nothing.";
}
int int_func(int n1, int n2) {
auto ret = n1 + n2;
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
double double_func(double ret) {
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
void void_func(void) {
std::cout << "I will return nothing." << std::endl;
}
template<typename RET>
struct get_functype_switch_helper{
template<typename... ARGS>
static auto x(RET(*func)(ARGS...), ARGS... args) {
return get_functype(func(args...));
}
};
template<>
struct get_functype_switch_helper<void>{
template<typename... ARGS>
static auto x(void(*func)(ARGS...), ARGS... args) {
func(args...);
return get_functype();
}
};
template<typename FUNC, typename... ARGS>
inline auto get_functype_switch(FUNC func, ARGS... args) {
return get_functype_switch_helper<decltype(func(args...))>::template x<ARGS...>(func, args...);
}
int main(void) {
std::cout << get_functype_switch(int_func, 30, 70) << std::endl;
std::cout << get_functype_switch(double_func, 3.14) << std::endl;
std::cout << get_functype_switch(void_func) << std::endl;
return 0;
}
これでOK。
可変長テンプレートと構造体を使った関数テンプレートの部分特殊化ハックで、さらにカオスな感じになってきたw
でも、こういうケースが出現するたびに、いちいちこんなの書くの面倒だし、、
こんなときは、
伝統の、
マクロww
void_switch.h
# ifndef INCLUDED_VOID_SWITCH_H
# define INCLUDED_VOID_SWITCH_H
# define DECLARE_VOID_SWITCH(func1) \
template<typename RET> \
struct func1##_switch_helper{ \
template<typename... ARGS> \
static auto x(RET(*func2)(ARGS...), ARGS... args) { \
return func1(func2(args...)); \
} \
}; \
\
template<> \
struct func1##_switch_helper<void>{ \
template<typename... ARGS> \
static auto x(void(*func2)(ARGS...), ARGS... args) { \
func2(args...); \
return func1(); \
} \
}; \
\
template<typename FUNC, typename... ARGS> \
inline auto func1##_switch(FUNC func2, ARGS... args) { \
return func1##_switch_helper<decltype(func2(args...))> \
::template x<ARGS...>(func2, args...); \
} \
# endif
sample05.cpp
# include <string>
# include <iostream>
# include "void_switch.h"
std::string get_functype(int) {
return "You have returned an int value.";
}
std::string get_functype(double) {
return "You have returned a double value.";
}
std::string get_functype(void) {
return "You have returned nothing.";
}
int int_func(int n1, int n2) {
auto ret = n1 + n2;
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
double double_func(double ret) {
std::cout << "I will return " << ret << "." << std::endl;
return ret;
}
void void_func(void) {
std::cout << "I will return nothing." << std::endl;
}
DECLARE_VOID_SWITCH(get_functype)
int main(void) {
std::cout << get_functype_switch(int_func, 30, 70) << std::endl;
std::cout << get_functype_switch(double_func, 3.14) << std::endl;
std::cout << get_functype_switch(void_func) << std::endl;
return 0;
}
醜いマクロ宣言はヘッダファイルに追い出して、コードもスッキリ。再利用もできる。
めでたしめでたし。
※途中のテンプレート関数呼び出しで、テンプレートパラメータを引き継ぐように若干修正。