123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- #pragma once
- #include <type_traits>
- #include <optional>
- #include <variant>
- //-----------------------------------------------------------------------------
- // Either
- // 测试发现, 如果 R 是一个对象, 而且整个使用过程中一直没有引用 R,
- // 那么 R 永远不会被构造
- // 见文末的测试
- //
- // 因此, 可以放心地当成 std::expect <T, E> 来使用
- // 因为正常情况下, 是不会有 E 的.
- //
- // std::optional <T> 也是如此, 如果返回 nullopt, 那么 T 不会被构造
- // 为避免 L 与 R 相同时造成编译错误, 用以下语句来禁用一部分成员函数:
- // template <typename Dummy = void, typename = std::enable_if <! std::is_same <TL, TR>::value, Dummy>::type>
- // 或者写成
- // template <typename Dummy = void, typename = std::enable_if_t <! std::is_same <TL, TR>::value>>
- // 这两种语法都是合法的
- //
- //-----------------------------------------------------------------------------
- namespace ECOM::Utility
- {
- template <typename TL>
- struct __tLeft
- {
- TL value;
- };
- template <typename TR>
- struct __tRight
- {
- TR value;
- };
- // 从 value 构造出 Left/Right 类型
- template <typename TL>
- constexpr __tLeft <TL> left (const TL & _value) { return { _value }; }
- template <typename TR>
- constexpr __tRight <TR> right (const TR & _value) { return { _value }; }
- template <typename TL>
- constexpr __tLeft <TL> left (TL && _value) { return { std::move (_value) }; }
- template <typename TR>
- constexpr __tRight <TR> right (TR && _value) { return { std::move (_value) }; }
- template <typename TL, typename TR>
- struct Either
- {
- template <typename> static constexpr bool dependent_false = false;
- public:
- using type_left = typename TL;
- using type_right = typename TR;
- private:
- std::variant < TL, TR > value;
- public:
- constexpr Either () = delete; // ! 不支持默认构造函数, 否则没法知道是左还是右 !
- //< 拷贝构造函数及移动构造函数
- constexpr Either (Either <TL, TR> const & e) : value { e.value } {}
- constexpr Either (Either <TL, TR> && e) : value { std::move (e.value) } {}
- //>
- //< 从 left 或 right 构造
- constexpr Either (__tLeft <TL> const & _left) : value { _left.value } {}
- constexpr Either (__tRight <TR> const & _right) : value { _right.value } {}
- constexpr Either (__tLeft <TL> && _left) : value { std::move (_left.value) } {}
- constexpr Either (__tRight <TR> && _right) : value { std::move (_right.value) } {}
- //>
- //< 直接从 value 构造
- // 但是只构造 left, 不构造 right;
- // 原因是, 调用者很难意识到: 是构造了 left, 还是构造了 right ?
- explicit constexpr Either (TL const & _left) : value { _left} {}
- explicit constexpr Either (TL && _left) : value { std::move (_left) } {}
- template <typename Dummy = void, typename = std::enable_if_t <! std::is_same <TL, TR>::value>>
- constexpr Either (TR const & _right) : value { _right } {}
- template <typename Dummy = void, typename = std::enable_if_t <! std::is_same <TL, TR>::value>>
- constexpr Either (TR && _right) : value { std::move (_right) } {}
- //>
- // 析构函数
- ~Either () = default;
- public: // 赋值. ?? 只赋值一侧, 赋值后, 要清除另一侧吗 ??
- constexpr Either & operator = (Either <TL, TR> const & from)
- {
- value = from.value;
- return (*this);
- }
- constexpr Either & operator = (Either <TL, TR> && from)
- {
- value = std::move (from.value);
- return (*this);
- }
- constexpr Either & operator = (TL const & _left)
- {
- value = _left;
- return (*this);
- }
- constexpr Either & operator = (TL && _left)
- {
- value = std::move (_left);
- return (*this);
- }
- template <typename Dummy = void, typename = std::enable_if_t <! std::is_same <TL, TR>::value>>
- constexpr Either & operator = (TR const & _right)
- {
- value = _right;
- return (*this);
- }
- template <typename Dummy = void, typename = std::enable_if_t <! std::is_same <TL, TR>::value>>
- constexpr Either & operator = (TR && _right)
- {
- value = std::move (_right);
- return (*this);
- }
- constexpr bool operator == (const Either <TL, TR> & _with) const { return value == _with.value; }
- constexpr bool operator != (const Either <TL, TR> & _with) const { return value != _with.value; }
- constexpr bool operator < (const Either <TL, TR> & _with) const { return value < _with.value; }
- constexpr bool operator > (const Either <TL, TR> & _with) const { return value > _with.value; }
- constexpr bool operator <= (const Either <TL, TR> & _with) const { return value <= _with.value; }
- constexpr bool operator >= (const Either <TL, TR> & _with) const { return value >= _with.value; }
- public:
- constexpr bool isLeft () const
- {
- return value.index () == 0;
- }
- constexpr bool isRight () const
- {
- return value.index () == 1;
- }
- constexpr operator bool () const
- {
- return value.index () == 0;
- }
- public:
- //< 禁止自动转换, 免得误转换了都不知道
- constexpr operator TL () const = delete;
- template <typename Dummy = void, typename = std::enable_if_t <! std::is_same <TL, TR>::value>>
- constexpr operator TR () const = delete;
- //>
- public:
- //< 基于类型的判断和转换
- // using dwString = ECOM::Utility::Either <eSTR::DString, eSTR::WString>;
- // auto b1 = dwAck.is <eSTR::DString> ();
- // auto b2 = dwAck.is <eSTR::WString> ();
- // const auto v1 = dwAck.get <eSTR::DString> ();
- // const auto v2 = dwAck.get <eSTR::WString> ();
- template <typename T>
- constexpr bool is () const
- {
- static_assert (dependent_false <T>, "only support <TL> or <TR>");
- }
- template <> constexpr bool is <TL> () const { return value.index () == 0; }
- template <> constexpr bool is <TR> () const { return value.index () == 1; }
- template <typename T> constexpr const T & get () const
- {
- static_assert (dependent_false <T>, "only support <TL> or <TR>");
- }
- template <> constexpr const TL & get <TL> () const { return getLeft (); }
- template <> constexpr const TR & get <TR> () const { return getRight (); }
- template <typename T> constexpr const T * get_if () const
- {
- static_assert (dependent_false <T>, "only support <TL> or <TR>");
- }
- template <> constexpr const TL * get_if <TL> () const { return getIfLeft (); }
- template <> constexpr const TR * get_if <TR> () const { return getIfRight (); }
- //>
- public:
- //< 基于整数的判断和转换. 例子代码如下:
- // using dwString = ECOM::Utility::Either <eSTR::DString, eSTR::WString>;
- // auto b0 = dwAck.is <0> ();
- // auto b1 = dwAck.is <1> ();
- // const auto v0 = dwAck.get <0> ();
- // const auto v1 = dwAck.get <1> ();
- // eSTR::DString s0; eSTR::WString s1;
- // b0 = dwAck.to (s0);
- // b1 = dwAck.to (s1);
- template <int k, typename = std::enable_if_t < k==0 || k == 1 >>
- constexpr bool is () const
- {
- return (value.index () == k);
- }
- template <int k, typename = std::enable_if_t < k == 0>>
- constexpr const TL & get () const { return getLeft (); }
- template <int k, typename = std::enable_if_t < k == 1 >>
- constexpr const TR & get () const { return getRight (); }
- template <int k, typename = std::enable_if_t < k == 0>>
- constexpr TL & get () { return getLeft (); }
- template <int k, typename = std::enable_if_t < k == 1 >>
- constexpr TR & get () { return getRight (); }
- // 如下写法, 语法上没问题, 但是容易导致编译器混淆如下两条语句:
- // ei.get <0> ()
- // ei.get <false> ();
- // 糟糕的是, 这两条语句的语义还是相反的
- // template <bool k, typename = std::enable_if_t < k == true>>
- // constexpr TL & get () { return getLeft (); }
- // template <bool k, typename = std::enable_if_t < k == false >>
- // constexpr TR & get () { return getRight (); }
- template <int k, typename = std::enable_if_t < k == 0>>
- constexpr const TL * get_if () const { return getIfLeft (); }
- template <int k, typename = std::enable_if_t < k == 1 >>
- constexpr const TR * get_if () const { return getIfRight (); }
- template <int k, typename = std::enable_if_t < k == 0>>
- constexpr TL * get_if () { return getIfLeft (); }
- template <int k, typename = std::enable_if_t < k == 1 >>
- constexpr TR * get_if () { return getIfRight (); }
- public:
- //<
- template <typename T>
- constexpr bool onLeftTo (T & _val)
- {
- if (value.index () == 0)
- {
- _val = std::get <TL> (value);
- return true;
- }
- return false;
- }
- template <typename T>
- constexpr bool onRightTo (T & _val)
- {
- if (value.index () == 1)
- {
- _val = std::get <TR> (value);
- return true;
- }
- return false;
- }
- //>
- //< 两个 to 函数, 不需要事先判断. 如果可以, 就赋值给传入的参数. 返回: 是否赋值
- constexpr bool to (TL & _val)
- {
- if (value.index () == 0)
- {
- _val = std::get <TL> (value);
- return true;
- }
- return false;
- }
- constexpr bool to (TR & _val)
- {
- if (value.index () == 1)
- {
- _val = std::get <TR> (value);
- return true;
- }
- return false;
- }
- //>
- public:
- //< 作为左侧或右侧返回其引用. 如果不匹配, std::get <> 扔一个例外 : std::bad_variant_access
- constexpr const TL & getLeft () const { return std::get <TL> (value); }
- constexpr const TR & getRight () const { return std::get <TR> (value); }
- constexpr TL & getLeft () { return std::get <TL> (value); }
- constexpr TR & getRight () { return std::get <TR> (value); }
- //>
- //< 作为左侧或右侧返回其指针. 如果不匹配, 返回 nullptr
- constexpr const TL * getIfLeft () const { return std::get_if <TL> (value); }
- constexpr const TR * getIfRight () const { return std::get_if <TR> (value); }
- constexpr TL * getIfLeft () { return std::get_if <TL> (value); }
- constexpr TR * getIfRight () { return std::get_if <TR> (value); }
- //>
- public:
- // 如果是 Left, 就执行指定的函数, 参数就是 leftValue
- template <typename F, typename ... Args>
- constexpr Either & onLeft (F && f, Args && ... args)
- {
- if (isLeft ())
- f (getLeft (), std::forward <Args> (args)...);
- return (*this);
- }
- // 如果是 right, 就执行指定的函数, 参数就是 rightValue
- template <typename F, typename ... Args>
- constexpr Either & onRight (F && f, Args && ... args)
- {
- if (isRight ())
- f (getRight (), std::forward <Args> (args)...);
- return (*this);
- }
- //>
- //< 映射
- // 把我的 TL 作为参数, 施加于 F 函数, 最终映射成另外一个 either
- // 如果是左侧, 就用左侧传入给定的函数, 返回值也作为左侧
- // 如果是右侧, 直接返回
- // F 的返回值类型是 U, map () 的返回值类型是 Either <U, TR>
- // 某些语言把类似功能定义为 transform 函数
- template <typename F, typename ... Args>
- constexpr auto map (F && f, Args && ... args) const & -> Either <typename std::invoke_result_t <F, TL &, Args && ...>, TR>
- {
- if (isLeft ())
- return { left (f (std::get <TL> (value), std::forward <Args> (args)...)) };
- return { right (std::get <TR> (value)) };
- }
- template <typename F, typename ... Args>
- constexpr auto map (F && f, Args && ... args) && -> Either <typename std::invoke_result_t <F, TL &&, Args && ...>, TR>
- {
- if (isLeft ())
- return { left (f (std::move (std::get <TL> (value)), std::forward <Args> (args)...)) };
- return { right (std::move (std::get <TR> (value))) };
- }
- //>
- //< 带默认值的映射
- //< 把我的 TL 作为参数, 作用到 F 函数上
- // 如果是左侧, 就用左侧传入给定的函数, 然后返回 F 的返回值
- // 如果是右侧, 返回默认值
- // F 的返回值类型是 U, map_or () 的返回值类型也是 U
- template <typename U, typename F, typename ... Args>
- constexpr auto map_or (const U & u, F && f, Args && ... args) const & -> U
- {
- if (isLeft ())
- return f (std::get <TL> (value), std::forward <Args> (args)...);
- return (u);
- }
- template <typename U, typename F, typename ... Args>
- constexpr auto map_or (U && u, F && f, Args && ... args) && -> U
- {
- if (isLeft ())
- return f (std::move (std::get <TL> (value)), std::forward <Args> (args)...);
- return std::move (u);
- }
- //>
- //< 把我的 TL 作为参数, 调用 F 函数, 结果变成另外一个 either
- // 如果是左侧, 就用左侧传入给定的函数, 返回值也作为左侧
- // 如果是右侧, 直接返回
- // F 的返回值类型是 Either <U, TR>, 也就是说, 左值的类型可以变, 但是右值的类型不能变
- // 比如 this 的类型是 <int, string>, F 可以返回类型 <double, string>, 但是不能返回类型 <double, bool>
- // 某些语言把类似功能定义为 flatMap 函数
- template <typename F, typename ... Args>
- constexpr auto and_then (F && f, Args && ... args) const & -> typename std::invoke_result_t <F, TL &, Args && ...>
- {
- if (isLeft ())
- return { f (std::get <TL> (value), std::forward <Args> (args)...) };
- return { right (std::get <TR> (value)) };
- }
- template <typename F, typename ... Args>
- constexpr auto and_then (F && f, Args && ... args) && -> typename std::invoke_result_t <F, TL &, Args && ...>
- {
- if (isLeft ())
- return { f (std::move (std::get <TL> (value)), std::forward <Args> (args)...) };
- return { right (std::move (std::get <TR> (value))) };
- }
- //>
- //< 把我的 TR 作为参数, 调用 F 函数, 结果变成另外一个 either
- // 如果是右侧, 就用左侧传入给定的函数, 返回值也作为左侧
- // 如果是左侧, 直接返回
- // F 的返回值类型是 Either <TL, U>, 也就是说, 左值的类型不能变, 但是右值的类型可以变
- // 比如 this 的类型是 <int, string>, F 可以返回类型 <int, bool>, 但是不能返回类型 <double, bool>
- template <typename F, typename ... Args>
- constexpr auto or_else (F && f, Args && ... args) const & -> typename std::invoke_result_t <F, TR &, Args && ...>
- {
- if (isRight ())
- return { f (std::get <TR> (value), std::forward <Args> (args)...) };
- return { left (std::get <TL> (value)) };
- }
- template <typename F, typename ... Args>
- constexpr auto or_else (F && f, Args && ... args) && -> typename std::invoke_result_t <F, TR &, Args && ...>
- {
- if (isRight ())
- return { f (std::move (std::get <TR> (value)), std::forward <Args> (args)...) };
- return { left (std::move (std::get <TL> (value))) };
- }
- //>
- #if 0 // 编译出错: 函数重定义, 暂时禁用此功能
- //<
- // 把 Either <A, Either <A, B> > 变换成 Either <A, B>
- // 注意 共有类型 A
- // 比如把 Either <int, Either <int, string> > 变换成 Either <int, string>
- // template <typename Either <TL, typename TR::type_right>, typename = std::enable_if_t <std::is_same <TL, TR::type_left>::value>>
- template <typename = std::enable_if_t <std::is_same <TL, TR::type_left>::value>>
- constexpr auto flatten ()
- {
- if (isLeft ())
- return Either <TL, TR::type_right> { getLeft () };
- return getRight ();
- }
- //>
- //<
- // 把 Either < <Either <A, B>, B> > 变换成 Either <A, B>
- // 注意 共有类型 B
- // 比如把 Either < Either <int, string>, string> 变换成 Either <int, string>
- template <typename Dummy = void, typename = std::enable_if_t <std::is_same <TL::type_right, TR>::value>>
- constexpr auto flatten ()
- {
- if (isLeft ())
- return getLeft ();
- return Either <TL::type_left, TR> { getRight () };
- }
- //>
- #endif
- #if 1 // 改成这种方式, 就不会导致编译错误了. 这两个函数的签名似乎不同
- //<
- // 把 Either <A, Either <A, B> > 变换成 Either <A, B>
- // 注意 共有类型 A
- // 比如把 Either <int, Either <int, string> > 变换成 Either <int, string>
- template <typename = std::enable_if_t <std::is_same <TL, TR::type_left>::value>>
- constexpr auto flatten ()
- {
- if (isLeft ())
- return Either <TL, TR::type_right> { getLeft () };
- return getRight ();
- }
- //>
- //<
- // 把 Either < <Either <A, B>, B> > 变换成 Either <A, B>
- // 注意 共有类型 B
- // 比如把 Either < Either <int, string>, string> 变换成 Either <int, string>
- template <typename Dummy = void, typename = std::enable_if_t <std::is_same <TL::type_right, TR>::value>>
- constexpr auto flatten ()
- {
- if (isLeft ())
- return getLeft ();
- return Either <TL::type_left, TR> { getRight () };
- }
- //>
- #endif
- template <typename LL = TL, typename RR = TR>
- constexpr auto join () const -> typename std::common_type <LL, RR>::type
- {
- using toType = typename std::common_type <LL, RR>::type;
- if (isLeft ())
- return static_cast <toType> (std::get <0> (value));
- else
- return static_cast <toType> (std::get <1> (value));
- }
- public:
- constexpr static Either make (TL const & _left) { return Either { _left }; }
- constexpr static Either make (TL && _left) { return Either { std::move (_left) }; }
- template <typename Dummy = void, typename = std::enable_if_t <! std::is_same <TL, TR>::value>>
- constexpr static Either make (TR const & _right)
- {
- return Either { _right };
- }
- template <typename Dummy = void, typename = std::enable_if_t <! std::is_same <TL, TR>::value>>
- constexpr static Either make (TR && _right)
- {
- return Either {std::move (_right) };
- }
- template <typename tAny>
- constexpr static Either from (Either <TL, tAny> && ei)
- {
- assert (ei.isLeft ());
- return Either { std::move (ei.getLeft ()) };
- }
- template <typename tAny>
- constexpr static Either from (Either <tAny, TR> && ei)
- {
- assert (ei.isRight ());
- return Either { std::move (ei.getRight ()) };
- }
- }; // class Either
- template <typename TL, typename tAny>
- constexpr __tLeft <TL> left (Either <TL, tAny> && ei)
- {
- assert (ei.isLeft ());
- return { std::move (ei.getLeft ()) };
- }
- template <typename tAny, typename TR>
- constexpr __tRight <TR> right (Either <tAny, TR> && ei)
- {
- assert (ei.isRight ());
- return { std::move (ei.getRight ()) };
- }
- }
- /*//-----------------------------------------------------------------------------
- // 1. 如下语句, 编译通过且能运行
- class XString
- {
- private:
- XString ();
- };
- void Test ()
- {
- using eiResult = ::Utility::Either <int, XString>;
- eiResult x { ::Utility::left (2) };
- auto y = std::move (x);
- int a = y.asLeft ();
- }
- // 2. 两个 Either 类型, 但是 TL 或 TR 相同, 可以用如下语句来转换
- 但是使用之前必须先判断类型
- return eiResult { std::move (eiBLOBAck.getRight ()) };
- // return eiResult { std::move (eiBLOBAck.get <1> ()) };
- // return std::move (eiBLOBAck.getRight ());
- *///-----------------------------------------------------------------------------
|