Last active
August 29, 2015 14:16
-
-
Save ubnt-intrepid/4838a1b0f66780bcbde7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <functional> | |
#include <utility> | |
#include <type_traits> | |
using namespace std; | |
namespace detail { | |
template <typename MaybeCallable, class... Args> | |
struct is_callable | |
{ | |
private: | |
template <class U> | |
static auto check(U*) -> decltype( std::declval<U>()(std::declval<Args>()...), | |
std::true_type() ); | |
template <class> | |
static auto check(...) -> std::false_type; | |
public: | |
using type = decltype( check<MaybeCallable>(nullptr) ); | |
}; | |
} // namespace detail | |
template <typename MaybeCallable, class... Args> | |
using is_callable = typename detail::is_callable<MaybeCallable, Args...>::type; | |
template <class Optional, class Func> | |
void call_value(Optional& opt, Func f, std::true_type) | |
{ | |
f(opt.get()); | |
} | |
template <class Optional, class Func> | |
void call_value(Optional&, Func, std::false_type) {} | |
template <class Func> | |
void call_none(Func f, std::true_type) | |
{ | |
f(); | |
} | |
template <class Func> | |
void call_none(Func, std::false_type) {} | |
template <typename T> | |
class optional | |
{ | |
public: | |
T& get() const { return *((T*)nullptr); } | |
explicit operator bool() const { return false; } | |
template <class Func> | |
optional& match(Func f) | |
{ | |
if (*this) call_value(*this, f, is_callable<Func, T&>()); | |
else call_none(f, is_callable<Func>()); | |
return *this; | |
} | |
template <class Func> | |
optional const& match(Func f) const | |
{ | |
if (*this) call_value(*this, f, is_callable<Func, T const&>()); | |
else call_none(f, is_callable<Func>()); | |
return *this; | |
} | |
template <class Pred> | |
optional& where(Pred pred) | |
{ | |
if (*this && !pred(this->get())) { | |
} | |
return *this; | |
} | |
template <class Func1, class Func2> | |
optional& match(Func1 f1, Func2 f2) | |
{ | |
if (*this) call_value(*this, f1, is_callable<Func1, T&>()); | |
else call_none(f2, is_callable<Func2>()); | |
return *this; | |
} | |
template <class Func1, class Func2> | |
optional const& match(Func1 f1, Func2 f2) const | |
{ | |
if (*this) call_value(*this, f1, is_callable<Func1, T const&>()); | |
else call_none(f2, is_callable<Func2>()); | |
return *this; | |
} | |
}; | |
optional<int> some_func(...) | |
{ | |
return optional<int>(); | |
} | |
int main() | |
{ | |
using std::placeholders::_1; | |
optional<int> a; | |
a.match([](int&){ cout << "1" << endl; }); | |
a.match([]{ cout << "1-1" << endl; }); | |
a.match([](int&){ cout << "2" << endl; }, []{ cout << "3" << endl; }); | |
optional<int> const b; | |
b.match([](int const&){ cout << "4" << endl; }); | |
b.match([]{ cout << "4-1" << endl; }); | |
b.match([](int const&){ cout << "5" << endl; }, []{ cout << "6" << endl; }); | |
// ある関数を実行しその戻り値がoptional値の場合は | |
// 一時変数を介することなくパターンマッチすることができる | |
auto ret = some_func(10, 20, 30) | |
.match([](int const&){ cout << "successed" << endl; }, | |
[] { cout << "failed" << endl; }) | |
.match([] { cout << "failed2" << endl; }) | |
.match([](int const&){ cout << "success2" << endl; }) | |
.match([] { cout << "failed3" << endl; }); | |
// optional<T>::where(pred) | |
// pred :: T -> Bool を用いて内部状態を評価し,falseの場合は自動的にfalseにする | |
// | |
// <example> 追加の範囲条件チェックを実行 | |
some_func(10, 20, 30) | |
.where([](int val){ return 0 <= val && val <= 10; }) | |
.match([]{ cout << "the result of some_func(10,20,30) is out-of-range.\n"; }); | |
sockaddr_in addr; | |
auto deleter = [](SOCKET s){ ::closesocket(s); }; | |
auto listener_socket = | |
// 1. チェックしたい値(この場合はソケットディスクリプタ)を持ち上げて格納する | |
optional<SOCKET>(::socket(AF_INET, SOCK_STREAM, 0)) | |
// 2. 条件: ソケット生成に成功しているか | |
.where([](SOCKET s){ return s != INVALID_SOCKET; }) | |
// 3. 条件: バインドが成功したか | |
.where([&](SOCKET s){ return ::bind(s, &addr, sizeof(addr)) != -1; }, deleter) | |
// 4. 条件: リスンが成功したか | |
.where([](SOCKET s){ return ::listen(s, 5) != -1; }, deleter) | |
// いずれかの処理に失敗したときは optional の中身が none となり,以後の | |
// 処理は実行されない | |
// すべての条件をクリアした場合に限り値が格納される | |
; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
LINQっぽいアクセス
ただしLINQとは異なり呼び出し元の値が書き換えられることに注意