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 ではなく、ローカルにそのままクラスを記述すれば問題はないけど、引数に直接記述出来ないので微妙だにゃー。

[コンパイラ]

  • g++ (GCC) 4.7.0 20120218 (experimental)
  • clang++ (LLVM) 3.1 20120226(trunk)