static_assert を遅延処理(ボツネタ)
件の仕様に気づく前に書いていたネタ。
static_assert を遅延処理したりします。
[ソース]
#include <type_traits> #include <utility> #include <tuple> template<typename T> using decay = typename std::decay<T>::type; template<typename T> constexpr typename std::add_rvalue_reference<T>::type forward(typename std::remove_reference<T>::type& x){ return static_cast<T&&>(x); } template<typename T> void forward(typename std::remove_reference<T>::type&& x) =delete; #define constexpr_(expr) \ []{ \ struct X{ \ typedef eval_tag_type eval_tag; \ constexpr decltype(expr) operator()() const{ \ return expr; \ } \ }; \ return X{}; \ }() struct eval_tag_type{}; template<typename T> struct val_impl{ typedef eval_tag_type eval_tag; constexpr T operator ()(...) const{ return value; } T value; }; template<typename T> constexpr val_impl<decay<T>> val(T&& t){ return { t }; } template<std::size_t Index> struct arg_impl{ typedef eval_tag_type eval_tag; template<typename ...Args> constexpr typename std::tuple_element<Index, std::tuple<Args...>>::type operator ()(Args&&... args) const{ return std::get<Index>(std::make_tuple(::forward<Args>(args)...)); } }; template<> struct arg_impl<0>{ typedef eval_tag_type eval_tag; template<typename T, typename ...Args> constexpr T operator ()(T&& t, Args&&... args) const{ return t; } }; template<> struct arg_impl<1>{ typedef eval_tag_type eval_tag; template<typename T, typename T2, typename ...Args> constexpr T operator ()(T, T2&& t2, Args...) const{ return t2; } }; constexpr arg_impl<0> arg1 = {}; constexpr arg_impl<1> arg2 = {}; template<typename Expr> struct not_impl{ template<typename ...Args> constexpr bool operator ()(Args&&... args) const{ return !expr(::forward<Args>(args)...); } Expr expr; }; template<typename Left, typename Right> struct equal_impl{ template<typename ...Args> constexpr bool operator ()(Args&&... args) const{ return left (::forward<Args>(args)...) == right(::forward<Args>(args)...); } Left left; Right right; }; template<typename Left, typename Right> constexpr equal_impl<decay<Left>, decay<Right>> equal(Left&& left, Right&& right){ return { left, right }; } template< typename Left, typename Right, typename LeftTag = typename decay<Left>::eval_tag, typename RightTag = typename decay<Left>::eval_tag > constexpr equal_impl<decay<Left>, decay<Right>> operator ==(Left&& left, Right&& right){ return { left, right }; } template<typename Left, typename Right> constexpr not_impl<equal_impl<decay<Left>, decay<Right>>> not_equal(Left&& left, Right&& right){ return { { left, right } }; } template< typename Left, typename Right, typename LeftTag = typename decay<Left>::eval_tag, typename RightTag = typename decay<Left>::eval_tag > constexpr not_impl<equal_impl<decay<Left>, decay<Right>>> operator !=(Left&& left, Right&& right){ return { { left, right } }; } template<typename Expr> struct static_assert_impl{ template<typename ...Args> void operator ()(Args...) const{ static_assert(Expr()()(Args()()...), ""); } }; template<typename Expr> constexpr static_assert_impl<decay<Expr>> static_assert_(Expr){ return {}; } template<typename Expr, typename Arg> void eval(Expr, Arg){ Expr()(Arg()); } #include <sprout/string.hpp> int main(){ // static_assert_ に渡す式は constexpr_ でラップする必要がある static_assert_(constexpr_( val(1) == val(1) ))(); static_assert_(constexpr_( val(1) != val(2) ))(); // static_assert_ を評価する場合に渡す引数は constexpr_ でラップする必要がある static_assert_(constexpr_( val(1) == arg1 ))( constexpr_(1) ); static_assert_(constexpr_( val(1) != arg1 ))( constexpr_(2) ); // 遅延評価 constexpr auto value = val(0); // 実際に必要なのは expr の型)ので constexpr では受け取らない auto expr = static_assert_( constexpr_( arg1 == value ) ); // eval 関数を使用して評価 eval(expr, constexpr_(0)); // lambda 内のクラスを使用しない場合 using is_homu = struct{ typedef eval_tag_type eval_tag; constexpr auto operator()() ->decltype(arg1 == val(sprout::to_string("homu"))) const{ return arg1 == val(sprout::to_string("homu")); } }; using homu = struct{ typedef eval_tag_type eval_tag; constexpr auto operator()() ->decltype(sprout::to_string("homu")) const{ return sprout::to_string("homu"); } }; using mado = struct{ typedef eval_tag_type eval_tag; constexpr auto operator()() ->decltype(sprout::to_string("mado")) const{ return sprout::to_string("mado"); } }; auto is_homu_expr = static_assert_(is_homu{}); eval(is_homu_expr, homu{}); // eval(is_homu_expr, mado{}); // static error return 0; }
とりあえず、ヒャッハーと叫びながらガシガシ書いただけなので、使い道があるのかは知らない。
lambda expressions ではなく、ローカルにそのままクラスを記述すれば問題はないけど、引数に直接記述出来ないので微妙だにゃー。