結論
できなさそうだ。一応、完全ではないがマクロでやればできる。
(18/08/21 1:48 追記) コメントで、マクロを使わない実装を頂きました!
理由
可変長引数関数をカリー化して一つずつ引数を入れるというアプローチをとった。しかし、カリー化の際に使用するstd::bind
(またはsprout::bind
←可変長プレースホルダーが使えるここに詳細)は、引数を適用するたびに型が変わるようだ。そのため、std::vector
の、実行時にはわからないの要素数用のstd::bind
はコンパイル時に作ることができないだろう。なので、実行時に可変長引数関数をカリー化し、std::vector
の要素一つ一つに引数を適用することはできなかった。(コンパイル時でも、要素数がわからなそう(std::vector::size()
は定数式ではない)なのでできなそうだ)
コード例
# include <iostream>
# include <vector>
# include <utility>
# include <boost/any.hpp>
# include <sprout/functional.hpp>
int main()
{
std::vector<int> v{10, 20, 30}; // 実行時に任意の要素数で構築したとする
using sprout::placeholders::_va; // 可変長引数プレースホルダー
constexpr auto lambda = [](auto&&... args) // 任意の可変長引数関数
{
(std::cout << ... << std::forward<decltype(args)>(args)) << '\n';
};
// forループの際に、型が変化するのでenyに入れた
boost::any fn = sprout::bind(lambda, _va);
for (auto&& it : v)
{
// このfnの型を、コンパイル時には知ることができない
// fn = sprout::bind(boost::any_cast<不明>(fn), it, _va);
}
// boost::any_cast<不明>(fn)();
}
マクロを使った解決策
マクロを使って、可変長引数の引数の数それぞれのパターンを自動生成している。これは、それぞれの引数の数に対して、switch構文のcase一つ一つが対応するようにしている。これは一応動くが、生成されるコードの量は多くなっており、引数の数も制限されている。
# include <iostream>
# include <vector>
# include <utility>
# include <boost/preprocessor/enum.hpp>
# include <boost/preprocessor/repetition/repeat.hpp>
int main()
{
std::vector<int> v{10, 20, 30}; // 実行時に任意の要素数で構築したとする
constexpr auto lambda = [](auto&&... args) // 任意の可変長引数関数
{
(std::cout << ... << std::forward<decltype(args)>(args)) << '\n';
};
// caseの数(0からBOOST_PP_LIMIT_REPEAT(256に展開)までの値)
# define CASE_NUM 15
# define ARG(z, n, unused) v[n]
# define CASE(z, n, unused) \
case n: \
lambda(BOOST_PP_ENUM(n, ARG, unused)); \
break;
switch (v.size())
{
BOOST_PP_REPEAT(CASE_NUM, CASE, unused)
default:
std::cout << "引数が多すぎます。 "
<< CASE_NUM - 1 << "個までの引数にしてください。\n";
break;
}
# undef CASE
# undef ARG
# undef CASE_NUM
}