Skip to content

Instantly share code, notes, and snippets.

@ubnt-intrepid
Last active August 29, 2015 14:16
Show Gist options
  • Save ubnt-intrepid/4838a1b0f66780bcbde7 to your computer and use it in GitHub Desktop.
Save ubnt-intrepid/4838a1b0f66780bcbde7 to your computer and use it in GitHub Desktop.
#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 となり,以後の
// 処理は実行されない
// すべての条件をクリアした場合に限り値が格納される
;
}
@ubnt-intrepid
Copy link
Author

LINQっぽいアクセス
ただしLINQとは異なり呼び出し元の値が書き換えられることに注意

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment