Skip to content

Latest commit

 

History

History
441 lines (368 loc) · 10.5 KB

199.md

File metadata and controls

441 lines (368 loc) · 10.5 KB
Info

Example

struct foo { int value{42}; };
struct bar { };

constexpr auto value = [](auto t) {
  if constexpr ( requires { t.value; } ) {
    return t.value;
  } else {
    return 0;
  }
};

static_assert(42 == value(foo{}));
static_assert(0  == value(bar{}));

https://godbolt.org/z/v54aba

Puzzle

  • Can you implement a bash style constexpr ternary operator powered by C++20 concepts?
/* TODO
 * Bash style constexpr ternary operator powered by C++20 concepts
 * [[ $b = 5 ]] && a="$c" || a="$d"
*/
int main() {
  struct foo { int value{42}; };
  struct bar { };

  {
  constexpr auto value = [](auto t) { return requires { t.value; }; } && [](auto t) { return t.value; } || [](auto t) { return 0; };
  static_assert(0 == value(bar{}));
  static_assert(42 == value(foo{42}));
  }

  {
  constexpr auto value = [](auto t) { return requires { t.value; }; } and [](auto t) { return t.value; } or [](auto t) { return sizeof(t); };
  static_assert(sizeof(bar) == value(bar{}));
  static_assert(42 == value(foo{42}));
  }

  {
  constexpr auto value = [](auto t) { return requires { t.value; }; } and [](auto t) { return t.value; } or 0;
  static_assert(0 == value(bar{}));
  static_assert(42 == value(foo{42}));
  }

  {
  constexpr auto value = [](auto t) { return not requires { t.value; }; } and 1 or [](auto t) { return t.value; };
  static_assert(1 == value(bar{}));
  static_assert(42 == value(foo{42}));
  }

  {
  constexpr auto value = [](auto t) { return requires { t.value; }; } and 1 or 2;
  static_assert(2 == value(bar{}));
  static_assert(1 == value(foo{}));
  }

  {
  constexpr auto value = [](auto t) { return requires { t.value; }; } and 1;
  static_assert(0 == value(bar{}));
  static_assert(1 == value(foo{}));
  }

  {
  constexpr auto value = [](auto t) { return requires { t.value; }; } or 1;
  static_assert(1 == value(bar{}));
  static_assert(0 == value(foo{}));
  }

  {
  static_assert(4 == ([](auto t) { return requires { t.value; }; } and 4 or 2)(foo{}));
  static_assert(2 == ([](auto t) { return requires { t.value; }; } and 4 or 2)(bar{}));
  }
}

https://godbolt.org/z/W5Pc3d

Solutions

namespace detail {
constexpr auto call_or_value = [] (auto f, [[maybe_unused]] auto t) {
  if constexpr (requires { f(t); }) {
    return f(t);
  } else {
    return f;
  }
};

template <class Cond, class Cons, class Alt>
struct alt {
  template <class T>
  constexpr auto operator()(T t) const {
    if constexpr (Cond{}(T{})) {
      return call_or_value(c, t);
    } else {
      return call_or_value(a, t);
    }
  }

  Cons c;
  Alt a;
};

template <class Cond, class Cons>
struct cond {
  template <class T>
  constexpr auto operator()(T t) const {
    if constexpr (Cond{}(T{})) {
      return call_or_value(c, t);
    } else {
      return decltype(call_or_value(c, t)){};
    }
  }

  Cons c{};
};
}

template <class Cond, class Cons>
constexpr auto operator and(Cond, Cons c) {
  return detail::cond<Cond, Cons>{c};
}

template <class Cond, class Cons, class Alt>
constexpr auto operator or(detail::cond<Cond, Cons> cond, Alt a) {
  return detail::alt<Cond, Cons, Alt>{cond.c, a};
}

template <class Cond, class Alt>
constexpr auto operator or(Cond, Alt a) {
  constexpr auto neg = [] <class T> (T t) {
    return not Cond{}(t);
  };
  return detail::cond<decltype(neg), Alt>{a};
}

https://godbolt.org/z/P1v8re

namespace detail {
template <typename T, bool B>
struct test
{
private:
  T value;

public:
  constexpr test() : value{} {}
  constexpr test(T t) : value(t) {}

  template <typename = std::enable_if<not std::same_as<bool, T>>>
  constexpr operator bool() const { return B; }

  constexpr operator T() const { return value; }
};

template <typename>
struct has_value : std::false_type {};

template <typename T, bool B>
struct has_value<test<T, B>> { static constexpr bool value = B; };

template <typename T>
constexpr bool has_value_v = has_value<T>::value;

constexpr auto result(auto expr, auto val)
{
  if constexpr (requires { expr(val); }) return expr(val);
  else return expr;
}
} // namespace detail

template <typename Pred, typename Expr>
constexpr auto operator and(Pred, Expr expr)
{
  return [=]<typename T>(T value)
  {
    if constexpr (Pred{}(T{})) {
      auto result = detail::result(expr, value);
      return detail::test<decltype(result), true>(result);
    }
    else {
      return detail::test<Expr, false>{};
    }
  };
}

template <typename Pred, typename Expr>
constexpr auto operator or(Pred pred, Expr expr)
{
  return [=](auto value)
  {
    auto cond = pred(value);

    if constexpr (std::same_as<decltype(cond), bool>) {
      if (cond) {
        return detail::test<Expr, true>(Expr{});
      }
    }

    if constexpr (not detail::has_value_v<decltype(cond)>) {
      auto result = detail::result(expr, value);
      return detail::test<decltype(result), true>(result);
    }
    else {
      return cond;
    }
  };
}

https://godbolt.org/z/3KP1vn

namespace detail {
[[nodiscard]] constexpr auto call(auto f, auto v) {
  if constexpr (requires { f(v); }) {
    return f(v);
  } else {
    return f;
  }
}

template<class TLhs, class TRhs>
struct if_ {
  constexpr if_(TLhs lhs, TRhs rhs) : lhs{lhs}, rhs{rhs} {}

  [[nodiscard]] constexpr auto operator()(auto x, auto y) const {
    if constexpr (TLhs{}(decltype(x){})) {
      return call(rhs, x);
    } else {
      return call(y, x);
    }
  }

  [[nodiscard]] constexpr auto operator()(auto t) const {
    if constexpr (TLhs{}(decltype(t){})) {
      return call(rhs, t);
    } else {
      return decltype(call(rhs, t)){};
    }
  }

 private:
  TLhs lhs{};
  TRhs rhs{};
};

template<class TLhs, class TRhs>
struct else_ {
  constexpr else_(TLhs lhs, TRhs rhs) : lhs{lhs}, rhs{rhs} {}

  [[nodiscard]] constexpr auto operator()(auto t) const {
    if constexpr (requires { lhs(t, rhs); }) {
      return lhs(t, rhs);
    } else if constexpr (TLhs{}(decltype(t){})) {
      return decltype(call(rhs, lhs)){};
    } else {
      return call(rhs, lhs);
    }
  }

 private:
  TLhs lhs{};
  TRhs rhs{};
};

} // namespace detail

[[nodiscard]] constexpr auto operator and(auto lhs, auto rhs) { return detail::if_{lhs, rhs}; }
[[nodiscard]] constexpr auto operator  or(auto lhs, auto rhs) { return detail::else_{lhs, rhs}; }

https://godbolt.org/z/67oY6M

template <typename FuncOrVal, typename Val>
constexpr auto maybe_call(FuncOrVal funcorval, Val val) {
    if constexpr (requires{funcorval(Val{});})
        return funcorval(val);
    else
        return funcorval;
}

template <typename Condition, typename IfTrue>
struct and_t {
    Condition condition{};
    IfTrue iftrue{};

    template <typename Value>
    constexpr auto operator()(Value value) const {
        if constexpr (Condition{}(Value{})) {
            return maybe_call(iftrue, value);
        } else {
            return decltype(maybe_call(iftrue, value)){};
        }
    }
};

template <typename Condition, typename IfTrue>
constexpr auto operator&&(Condition condition, IfTrue iftrue) {
    return and_t{condition, iftrue};
}

template <typename Condition, typename IfTrue, typename IfFalse>
constexpr auto operator||(and_t<Condition, IfTrue> and_, IfFalse iffalse) {
    return [=]<typename T>(T value) {
        if constexpr (and_.condition(T{})) {
            return maybe_call(and_.iftrue, value);
        } else {
            return maybe_call(iffalse, value);
        }
    };
}

template <typename Condition, typename IfFalse>
constexpr auto operator||(Condition condition, IfFalse iffalse) {
    return [=]<typename T>(T value) {
        if constexpr (not condition(T{})) {
            return maybe_call(iffalse, value);
        } else {
            return decltype(maybe_call(iffalse, value)){};
        }
    };
}

https://godbolt.org/z/4Kz4bY

template<typename Cond, typename TRes, typename FRes = int>
struct Ternary {
    constexpr Ternary(Cond cond, TRes tres, FRes fres = 0) : cond(cond), tres(tres), fres(fres) {}
    template<typename T>
    auto constexpr operator()(const T& t) const {
        TRes tresLocal = tres;
        FRes fresLocal = fres;
        Cond condLocal = cond;
        if constexpr( condLocal(T())) {
            if constexpr( std::is_fundamental_v<TRes>)
                return tresLocal;
            else
                return tresLocal(t);
        } else {
            if constexpr( std::is_fundamental_v<FRes>)
                return fresLocal;
            else
                return fresLocal(t);
        }
    }
    Cond cond;
    TRes tres;
    FRes fres;
};

template<typename Cond, typename TRes, typename FRes>
auto constexpr operator||(Ternary<Cond, TRes> ternary, FRes fres) {
    return Ternary(ternary.cond, ternary.tres, fres);
}

template<typename Cond, typename FRes>
auto constexpr operator||(Cond cond, FRes fres) {
    return Ternary(cond, 0, fres);
}

template<typename Cond, typename TRes>
auto constexpr operator&&(Cond cond, TRes tres) {
    return Ternary(cond, tres);
}

https://godbolt.org/z/evo9Mr

template <typename TCompare, typename TTrue, typename TFalse = int>
struct compare {
    constexpr explicit(true) compare(const TCompare&, const TTrue& true_in) : true_val(true_in), false_val(0) {}
    constexpr explicit(true) compare(const compare<TCompare, TTrue, int>& partial, const TFalse& false_in)
        : true_val(partial.true_val)
        , false_val(false_in)
    {
    }

    template <typename TValue>
    constexpr auto operator()(const TValue& value) const
    {
        if constexpr (TCompare{}(TValue{})) {
            if constexpr (requires(TValue v) { TTrue{}(v); }) {
                return TTrue{}(value);
            } else {
                return true_val;
            }
        } else {
            if constexpr (requires(TValue v) { TFalse{}(v); }) {
                return TFalse{}(value);
            } else {
                return false_val;
            }
        }
    }

    TTrue true_val;
    TFalse false_val;

    using compare_sentenel = void;
};

constexpr auto operator and(const auto& compare_func, const auto& true_in)
{
    return compare{compare_func, true_in};
}

template<typename TCompare>
constexpr auto operator or(const TCompare& lhs, const auto& false_in)
{
    if constexpr (requires{TCompare::compare_sentenel;}) {
        return compare{lhs, false_in};
    } else {
        return compare{compare{lhs, 0}, false_in};
    }
}

https://godbolt.org/z/debW6e