#include <stdexcept> #include <utility> namespace proto_etude // prototype etude { // あらゆる型として評価できるが,実際に評価されると例外を投げる型 struct undefined_t { // 任意の型への変換 template< class T > operator T&&() const { throw std::logic_error( "etude::undefined must not be used!" ); } }; undefined_t const undefined = {}; // 例外を投げる関数. // throw との違いは,戻り値をあらゆる型として評価できる点 // 例外オブジェクトを指定して例外を投げる // error<std::runtime_error>( "はわわ ><" ); のように使う template< class Exception, class... Args > inline undefined_t error( Args&&... args ) { throw Exception( std::forward<Args>(args)... ); } // std::logic_error を投げる template< class What > inline undefined_t error( What && what ) { throw std::logic_error( std::forward<What>( what ) ); } // デフォルトのエラーメッセージでエラーを投げる inline undefined_t error() { throw std::logic_error( "etude::error() must not be used!" ); } // 本当は Haskell の error に相当する「 lazy なメッセージ指定つきエラー関数」を作りたかったが, // 面倒だったのでやめた. undefined を使えばいいし. } // namespace proto_etude // 使い道 // error の例として,単項 * 演算子をラップしてみる template< class T > inline T& dereference( T* p ) { return p ? *p : proto_etude::error( "ぬるぽ" ); // throw だとこうは書けない } #include <iostream> int main() { // undefined の一例 constexpr int i = true ? 0 : proto_etude::undefined; // 評価されなければエラーにならない std::cout << dereference(&i) << std::endl; // 0 try { std::cout << dereference( (int*)0 ) << std::endl; } catch( std::logic_error& ) { std::cout << "OK.\n"; } }
注意事項としては void
へは変換できない点ですが,その場合は
void f() { return (void)proto_etude::error(); }
ないし
void f() { return throw std::logic_error("にゃー><"); }
とでも書けばいいでしょう.
戻り値がテンプレートの場合には,
template< class R > R f() { return static_cast<R>( proto_etude::error() ); }
で問題なし.
何にせよ, template
万歳.
これを使えば,大抵の lazy 戦略は書けちゃうんじゃなイカ?