読者です 読者をやめる 読者になる 読者になる

ジェネリックラムダをもっと手軽に書くためのマクロ

ラムダのキャプチャは無くてはならないものだけれど,たいていの場合は [&] とかで済んでしまう.また,引数の型もジェネリックラムダの登場で,だいたい auto で済んでしまうようになったので,こんな感じで書けると良いなぁと思うようになった.

std::vector<int> v = {1,2,3,4,5};
auto product = boost::accumulate(v, 1, lambda(acc, i){ return acc * i; });

つまり,lambda(x, y, ...)[&](auto const& x, auto const& y, ...) に展開されるようなマクロをつくると便利かなーと思った.

なので,こんなマクロあれば良いなーとどことなくそれっぽい方角へつぶやいたところ,

と言われてしまったので泣きながら Boost.PP のドキュメントを読んでいたら @fimbul11 (id:fimbul) さんがささっと実装してくれた.このままツイートが流れてしまうのも勿体ないと思ったのでメモしておく.

#define BOOST_PP_VARIADICS
#include <boost/preprocessor.hpp>

#define lambda_impl(r, data, i, elem) auto const& elem\
  BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(data, i))

#define lambda(...) [&](\
  BOOST_PP_SEQ_FOR_EACH_I(lambda_impl,\
  BOOST_PP_DEC(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)),\
  BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)))

さすがにマクロ名 lambda はやりすぎかもしれないので適切に変更して活用したい.fimbul さんありがとうございました.

追記(1/30)

せっかくなので Boost 無しでつくってみた.引数は 16 まで対応で,上記のコードでは対応できていない,引数が0個の場合にも対応させた.IS_EMPTY() マクロはやり方を思いつかなかったのでネットから拾ってきた.

// IS_EMPTY() from http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
#define HAS_COMMA_I(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, ...) a15
#define HAS_COMMA(...) HAS_COMMA_I(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define TRIGGER_PARENTHESIS(...) ,
 
#define IS_EMPTY(...) \
IS_EMPTY_I(HAS_COMMA(__VA_ARGS__), \
           HAS_COMMA(TRIGGER_PARENTHESIS __VA_ARGS__), \
           HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
           HAS_COMMA(TRIGGER_PARENTHESIS __VA_ARGS__ (/*empty*/)))
 
#define CAT5(a0, a1, a2, a3, a4) a0 ## a1 ## a2 ## a3 ## a4
#define IS_EMPTY_I(a0, a1, a2, a3) HAS_COMMA(CAT5(IS_EMPTY_CASE_, a0, a1, a2, a3))
#define IS_EMPTY_CASE_0001 ,
 
#define VARIADIC_SIZE(...) VARIADIC_SIZE_I(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,)
#define VARIADIC_SIZE_I(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, size, ...) size
 
#define LAMBDA_16(a, ...) auto const& a, LAMBDA_15(__VA_ARGS__)
#define LAMBDA_15(a, ...) auto const& a, LAMBDA_14(__VA_ARGS__)
#define LAMBDA_14(a, ...) auto const& a, LAMBDA_13(__VA_ARGS__)
#define LAMBDA_13(a, ...) auto const& a, LAMBDA_12(__VA_ARGS__)
#define LAMBDA_12(a, ...) auto const& a, LAMBDA_11(__VA_ARGS__)
#define LAMBDA_11(a, ...) auto const& a, LAMBDA_10(__VA_ARGS__)
#define LAMBDA_10(a, ...) auto const& a, LAMBDA_9(__VA_ARGS__)
#define LAMBDA_9(a, ...)  auto const& a, LAMBDA_8(__VA_ARGS__)
#define LAMBDA_8(a, ...)  auto const& a, LAMBDA_7(__VA_ARGS__)
#define LAMBDA_7(a, ...)  auto const& a, LAMBDA_6(__VA_ARGS__)
#define LAMBDA_6(a, ...)  auto const& a, LAMBDA_5(__VA_ARGS__)
#define LAMBDA_5(a, ...)  auto const& a, LAMBDA_4(__VA_ARGS__)
#define LAMBDA_4(a, ...)  auto const& a, LAMBDA_3(__VA_ARGS__)
#define LAMBDA_3(a, ...)  auto const& a, LAMBDA_2(__VA_ARGS__)
#define LAMBDA_2(a, ...)  auto const& a, LAMBDA_1(__VA_ARGS__)
#define LAMBDA_1(a)       auto const& a
 
#define lambda(...) LAMBDA_III(IS_EMPTY(__VA_ARGS__))(__VA_ARGS__)
#define LAMBDA_III(b) LAMBDA_IIII(b)
#define LAMBDA_IIII(b) LAMBDA_ARGS_##b
#define LAMBDA_ARGS_1 LAMBDA_0
#define LAMBDA_ARGS_0(...) LAMBDA_0(LAMBDA_I(VARIADIC_SIZE(__VA_ARGS__))(__VA_ARGS__))
#define LAMBDA_I(n) LAMBDA_II(n)
#define LAMBDA_II(size) LAMBDA_##size
#define LAMBDA_0 [&]