複数の変数の値を適当なフォーマットでログに出す関数を作成しているとします。
呼び出し側はこんな感じ。
int a;
string b;
ClassA c;
logFunction(a, b, c);
色々な型の引数をもつ関数を定義するなら関数テンプレート。
引数が1つの場合こんな感じになります。
template
void logFunction(T arg) {
DO_SOMETHING(arg);
}
DO_SOMETHINGは、
std::cout << arg;
みたいないろいろな型を受けられるようにになっているものをイメージしてください。
ですが、さらに引数の型も数も不定な場合、どうしようかと。
最近ならC++11以降の可変長引数テンプレート(Variadic template)でできるのでしょうが、
残念ながらコンパイラのバージョンが古くて使えない状況。。
最初はごり押しで書いてみました。
template <typename T0, typename T1>
void logFunction(T0 arg0, T1 arg1) {
DO_SOMETHING(arg0);
DO_SOMETHING(arg1);
}
template <typename T0, typename T1, typename T3>
void logFunction(T0 arg0, T1 arg1, T2 arg2) {
DO_SOMETHING(arg0);
DO_SOMETHING(arg1);
DO_SOMETHING(arg2);
}
.
.
.
しかしいつの間にか引数が数十個まで必要になってしまい冗長にもほどがある状態になってしまいました。
Boost.Preprocessor
ある日、この肥大化した関数たちの処理を修正したいときがきまして、
そろそろなんとかしようかなと適当に検索していたら
Boost.Preprocessorでできそうな気がしてきました。
(繰り返しになりますが最近のコンパイラならVariadic templateでできそう)。
数少ない手に入るサンプルをこねくり回して作成したのが下記のコード。
これで引数25個まで一気に定義できます。
MAX_SIZEを変えればもっと多くてもできそうです。
#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#define MAX_SIZE 25
#define repSomething(z, n, unused) \
DO_SOMETHING(arg##n);\
#define DefLogFunction(z, n, unused) \
template <typename T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, typename T)> \
void logFunction(\
T arg\
BOOST_PP_COMMA_IF(n) \
BOOST_PP_ENUM_BINARY_PARAMS(n,T,arg) \
) {\
DO_SOMETHING(arg);\
BOOST_PP_REPEAT(n, repSomething, ~)\
}\
BOOST_PP_REPEAT(MAX_SIZE, DefLogFunction, ~)
#undef DefLogFunction
#undef repSomething