Skip to content

Commit

Permalink
is()/as(): refactor of is() and as() for variant to new design (part …
Browse files Browse the repository at this point in the history
…2 of n) (#1203)

* is(): Add tests for variant

* is(): Add test for is as value with variant

* is(): refactor is() for variant

* as(): refactor of as() for std::variant

No functional changes
The implementation still can handle only 20 types in variant.
It will be changed in future Pull Requests.

* is(): Add tests for variant (other platforms)

* is(): Add test for is as value with variant (other platforms)

* Fixup: remove the limits of number of types

* Fixup: mixed-is-as-variant

* Fixup: tests of mixed-is-as-variant when no limits

* Minor editorial touchup

* Move is() and as() for variant to separate functions

* Fix compiler error - cannot use concept inline with auto

* Fix tests

---------

Co-authored-by: Herb Sutter <[email protected]>
  • Loading branch information
filipsajdak and hsutter authored Aug 28, 2024
1 parent 2e23597 commit 78867f8
Show file tree
Hide file tree
Showing 59 changed files with 2,941 additions and 267 deletions.
338 changes: 141 additions & 197 deletions include/cpp2util.h

Large diffs are not rendered by default.

112 changes: 112 additions & 0 deletions regression-tests/mixed-is-as-value-with-variant.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
auto in(int min, int max) {
return [=](int x){ return min <= x && x <= max; };
}

test: (forward v) = {
if v is (42) {
std::cout << " 42";
}
if v is (24) {
std::cout << " 24";
}
if v is (100) {
std::cout << " 100";
}
if v is (-100) {
std::cout << " -100";
}
if v is (314) {
std::cout << " 314";
}
if v is (std::optional<int>(100)) {
std::cout << " std::optional<int>(100)";
}
if v is (std::any(-100)) {
std::cout << " std::any(-100)";
}
if v is (new<int>(1000)) {
std::cout << " std::unique_ptr<int>(1000)";
}
i : int = 314;
if v is (i&) {
std::cout << " *int(314)";
}
if v is (in(0,100)) {
std::cout << " in(0,100)";
}
std::cout << "\n---" << std::endl;
}

my_variant: type == std::variant<std::monostate, int, int, std::optional<int>, std::any, *int, std::unique_ptr<int>>;

main: () -> int = {

v: std::variant<std::monostate, int, int, std::optional<int>, std::any, *int, std::unique_ptr<int>, my_variant> = ();

header(1, "std::monostate");
v..emplace<0>();
test(v);

header(1, "int(42)");
v..emplace<1>(42);
test(v);

header(1, "int(24)");
v..emplace<2>(24);
test(v);

header(1, "std::optional<int>(100)");
v..emplace<3>(100);
test(v);

header(1, "std::any(-100)");
v..emplace<4>(-100);
test(v);

i : int = 314;
header(1, "*int(314)");
v..emplace<5>(i&);
test(v);

header(1, "std::unique_ptr<int>(1000)");
v..emplace<6>(new<int>(1000));
test(v);

header(1, "my_variant(std::monostate)");
v..emplace<7>();
test(v);

header(1, "my_variant(int(42))");
v..emplace<7>();
std::get<7>(v)..emplace<1>(42);
test(v);

header(1, "my_variant(int(24))");
v..emplace<7>();
std::get<7>(v)..emplace<2>(24);
test(v);

header(1, "my_variant(std::optional<int>(100))");
v..emplace<7>();
std::get<7>(v)..emplace<3>(100);
test(v);

header(1, "my_variant(std::any(-100))");
v..emplace<7>();
std::get<7>(v)..emplace<4>(-100);
test(v);

header(1, "my_variant(*int(314))");
v..emplace<7>();
std::get<7>(v)..emplace<5>(i&);
test(v);

header(1, "my_variant(std::unique_ptr<int>(1000))");
v..emplace<7>();
std::get<7>(v)..emplace<6>(new<int>(1000));
test(v);
}

header: (lvl : int, msg: std::string) = {
std::cout << std::string(lvl, '#') << " " << msg << std::endl;
}
91 changes: 91 additions & 0 deletions regression-tests/mixed-is-as-variant.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
test: (forward v) = {
std::cout << "v is empty = (v is void)$" << std::endl;
std::cout << "v is std::monostate = (v is std::monostate)$" << std::endl;
std::cout << "v is X< 0> = (v is X< 0>)$,\t(v as X< 1>) = " << expect_no_throw(forward v, :(forward v) v as X<0>) << std::endl;
std::cout << "v is X< 1> = (v is X< 1>)$,\t(v as X< 1>).to_string() = (expect_no_throw(forward v, :(forward v) -> std::string = { return (v as X< 1>).to_string();}))$" << std::endl;
std::cout << "v is X<19> = (v is X<19>)$,\t(v as X<19>).to_string() = (expect_no_throw(forward v, :(forward v) -> std::string = { return (v as X<19>).to_string();}))$" << std::endl;
std::cout << "v is X<20> = (v is X<20>)$,\t(v as X<20>) = " << expect_no_throw(forward v, :(forward v) v as X<20>) << std::endl;
std::cout << std::endl;
}

main: () -> int = {

v: std::variant<std::monostate,
X< 1>, X< 2>, X< 3>, X< 4>, X< 5>, X< 6>, X< 7>, X< 8>, X< 9>, X<10>,
X<11>, X<12>, X<13>, X<14>, X<15>, X<16>, X<17>, X<18>, X<19>, X<20> > = ();

header(1, "std::monostate");
v..emplace<0>();
run_tests(v);

header(1, "X<1>");
v..emplace<1>();
run_tests(v);

header(1, "X<19>");
v..emplace<19>();
run_tests(v);

header(1, "X<20>");
v..emplace<20>();
run_tests(v);

header(1, "X<10>(std::exception)");
set_to_valueless_by_exception<10>(v);
run_tests(v);

}

run_tests: (forward v) = {
header(2, "v as lvalue reference");
test(v);

header(2, "v as const lvalue reference");
test(std::as_const(v));

header(2, "v as rvalue reference");
test(move v);
}

header: (lvl : int, msg: std::string) = {
std::cout << std::string(lvl, '#') << " " << msg << "\n" << std::endl;
}

template<int I>
struct X {
operator int() const { return I; }
X() = default;
X(std::exception const& e) { throw e; }
auto to_string() const { return "X<" + std::to_string(I) + ">"; }
};

template <std::size_t I>
void set_to_valueless_by_exception(auto& v) try {
v.template emplace<I>(std::runtime_error("make valueless"));
} catch (...) {}

auto expect_no_throw(auto&& l) -> std::string try {
if constexpr ( requires { { l() } -> std::convertible_to<std::string>; }) {
return l();
} else {
l();
return "works!";
}
} catch (std::exception const& e) {
return e.what();
} catch (...) {
return "unknown exception!";
}

auto expect_no_throw(auto&& v, auto&& l) -> std::string try {
if constexpr ( requires { { l(v) } -> std::convertible_to<std::string>; }) {
return l(v);
} else {
l(v);
return "works!";
}
} catch (std::exception const& e) {
return e.what();
} catch (...) {
return "unknown exception!";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# std::monostate

---
# int(42)
42 in(0,100)
---
# int(24)
24 in(0,100)
---
# std::optional<int>(100)
100 std::optional<int>(100)
---
# std::any(-100)

---
# *int(314)

---
# std::unique_ptr<int>(1000)

---
# my_variant(std::monostate)

---
# my_variant(int(42))

---
# my_variant(int(24))

---
# my_variant(std::optional<int>(100))

---
# my_variant(std::any(-100))

---
# my_variant(*int(314))

---
# my_variant(std::unique_ptr<int>(1000))

---
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# std::monostate

## v as lvalue reference

v is empty = true
v is std::monostate = true
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = false, (v as X<20>) = bad_variant_access

## v as const lvalue reference

v is empty = true
v is std::monostate = true
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = false, (v as X<20>) = bad_variant_access

## v as rvalue reference

v is empty = true
v is std::monostate = true
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = false, (v as X<20>) = bad_variant_access

# X<1>

## v as lvalue reference

v is empty = false
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = true, (v as X< 1>).to_string() = X<1>
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = false, (v as X<20>) = bad_variant_access

## v as const lvalue reference

v is empty = false
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = true, (v as X< 1>).to_string() = X<1>
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = false, (v as X<20>) = bad_variant_access

## v as rvalue reference

v is empty = false
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = true, (v as X< 1>).to_string() = X<1>
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = false, (v as X<20>) = bad_variant_access

# X<19>

## v as lvalue reference

v is empty = false
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = true, (v as X<19>).to_string() = X<19>
v is X<20> = false, (v as X<20>) = bad_variant_access

## v as const lvalue reference

v is empty = false
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = true, (v as X<19>).to_string() = X<19>
v is X<20> = false, (v as X<20>) = bad_variant_access

## v as rvalue reference

v is empty = false
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = true, (v as X<19>).to_string() = X<19>
v is X<20> = false, (v as X<20>) = bad_variant_access

# X<20>

## v as lvalue reference

v is empty = false
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = true, (v as X<20>) = works!

## v as const lvalue reference

v is empty = false
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = true, (v as X<20>) = works!

## v as rvalue reference

v is empty = false
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = true, (v as X<20>) = works!

# X<10>(std::exception)

## v as lvalue reference

v is empty = true
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = false, (v as X<20>) = bad_variant_access

## v as const lvalue reference

v is empty = true
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = false, (v as X<20>) = bad_variant_access

## v as rvalue reference

v is empty = true
v is std::monostate = false
v is X< 0> = false, (v as X< 1>) = bad_variant_access
v is X< 1> = false, (v as X< 1>).to_string() = bad_variant_access
v is X<19> = false, (v as X<19>).to_string() = bad_variant_access
v is X<20> = false, (v as X<20>) = bad_variant_access

Original file line number Diff line number Diff line change
@@ -1 +1 @@
../../../include/cpp2util.h(1007) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector<int>]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4]
../../../include/cpp2util.h(1103) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector<int>]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4]
Original file line number Diff line number Diff line change
@@ -1 +1 @@
../../../include/cpp2util.h(819) : Bounds safety violation
../../../include/cpp2util.h(915) : Bounds safety violation
Loading

0 comments on commit 78867f8

Please sign in to comment.