Skip to content

Extension of is() part1: free function predicates, fix implicit cast for build-in and custom is operator, refactoring #1053

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 151 additions & 27 deletions include/cpp2util.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,125 @@ using longdouble = long double;
using _schar = signed char; // normally use i8 instead
using _uchar = unsigned char; // normally use u8 instead

//-----------------------------------------------------------------------
//
// Helper for concepts
//
//-----------------------------------------------------------------------
//

template<typename Ret, typename Arg>
auto argument_of_helper(Ret(*) (Arg)) -> Arg;

template<typename Ret, typename F, typename Arg>
auto argument_of_helper(Ret(F::*) (Arg)) -> Arg;

template<typename Ret, typename F, typename Arg>
auto argument_of_helper(Ret(F::*) (Arg)&) -> Arg;

template<typename Ret, typename F, typename Arg>
auto argument_of_helper(Ret(F::*) (Arg)&&) -> Arg;

template<typename Ret, typename F, typename Arg>
auto argument_of_helper(Ret(F::*) (Arg) const) -> Arg;

template<typename Ret, typename F, typename Arg>
auto argument_of_helper(Ret(F::*) (Arg) const&) -> Arg;

template<typename Ret, typename F, typename Arg>
auto argument_of_helper(Ret(F::*) (Arg) const&&) -> Arg;

template <typename F>
auto argument_of_helper(F const&) -> CPP2_TYPEOF(argument_of_helper(&F::operator()));

template <typename T>
using argument_of_t = CPP2_TYPEOF(argument_of_helper(std::declval<T>()));

template <typename F>
auto argument_of_helper_op_is(F const&) -> CPP2_TYPEOF(argument_of_helper(&F::op_is));

template <typename T>
using argument_of_op_is_t = CPP2_TYPEOF(argument_of_helper_op_is(std::declval<T>()));

template <typename T>
using pointee_t = std::iter_value_t<T>;

template <template <typename...> class C, typename... Ts>
constexpr auto specialization_of_template_helper(C< Ts...> const& ) -> std::true_type {
return {};
}

template <template <typename, auto...> class C, typename T, auto... Ns>
requires (sizeof...(Ns) > 0)
constexpr auto specialization_of_template_helper(C< T, Ns... > const& ) -> std::true_type {
return {};
}

//-----------------------------------------------------------------------
//
// Concepts
//
//-----------------------------------------------------------------------
//

template <typename X, template<typename...> class C>
concept specialization_of_template = requires (X x) {
{ specialization_of_template_helper<C>(std::forward<X>(x)) } -> std::same_as<std::true_type>;
};

template <typename X, template<typename,auto...> class C>
concept specialization_of_template_type_and_nttp = requires (X x) {
{ specialization_of_template_helper<C>(std::forward<X>(x)) } -> std::same_as<std::true_type>;
};

template <typename X>
concept dereferencable = requires (X x) { *x; };

template <typename X>
concept default_constructible = std::is_default_constructible_v<std::remove_cvref_t<X>>;

template <typename X>
concept bounded_array = std::is_bounded_array_v<std::remove_cvref_t<X>>;

template <typename X>
concept pointer_like = dereferencable<X> && default_constructible<X> && std::equality_comparable<X>
&& !bounded_array<X>;

template< typename From, typename To >
concept brace_initializable_to = requires (From x) { To{x}; };

template< typename X, typename C >
concept same_type_as = std::same_as<std::remove_cvref_t<X>, std::remove_cvref_t<C>>;

template <typename X>
concept defined = requires { std::declval<X>(); };

template <typename X>
concept has_defined_argument = requires {
std::declval<argument_of_t<X>>();
};

template <typename X, typename F>
concept covertible_to_argument_of = same_type_as<X,argument_of_t<F>>
|| (pointer_like<argument_of_t<F>> && brace_initializable_to<X, pointee_t<argument_of_t<F>>>)
|| (!pointer_like<argument_of_t<F>> && brace_initializable_to<X, argument_of_t<F>>)
;

template <typename F, typename X>
concept valid_predicate = (std::predicate<F, X> && !has_defined_argument<F>)
|| (std::predicate<F, X> && has_defined_argument<F> && covertible_to_argument_of<X, F>);

template <typename X, typename O, auto mem_fun_ptr>
concept predicate_member_fun = requires (X x, O o) {
{ (o.*mem_fun_ptr)(x) } -> std::convertible_to<bool>;
};

template <typename F, typename X>
concept valid_custom_is_operator = predicate_member_fun<X, F, &F::op_is>
&& (
!defined<argument_of_op_is_t<F>>
|| brace_initializable_to<X, argument_of_op_is_t<F>>
);

//-----------------------------------------------------------------------
//
Expand Down Expand Up @@ -1116,7 +1235,7 @@ inline auto to_string(std::tuple<Ts...> const& t) -> std::string
#if defined(__cpp_lib_format) || (defined(_MSC_VER) && _MSC_VER >= 1929)
inline auto to_string(auto&& value, std::string_view fmt) -> std::string
{
return std::vformat(fmt, std::make_format_args(CPP2_FORWARD(value)));
return std::vformat(fmt, std::make_format_args(value));
}
#else
inline auto to_string(auto&& value, std::string_view) -> std::string
Expand Down Expand Up @@ -1149,31 +1268,24 @@ using empty = void;

// Templates
//
template <template <typename...> class C, typename... Ts>
constexpr auto is(C< Ts...> const& ) -> bool {
return true;
}

#if defined(_MSC_VER)
template <template <typename, typename...> class C, typename T>
constexpr auto is( T const& ) -> bool {
return false;
template <template <typename...> class C, typename X>
constexpr auto is( X&& ) {
if constexpr (specialization_of_template<X, C>) {
return std::true_type{};
}
#else
template <template <typename...> class C, typename T>
constexpr auto is( T const& ) -> bool {
return false;
else {
return std::false_type{};
}
#endif

template <template <typename,auto> class C, typename T, auto V>
constexpr auto is( C<T, V> const& ) -> bool {
return true;
}

template <template <typename,auto> class C, typename T>
constexpr auto is( T const& ) -> bool {
return false;
template <template <typename, auto> class C, typename X>
constexpr auto is( X&& ) {
if constexpr (specialization_of_template_type_and_nttp<X, C>) {
return std::true_type{};
}
else {
return std::false_type{};
}
}

// Types
Expand Down Expand Up @@ -1220,25 +1332,37 @@ auto is( X const& x ) -> bool {
inline constexpr auto is( auto const& x, auto&& value ) -> bool
{
// Value with customized operator_is case
if constexpr (requires{ x.op_is(value); }) {
if constexpr (valid_custom_is_operator<decltype(x), decltype(value)>) {
return x.op_is(value);
}

// Predicate case
else if constexpr (requires{ bool{ value(x) }; }) {
else if constexpr (valid_predicate<decltype(value), decltype(x)>) {
return value(x);
}
else if constexpr (std::is_function_v<decltype(value)> || requires{ &value.operator(); }) {

// Value equality case: C/C++ arrays or individual values
else if constexpr (std::is_array_v<CPP2_TYPEOF(x)> && std::is_array_v<CPP2_TYPEOF(value)>) {
if (std::ssize(x) == std::ssize(value)) {
return std::equal( std::begin(x), std::end(x), std::begin(value));
}
return false;
}

// Value equality case
else if constexpr (requires{ bool{x == value}; }) {
return x == value;
}
return false;
}

//-----------------------------------------------------------------------
//
// and "is predicate" for generic function used as predicate
//

template <typename X>
inline constexpr auto is( X const& x, bool (*value)(X const&) ) -> bool {
return value(x);
}

//-------------------------------------------------------------------------------------------------------------
// Built-in as
Expand Down
31 changes: 31 additions & 0 deletions regression-tests/pure2-is-with-free-functions-predicate.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
fun: (v) = {
if v is (pred_i) {
std::cout << "(v)$ is integer bigger than 3" << std::endl;
}

if v is (pred_d) {
std::cout << "(v)$ is double bigger than 3" << std::endl;
}

if v is (pred_) {
std::cout << "(v)$ is bigger than 3" << std::endl;
}
}

main: () -> int = {
fun(3.14);
fun(42);
fun('a');
}

pred_i: (x : int ) -> bool = {
return x > 3;
}

pred_d: (x : double ) -> bool = {
return x > 3;
}

pred_: (x) -> bool = {
return x > 3;
}
19 changes: 19 additions & 0 deletions regression-tests/pure2-is-with-unnamed-predicates.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
fun: (v) = {
if v is (:(x:int) x>3;) {
std::cout << "(v)$ is integer bigger than 3" << std::endl;
}

if v is (:(x:double) x>3;) {
std::cout << "(v)$ is double bigger than 3" << std::endl;
}

if v is (:(x) x>3;) {
std::cout << "(v)$ is bigger than 3" << std::endl;
}
}

main: () -> int = {
fun(3.14);
fun(42);
fun('a');
}
41 changes: 41 additions & 0 deletions regression-tests/pure2-is-with-variable-and-value.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
fun: (name, v) = {
std::cout << name << ": " <<
inspect v -> std::string {
is (42) = "42";
is (123) = "op_is";
is (-123) = "generic op_is";
is (4321) = "comparable";
is ("text") = "text";
is _ = "unknown";
}
<< std::endl;
}

main: () -> int = {
fun("3.14", 3.14);
fun("42", 42);
fun("WithOp()", WithOp());
fun("WithGenOp()", WithGenOp());
fun("Cmp()", Cmp());
fun("std::string(\"text\")", std::string("text"));
fun("\"text\"", "text");
fun("std::string_view(\"text\")", std::string_view("text"));
fun(":std::vector = ('t','e','x','t','\\0')", :std::vector = ('t','e','x','t','\0'));
}

WithOp : type = {
op_is: (this, x : int) x == 123;
}

WithGenOp : type = {
op_is: (this, x) -> bool = {
if constexpr std::convertible_to<decltype(x), int> {
return x == -123;
}
return false;
}
}

Cmp : type = {
operator==: (this, x : int) -> bool = x == 4321;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ zero
3
integer -42
xyzzy
3
(no match)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
3.140000 is double bigger than 3
3.140000 is bigger than 3
42 is integer bigger than 3
42 is bigger than 3
a is integer bigger than 3
a is bigger than 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
3.140000 is double bigger than 3
3.140000 is bigger than 3
42 is integer bigger than 3
42 is bigger than 3
a is integer bigger than 3
a is bigger than 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
3.14: unknown
42: 42
WithOp(): unknown
WithGenOp(): unknown
Cmp(): comperable
std::string("text"): text
"text": text
std::string_view("text"): text
:std::vector = ('t','e','x','t','\0'): unknown
Loading
Loading