新しいswitch statementのご案内

前に http://ideone.com/7NnRj というのを書いて、その後パターンマッチとかできればうれしいなーと思ったので、そんなものを書いた。書いてるうちにぼくのかんがえたさいきょうのぱたーんまっち。という記事が先に上がってたのでこれお蔵入りかなーとか思ったけど、せっかくtraitsで拡張できるようにしたんだし公開しよう、そうしよう、というわけで公開します。ほげー
実際のところはパターンマッチというか、boost::variantっぽいものから中に入っているオブジェクトを取り出そうと試みて、取り出せなかったら次の節を試して、取り出せたらそのオブジェクトをtieで初期化させろみたいにバラす、というのを同時にやるものです。query, unwrap, unpackというtraitsがあって、説明は省略(うまくできない)。まぁcpp -E -Pでもしたあと適当に改行とか入れて読んでください。ちなみに今回はrvalue referenceに対する取り組みはやっていません。考えるのめんどくさくなったので。あとネストした構造に対しては何も考えてません。

namespace arbital { namespace match {

template<typename Tag, typename T, typename = void>
struct query {
template<typename U>
static U & apply(U & v) {
return v;
}
};

template<typename T, typename = void>
struct unwrap {
template<typename U>
static U & apply(U & v) {
return v;
}
};

template<typename Tag, typename T, typename = void>
struct unpack {
template<typename U>
static U & apply(U & v) {
return v;
}
};

}}


#include <boost/variant/variant_fwd.hpp>
#include <boost/variant/get.hpp>
#include <boost/optional/optional.hpp>
#include <boost/none.hpp>

namespace arbital { namespace match {

template<typename Tag, BOOST_VARIANT_ENUM_PARAMS(typename T)>
struct query<Tag, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> > {
template<typename U>
static auto apply(U & v)->
boost::optional<decltype(*boost::get<Tag>(&v)) &>
{
if (auto chosen = boost::get<Tag>(&v)) {
return boost::optional<decltype(*chosen) &>(*chosen);
} else {
return boost::none;
}
}
};

}}


#include <boost/optional/optional.hpp>

namespace arbital { namespace match {

template<typename T>
struct unwrap<boost::optional<T> > {
template<typename U>
static auto apply(U & v)->decltype(boost::get(v)) {
return boost::get(v);
}
};

}}


#include <boost/fusion/support/category_of.hpp> // is_random_access
#include <boost/fusion/support/is_sequence.hpp>
#include <boost/fusion/container/vector/vector_fwd.hpp>
#include <boost/fusion/sequence/intrinsic/at.hpp>

#include <boost/utility/enable_if.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/or.hpp>

namespace arbital { namespace match {

template<typename N, typename T>
struct unpack<N, T,
typename boost::enable_if<
boost::mpl::and_<boost::fusion::traits::is_sequence<T>,
boost::fusion::traits::is_random_access<T> > >::type>
{
template<typename U>
static auto apply(U & v)->
decltype(boost::fusion::at<N>(v))
{
return boost::fusion::at<N>(v);
}
};

}}


#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/logical/not.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/facilities/intercept.hpp>
#include <boost/preprocessor/punctuation/paren.hpp>
#include <boost/preprocessor/punctuation/comma.hpp>

#define ARBITAL_PP_TUPLE_SIZE(tuple) \
ARBITAL_PP_TUPLE_SIZE_I(ARBITAL_PP_TUPLE_REM tuple ARBITAL_PP_TUPLE_SIZE_TABLE)
#define ARBITAL_PP_TUPLE_SIZE_I(x) ARBITAL_PP_TUPLE_HEAD((ARBITAL_PP_TUPLE_DROP_255(x)))

#define ARBITAL_PP_TUPLE_ELEM(n, tuple) \
ARBITAL_PP_TUPLE_ELEM_I( \
BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PP_SUB(255, n), \
BOOST_PP_INTERCEPT) \
ARBITAL_PP_TUPLE_REM tuple)
#define ARBITAL_PP_TUPLE_ELEM_I(x) ARBITAL_PP_TUPLE_HEAD((ARBITAL_PP_TUPLE_DROP_255(x)))

#define ARBITAL_PP_TUPLE_REM(...) __VA_ARGS__

#define ARBITAL_PP_TUPLE_EAT(...)

#define ARBITAL_PP_TUPLE_HEAD(tup) \
ARBITAL_PP_TUPLE_HEAD_I tup
#define ARBITAL_PP_TUPLE_HEAD_I(...) \
ARBITAL_PP_TUPLE_HEAD_II(__VA_ARGS__,)
#define ARBITAL_PP_TUPLE_HEAD_II(_1,...) _1

#define ARBITAL_PP_TUPLE_TAIL(tup) \
ARBITAL_PP_TUPLE_TAIL_I tup
#define ARBITAL_PP_TUPLE_TAIL_I(...) \
ARBITAL_PP_TUPLE_TAIL_II(__VA_ARGS__ ARBITAL_PP_TUPLE_EAT BOOST_PP_LPAREN() , BOOST_PP_RPAREN())
#define ARBITAL_PP_TUPLE_TAIL_II(_1,...) (__VA_ARGS__)

#define ARBITAL_PP_TUPLE_TAKE(n, tuple) \
ARBITAL_PP_TUPLE_TAKE_III( \
BOOST_PP_IIF(BOOST_PP_NOT(n), \
() ARBITAL_PP_TUPLE_EAT, \
ARBITAL_PP_TUPLE_TAKE_I)(n, tuple))
#define ARBITAL_PP_TUPLE_TAKE_I(n, tuple) \
ARBITAL_PP_TUPLE_TAKE_II( \
BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PP_SUB(255, n), \
BOOST_PP_INTERCEPT), \
ARBITAL_PP_TUPLE_REM tuple)
#define ARBITAL_PP_TUPLE_TAKE_II(commas, tuple) \
(ARBITAL_PP_TUPLE_TAKE_255(ARBITAL_PP_TUPLE_EAT BOOST_PP_LPAREN() commas BOOST_PP_RPAREN() tuple))
#define ARBITAL_PP_TUPLE_TAKE_III(res) ARBITAL_PP_TUPLE_TAKE_IV(res)
#define ARBITAL_PP_TUPLE_TAKE_IV(res) res

#define ARBITAL_PP_TUPLE_DROP_255( \
_1,_2,_3,_4,_5,_6,_7,_8,_9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
_71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
_81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
_91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
_101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
_111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
_121,_122,_123,_124,_125,_126,_127,_128,_129,_130, \
_131,_132,_133,_134,_135,_136,_137,_138,_139,_140, \
_141,_142,_143,_144,_145,_146,_147,_148,_149,_150, \
_151,_152,_153,_154,_155,_156,_157,_158,_159,_160, \
_161,_162,_163,_164,_165,_166,_167,_168,_169,_170, \
_171,_172,_173,_174,_175,_176,_177,_178,_179,_180, \
_181,_182,_183,_184,_185,_186,_187,_188,_189,_190, \
_191,_192,_193,_194,_195,_196,_197,_198,_199,_200, \
_201,_202,_203,_204,_205,_206,_207,_208,_209,_210, \
_211,_212,_213,_214,_215,_216,_217,_218,_219,_220, \
_221,_222,_223,_224,_225,_226,_227,_228,_229,_230, \
_231,_232,_233,_234,_235,_236,_237,_238,_239,_240, \
_241,_242,_243,_244,_245,_246,_247,_248,_249,_250, \
_251,_252,_253,_254,_255,...) \
__VA_ARGS__

#define ARBITAL_PP_TUPLE_TAKE_255( \
_1,_2,_3,_4,_5,_6,_7,_8,_9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
_71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
_81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
_91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
_101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
_111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
_121,_122,_123,_124,_125,_126,_127,_128,_129,_130, \
_131,_132,_133,_134,_135,_136,_137,_138,_139,_140, \
_141,_142,_143,_144,_145,_146,_147,_148,_149,_150, \
_151,_152,_153,_154,_155,_156,_157,_158,_159,_160, \
_161,_162,_163,_164,_165,_166,_167,_168,_169,_170, \
_171,_172,_173,_174,_175,_176,_177,_178,_179,_180, \
_181,_182,_183,_184,_185,_186,_187,_188,_189,_190, \
_191,_192,_193,_194,_195,_196,_197,_198,_199,_200, \
_201,_202,_203,_204,_205,_206,_207,_208,_209,_210, \
_211,_212,_213,_214,_215,_216,_217,_218,_219,_220, \
_221,_222,_223,_224,_225,_226,_227,_228,_229,_230, \
_231,_232,_233,_234,_235,_236,_237,_238,_239,_240, \
_241,_242,_243,_244,_245,_246,_247,_248,_249,_250, \
_251,_252,_253,_254,_255,...) \
_1,_2,_3,_4,_5,_6,_7,_8,_9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
_71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
_81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
_91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
_101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
_111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
_121,_122,_123,_124,_125,_126,_127,_128,_129,_130, \
_131,_132,_133,_134,_135,_136,_137,_138,_139,_140, \
_141,_142,_143,_144,_145,_146,_147,_148,_149,_150, \
_151,_152,_153,_154,_155,_156,_157,_158,_159,_160, \
_161,_162,_163,_164,_165,_166,_167,_168,_169,_170, \
_171,_172,_173,_174,_175,_176,_177,_178,_179,_180, \
_181,_182,_183,_184,_185,_186,_187,_188,_189,_190, \
_191,_192,_193,_194,_195,_196,_197,_198,_199,_200, \
_201,_202,_203,_204,_205,_206,_207,_208,_209,_210, \
_211,_212,_213,_214,_215,_216,_217,_218,_219,_220, \
_221,_222,_223,_224,_225,_226,_227,_228,_229,_230, \
_231,_232,_233,_234,_235,_236,_237,_238,_239,_240, \
_241,_242,_243,_244,_245,_246,_247,_248,_249,_250, \
_251,_252,_253,_254,_255

#define ARBITAL_PP_TUPLE_SIZE_TABLE 256,255,254,253,252,251, \
250,249,248,247,246,245,244,243,242,241, \
240,239,238,237,236,235,234,233,232,231, \
230,229,228,227,226,225,224,223,222,221, \
220,219,218,217,216,215,214,213,212,211, \
210,209,208,207,206,205,204,203,202,201, \
200,199,198,197,196,195,194,193,192,191, \
190,189,188,187,186,185,184,183,182,181, \
180,179,178,177,176,175,174,173,172,171, \
170,169,168,167,166,165,164,163,162,161, \
160,159,158,157,156,155,154,153,152,151, \
150,149,148,147,146,145,144,143,142,141, \
140,139,138,137,136,135,134,133,132,131, \
130,129,128,127,126,125,124,123,122,121, \
120,119,118,117,116,115,114,113,112,111, \
110,109,108,107,106,105,104,103,102,101, \
100,99,98,97,96,95,94,93,92,91, \
90,89,88,87,86,85,84,83,82,81, \
80,79,78,77,76,75,74,73,72,71, \
70,69,68,67,66,65,64,63,62,61, \
60,59,58,57,56,55,54,53,52,51, \
50,49,48,47,46,45,44,43,42,41, \
40,39,38,37,36,35,34,33,32,31, \
30,29,28,27,26,25,24,23,22,21, \
20,19,18,17,16,15,14,13,12,11, \
10,9,8,7,6,5,4,3,2,1,



#include <type_traits>

#include <boost/mpl/size_t.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/facilities/is_empty.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/arithmetic/dec.hpp>

#define ARBITAL_SWITCH_DEFINE_EXPR_FUN(name_sig, body) \
auto name_sig->decltype body \
{ return body; }

namespace arbital { namespace match {

namespace _call {

template<typename Tag, typename T>
ARBITAL_SWITCH_DEFINE_EXPR_FUN(
query(T & v),
(match::query<Tag, typename std::remove_cv<T>::type>::apply(v)))

template<typename T>
ARBITAL_SWITCH_DEFINE_EXPR_FUN(
unwrap(T & v),
(match::unwrap<typename std::remove_cv<T>::type>::apply(v)))

template<typename Tag, typename T>
ARBITAL_SWITCH_DEFINE_EXPR_FUN(
unpack(T & v),
(match::unpack<Tag, typename std::remove_cv<T>::type>::apply(v)))

template<size_t N, typename T>
ARBITAL_SWITCH_DEFINE_EXPR_FUN(
unpack(T & v),
(_call::unpack<boost::mpl::size_t<N> >(v)))

}

}}

#undef ARBITAL_SWITCH_DEFINE_EXPR_FUN

#define ARBITAL_SWITCH(...) \
if (bool _switch_cond_ = true) \
for (auto && _switch_var_ = (__VA_ARGS__); \
_switch_cond_; \
_switch_cond_ = false) \
if (0) {}

#define ARBITAL_CASE(...) \
else if (_switch_var_ == (__VA_ARGS__))

#define ARBITAL_DEFAULT else

#define ARBITAL_MATCH(...) \
ARBITAL_MATCH_I)((__VA_ARGS__))(
#define ARBITAL_MATCH_I(tup) \
ARBITAL_MATCH_II(BOOST_PP_DEC(ARBITAL_PP_TUPLE_SIZE(tup)), tup)
#define ARBITAL_MATCH_II(n, tup) \
ARBITAL_MATCH_III(ARBITAL_PP_TUPLE_TAKE(n, tup), ARBITAL_PP_TUPLE_ELEM(n, tup))
#define ARBITAL_MATCH_III(tag, vars) \
else if (auto && _query_var_ = ::arbital::match::_call::query<ARBITAL_PP_TUPLE_REM tag>(_switch_var_)) \
for (auto && _match_var_ = ::arbital::match::_call::unwrap(_query_var_); \
_switch_cond_; \
_switch_cond_ = false) \
ARBITAL_SWITCH_UNPACK_TUPLE(_match_var_, vars)

#define ARBITAL_SWITCH_UNPACK_TUPLE(packed, vars) \
BOOST_PP_REPEAT(ARBITAL_PP_TUPLE_SIZE(vars), ARBITAL_SWITCH_UNPACK_TUPLE_M, (packed, vars))

#define ARBITAL_SWITCH_UNPACK_TUPLE_M(z, n, data) \
ARBITAL_SWITCH_UNPACK_TUPLE_M_I(n, ARBITAL_PP_TUPLE_REM data)
#define ARBITAL_SWITCH_UNPACK_TUPLE_M_I(n, x) \
ARBITAL_SWITCH_UNPACK_TUPLE_M_II(n, x)
#define ARBITAL_SWITCH_UNPACK_TUPLE_M_II(n, packed, vars) \
BOOST_PP_IIF(BOOST_PP_IS_EMPTY(ARBITAL_PP_TUPLE_ELEM(n, vars)), \
ARBITAL_PP_TUPLE_EAT, \
ARBITAL_SWITCH_UNPACK_TUPLE_M_III) \
(n, packed, ARBITAL_PP_TUPLE_ELEM(n, vars))
#define ARBITAL_SWITCH_UNPACK_TUPLE_M_III(n, packed, var) \
for (auto & var = ::arbital::match::_call::unpack<n>(packed); _switch_cond_; _switch_cond_ = false)



#include <iostream>
#include <string>

#include <boost/variant/variant.hpp>
#include <boost/fusion/container/vector/vector.hpp>
#include <boost/mpl/print.hpp>

void print() { std::cout << '\n'; }

template<typename T, typename ...U>
void print(T && x, U && ...xs) {
std::cout << std::forward<T>(x);
::print(std::forward<U>(xs)...);
}

int main() {
char x = 'x';
ARBITAL_SWITCH (x)
ARBITAL_CASE ('a') {
print("'a'");
} ARBITAL_CASE ('b') print("'b'");
ARBITAL_CASE (x) {
print("x");
} ARBITAL_DEFAULT {
print("other");
}

namespace bst = ::boost;
namespace fu = bst::fusion;

boost::variant<
int,
fu::vector<int, std::string> >
v = fu::vector<int, std::string>(1, "hogehoge");

ARBITAL_SWITCH (v)
ARBITAL_MATCH (int, (n)) {
print("int: n = ", n);
}
ARBITAL_MATCH (fu::vector<int, std::string>, (n, str)) {
print("(int, string) : n = ", n, ", str = ", str);
}
}

したいかどうかは別として、caseとmatchは混ぜて使うことができます。