Skip to content

Commit 025d7af

Browse files
committed
Support more unary and binary fold-expressions
Also support interpolation and "as std::string" for ints and bools (and remove uses of `std::boolalpha`, use `as std::string` or interpolation instead)
1 parent 1043622 commit 025d7af

24 files changed

+283
-228
lines changed

include/cpp2util.h

Lines changed: 118 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,13 @@ using __uchar = unsigned char; // normally use u8 instead
286286
//-----------------------------------------------------------------------
287287
//
288288

289-
constexpr auto max(auto... values) {
289+
inline constexpr auto max(auto... values) {
290290
return std::max( { values... } );
291291
}
292292

293+
template <class T, class... Ts>
294+
inline constexpr auto is_any = std::disjunction_v<std::is_same<T, Ts>...>;
295+
293296

294297
//-----------------------------------------------------------------------
295298
//
@@ -781,6 +784,108 @@ class out {
781784
}(PARAM1)
782785

783786

787+
//-----------------------------------------------------------------------
788+
//
789+
// to_string for string interpolation
790+
//
791+
//-----------------------------------------------------------------------
792+
//
793+
inline auto to_string(...) -> std::string
794+
{
795+
return "(customize me - no cpp2::to_string overload exists for this type)";
796+
}
797+
798+
inline auto to_string(std::same_as<std::any> auto const&) -> std::string
799+
{
800+
return "std::any";
801+
}
802+
803+
inline auto to_string(bool b) -> std::string
804+
{
805+
return b ? "true" : "false";
806+
}
807+
808+
template<typename T>
809+
inline auto to_string(T const& t) -> std::string
810+
requires requires { std::to_string(t); }
811+
{
812+
return std::to_string(t);
813+
}
814+
815+
inline auto to_string(char const& t) -> std::string
816+
{
817+
return std::string{t};
818+
}
819+
820+
inline auto to_string(char const* s) -> std::string
821+
{
822+
return std::string{s};
823+
}
824+
825+
inline auto to_string(std::string const& s) -> std::string const&
826+
{
827+
return s;
828+
}
829+
830+
template<typename T>
831+
inline auto to_string(T const& sv) -> std::string
832+
requires (std::is_convertible_v<T, std::string_view>
833+
&& !std::is_convertible_v<T, const char*>)
834+
{
835+
return std::string{sv};
836+
}
837+
838+
template <typename... Ts>
839+
inline auto to_string(std::variant<Ts...> const& v) -> std::string;
840+
841+
template < typename T, typename U>
842+
inline auto to_string(std::pair<T,U> const& p) -> std::string;
843+
844+
template < typename... Ts>
845+
inline auto to_string(std::tuple<Ts...> const& t) -> std::string;
846+
847+
template<typename T>
848+
inline auto to_string(std::optional<T> const& o) -> std::string {
849+
if (o.has_value()) {
850+
return cpp2::to_string(o.value());
851+
}
852+
return "(empty)";
853+
}
854+
855+
template <typename... Ts>
856+
inline auto to_string(std::variant<Ts...> const& v) -> std::string
857+
{
858+
if (v.valueless_by_exception()) return "(empty)";
859+
// Need to guard this with is_any otherwise the get_if is illegal
860+
if constexpr (is_any<std::monostate, Ts...>) if (std::get_if<std::monostate>(&v) != nullptr) return "(empty)";
861+
862+
return std::visit([](auto&& arg) -> std::string {
863+
return cpp2::to_string(arg);
864+
}, v);
865+
}
866+
867+
template < typename T, typename U>
868+
inline auto to_string(std::pair<T,U> const& p) -> std::string
869+
{
870+
return "(" + cpp2::to_string(p.first) + ", " + cpp2::to_string(p.second) + ")";
871+
}
872+
873+
template < typename... Ts>
874+
inline auto to_string(std::tuple<Ts...> const& t) -> std::string
875+
{
876+
if constexpr (sizeof...(Ts) == 0) {
877+
return "()";
878+
} else {
879+
std::string out = "(" + cpp2::to_string(std::get<0>(t));
880+
std::apply([&out](auto&&, auto&&... args) {
881+
((out += ", " + cpp2::to_string(args)), ...);
882+
}, t);
883+
out += ")";
884+
return out;
885+
}
886+
}
887+
888+
784889
//-----------------------------------------------------------------------
785890
//
786891
// is and as
@@ -1000,9 +1105,20 @@ auto as( X& x ) -> decltype(auto) {
10001105
return x;
10011106
}
10021107

1108+
1109+
template< typename C, typename X >
1110+
auto as(X const& x) -> C
1111+
requires (std::is_same_v<C, std::string> && std::is_integral_v<X>)
1112+
{
1113+
return cpp2::to_string(x);
1114+
}
1115+
1116+
10031117
template< typename C, typename X >
10041118
auto as( X const& x ) -> auto
1005-
requires (!std::is_same_v<C, X> && !std::is_base_of_v<C, X> && requires { C{x}; })
1119+
requires (!std::is_same_v<C, X> && !std::is_base_of_v<C, X> && requires { C{x}; }
1120+
&& !(std::is_same_v<C, std::string> && std::is_integral_v<X>) // exclude above case
1121+
)
10061122
{
10071123
// Experiment: Recognize the nested `::value_type` pattern for some dynamic library types
10081124
// like std::optional, and try to prevent accidental narrowing conversions even when
@@ -1158,9 +1274,6 @@ constexpr auto is( std::variant<Ts...> const& x, auto const& value ) -> bool
11581274

11591275
// as
11601276
//
1161-
template <class T, class... Ts>
1162-
inline constexpr auto is_any = std::disjunction_v<std::is_same<T, Ts>...>;
1163-
11641277
template<typename T, typename... Ts>
11651278
auto is( std::variant<Ts...> const& x ) {
11661279
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) { if (x.index() == 0) return true; }
@@ -1424,115 +1537,6 @@ class finally
14241537
};
14251538

14261539

1427-
//-----------------------------------------------------------------------
1428-
//
1429-
// to_string for string interpolation
1430-
//
1431-
//-----------------------------------------------------------------------
1432-
//
1433-
inline auto to_string(...) -> std::string {
1434-
return "(customize me - no cpp2::to_string overload exists for this type)";
1435-
}
1436-
1437-
inline auto to_string(std::same_as<std::any> auto const&) -> std::string {
1438-
return "std::any";
1439-
}
1440-
1441-
template<typename T>
1442-
inline auto to_string(T const& t) -> std::string
1443-
requires requires { std::to_string(t); }
1444-
{
1445-
return std::to_string(t);
1446-
}
1447-
1448-
inline auto to_string(char const& t) -> std::string
1449-
{
1450-
return std::string{t};
1451-
}
1452-
1453-
inline auto to_string(char const* s) -> std::string
1454-
{
1455-
return std::string{s};
1456-
}
1457-
1458-
inline auto to_string(std::string const& s) -> std::string const&
1459-
{
1460-
return s;
1461-
}
1462-
1463-
template<typename T>
1464-
inline auto to_string(T const& sv) -> std::string
1465-
requires (std::is_convertible_v<T, std::string_view>
1466-
&& !std::is_convertible_v<T, const char*>)
1467-
{
1468-
return std::string{sv};
1469-
}
1470-
1471-
template <typename... Ts>
1472-
inline auto to_string(std::variant<Ts...> const& v) -> std::string;
1473-
1474-
template < typename T, typename U>
1475-
inline auto to_string(std::pair<T,U> const& p) -> std::string;
1476-
1477-
template < typename... Ts>
1478-
inline auto to_string(std::tuple<Ts...> const& t) -> std::string;
1479-
1480-
template<typename T>
1481-
inline auto to_string(std::optional<T> const& o) -> std::string {
1482-
if (o.has_value()) {
1483-
return cpp2::to_string(o.value());
1484-
}
1485-
return "(empty)";
1486-
}
1487-
1488-
template <typename... Ts>
1489-
inline auto to_string(std::variant<Ts...> const& v) -> std::string
1490-
{
1491-
if (v.valueless_by_exception()) return "(empty)";
1492-
// Need to guard this with is_any otherwise the get_if is illegal
1493-
if constexpr (is_any<std::monostate, Ts...>) if (std::get_if<std::monostate>(&v) != nullptr) return "(empty)";
1494-
1495-
return std::visit([](auto&& arg) -> std::string {
1496-
return cpp2::to_string(arg);
1497-
}, v);
1498-
}
1499-
1500-
template < typename T, typename U>
1501-
inline auto to_string(std::pair<T,U> const& p) -> std::string
1502-
{
1503-
return "(" + cpp2::to_string(p.first) + ", " + cpp2::to_string(p.second) + ")";
1504-
}
1505-
1506-
template < typename... Ts>
1507-
inline auto to_string(std::tuple<Ts...> const& t) -> std::string
1508-
{
1509-
if constexpr (sizeof...(Ts) == 0) {
1510-
return "()";
1511-
} else {
1512-
std::string out = "(" + cpp2::to_string(std::get<0>(t));
1513-
std::apply([&out](auto&&, auto&&... args) {
1514-
((out += ", " + cpp2::to_string(args)), ...);
1515-
}, t);
1516-
out += ")";
1517-
return out;
1518-
}
1519-
}
1520-
1521-
1522-
//-----------------------------------------------------------------------
1523-
//
1524-
// and "as std::string" for the same cases
1525-
//
1526-
template< typename C >
1527-
auto as(auto& x) -> C
1528-
requires (std::is_same_v<C, std::string>
1529-
&& requires { cpp2::to_string(x); })
1530-
{
1531-
return cpp2::to_string(x);
1532-
}
1533-
1534-
1535-
15361540
//-----------------------------------------------------------------------
15371541
//
15381542
// args: see main() arguments as vector<string_view>

regression-tests/mixed-fixed-type-aliases.cpp2

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ namespace my {
88

99
test: (x:_) = {
1010
std::cout
11-
<< std::boolalpha
12-
<< std::is_floating_point_v<CPP2_TYPEOF(x)>
11+
<< std::is_floating_point_v<CPP2_TYPEOF(x)> as std::string
1312
<< "\n";
1413
}
1514

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
11
main: () =
2-
std::cout
3-
<< std::boolalpha
4-
<< (std::is_void_v<* i32> && std::is_void_v<const i32>)
5-
<< "\n";
2+
std::cout << "(std::is_void_v<* i32> && std::is_void_v<const i32>)$\n";

regression-tests/pure2-chained-comparisons.cpp2

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
main: () -> int = {
2-
std::cout << std::boolalpha;
32
i:=0; while i<3 next i++ {
43
j:=0; while j<3 next j++ {
54
k:=0; while k<3 next k++ {
65
std::cout
76
<< i << " "
87
<< j << " "
98
<< k << ": "
10-
<< (i == j == k) << " "
11-
<< (i < j <= k) << " "
12-
<< (i >= j > k) << "\n";
9+
<< (i == j == k) as std::string << " "
10+
<< (i < j <= k) as std::string << " "
11+
<< (i >= j > k) as std::string << "\n";
1312
}
1413
}
1514
}

regression-tests/pure2-variadics.cpp2

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11

2+
// Type pack expansion
23
x: <Ts...: type> type = {
34
tup: std::tuple<Ts...> = ();
45
}
56

7+
print: <Args...: type> (inout out: std::ostream, args...: Args) = {
8+
// Binary left fold expression
9+
(out << ... << args);
10+
}
11+
12+
all: <Args...: type> (args...: Args) -> bool =
13+
// Unary left fold expression
14+
(... && args);
15+
616
make_string: <Args...: type> (forward args...: Args) -> _ = :std::string = args...;
717

818
make: <T, Args...: type> (forward args...: Args) -> _ = :T = args...;
@@ -14,4 +24,9 @@ main: ()
1424
std::cout << std::string("xyzzy", 3) << "\n";
1525
std::cout << make_string("plugh", :u8=3) << "\n";
1626
std::cout << make<std::string>("abracadabra", :u8=3) << "\n";
27+
28+
print( std::cout, 3.14, "word", -1500 );
29+
30+
std::cout << "\nfirst all() returned (all(true, true, true, false))$";
31+
std::cout << "\nsecond all() returned " << all(true, true, true, true) as std::string;
1732
}

regression-tests/test-results/clang-12/pure2-enum.cpp.execution

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ f2 is (none)
1010
f2 is (cached)
1111
f as int is 1
1212
f2 as int is 1
13-
f is (f2) is 1
14-
f2 is (f ) is 1
13+
f is (f2) is true
14+
f2 is (f ) is true
1515

1616
f is (cached, current, obsolete, cached_and_current)
1717
f2 is (cached, current, cached_and_current)
1818
f as int is 7
1919
f2 as int is 3
20-
f == f2 is 0
21-
f is (f2) is 0
22-
f2 is (f ) is 0
23-
(f & f2) == f2 is 1
20+
f == f2 is false
21+
f is (f2) is false
22+
f2 is (f ) is false
23+
(f & f2) == f2 is true
2424
inspecting: includes all f2's flags ('cached' and 'current')
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
xyz
22
plu
33
abr
4+
3.14word-1500
5+
first all() returned false
6+
second all() returned true

regression-tests/test-results/gcc-10/pure2-bugfix-for-requires-clause-in-forward-declaration.cpp.output

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:46: error: expect
1111
In file included from pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:7:
1212
../../../include/cpp2util.h:10005:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
1313
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:4:1: note: in expansion of macro ‘CPP2_REQUIRES_’
14-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:82&&) requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type, std::__cxx11::string>’
14+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:81&&) requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type, std::__cxx11::string>’
1515
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:11: note: candidates are: ‘element::element(const element&)’
16-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:16:20: note: ‘template<class auto:80> element::element(auto:80&&)’
16+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:16:20: note: ‘template<class auto:79> element::element(auto:79&&)’
1717
16 | public: explicit element(auto&& n)
1818
| ^~~~~~~
1919
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:14:7: note: ‘class element’ defined here
2020
14 | class element {
2121
| ^~~~~~~
2222
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: expected unqualified-id before ‘{’ token
23-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:83&&) requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type, std::__cxx11::string>’
23+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:82&&) requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type, std::__cxx11::string>’
2424
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:6:16: note: candidates are: ‘void element::operator=(const element&)’
25-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:81> element& element::operator=(auto:81&&)’
25+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:80> element& element::operator=(auto:80&&)’
2626
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:14:7: note: ‘class element’ defined here
2727
14 | class element {
2828
| ^~~~~~~

0 commit comments

Comments
 (0)