Skip to content

Commit 32357dd

Browse files
committed
Support local function default template/ordinary parameters
This unlocks idioms useful in the regression tests, to disable test cases not supported on all compilers. An example that we can now do without macros: : <V: bool = gcc_clang_msvc_min_versions(1400, 1600, 1920)> () = { if constexpr V { // use std::views stuff without compiler bugs... // note this goes beyond what feature test macros allow, // because the feature may be implemented (and so // report it's available) but bugs may block the actual use } } (); And expand boolean value string output to explicitly-boolean-convertible types
1 parent 065a993 commit 32357dd

22 files changed

+207
-161
lines changed

include/cpp2util.h

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -332,20 +332,42 @@
332332
#define CPP2_CONTINUE_BREAK(NAME) goto CONTINUE_##NAME; CONTINUE_##NAME: continue; goto BREAK_##NAME; BREAK_##NAME: break;
333333
// these redundant goto's to avoid 'unused label' warnings
334334

335+
// Compiler version identification.
336+
//
337+
// This can use useful with 'if constexpr' to disable code known not to
338+
// work on some otherwise-supported compilers (without macros), for example:
339+
//
340+
// // Disable tests on lower-level compilers that have blocking bugs
341+
// []<auto V = gcc_clang_msvc_min_versions(1400, 1600, 1920)> () { if constexpr (V) {
342+
// // ... tests that would fail due to older compilers' bugs ...
343+
// }}();
344+
//
345+
// Note: Test Clang first because it pretends to be other compilers.
346+
//
335347
#if defined(__clang_major__)
336-
constexpr auto CPP2_CLANG_VER = __clang_major__ * 100 + __clang_minor__;
337-
constexpr auto CPP2_MSVC_VER = 0;
338-
constexpr auto CPP2_GCC_VER = 0;
348+
constexpr auto gcc_ver = 0;
349+
constexpr auto clang_ver = __clang_major__ * 100 + __clang_minor__;
350+
constexpr auto msvc_ver = 0;
339351
#elif defined(_MSC_VER)
340-
constexpr auto CPP2_CLANG_VER = 0;
341-
constexpr auto CPP2_MSVC_VER = _MSC_VER;
342-
constexpr auto CPP2_GCC_VER = 0;
352+
constexpr auto gcc_ver = 0;
353+
constexpr auto clang_ver = 0;
354+
constexpr auto msvc_ver = _MSC_VER;
343355
#elif defined(__GNUC__)
344-
constexpr auto CPP2_CLANG_VER = 0;
345-
constexpr auto CPP2_MSVC_VER = 0;
346-
constexpr auto CPP2_GCC_VER = __GNUC__ * 100 + __GNUC_MINOR__;
356+
constexpr auto gcc_ver = __GNUC__ * 100 + __GNUC_MINOR__;
357+
constexpr auto clang_ver = 0;
358+
constexpr auto msvc_ver = 0;
347359
#endif
348360

361+
constexpr auto gcc_clang_msvc_min_versions(
362+
auto gcc,
363+
auto clang,
364+
auto msvc
365+
)
366+
{
367+
return gcc_ver > gcc || clang_ver > clang || msvc_ver > msvc;
368+
}
369+
370+
349371
#if defined(_MSC_VER)
350372
// MSVC can't handle 'inline constexpr' yet in all cases
351373
#define CPP2_CONSTEXPR const
@@ -1492,8 +1514,9 @@ inline auto to_string(auto const& x) -> std::string
14921514
return x;
14931515
}
14941516

1495-
// Else customize bool (prefer this over << to avoid std::boolalpha)
1496-
if constexpr( std::is_same_v<CPP2_TYPEOF(x), bool> ) {
1517+
// Else customize convertible-to-bool - use { } to avoid narrowing
1518+
if constexpr( requires{ bool{x}; } )
1519+
{
14971520
return x ? "true" : "false";
14981521
}
14991522

regression-tests/mixed-type-safety-1.cpp2

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,18 @@ class Shape { public: virtual ~Shape() { } };
88
class Circle : public Shape { };
99
class Square : public Shape { };
1010

11-
//--- printing helpers -----------------
1211

13-
print: <T : type> ( msg: std::string, x: T )
14-
requires !std::convertible_to<T, bool> =
15-
std::cout << msg << x << "\n";
16-
17-
print: ( msg: std::string, b: bool ) =
18-
{
19-
bmsg: * const char;
20-
if b { bmsg = "true"; }
21-
else { bmsg = "false"; }
22-
std::cout << msg << bmsg << "\n";
23-
}
24-
25-
//--- examples -------------------------
12+
print: <T : type> ( msg: std::string, x: T ) =
13+
std::cout << "(msg)$ (x)$\n";
2614

2715
main: () -> int =
2816
{
29-
print( "1.1 is int? ", 1.1 is int );
30-
print( "1 is int? ", 1 is int );
17+
print( "1.1 is int?", 1.1 is int );
18+
print( "1 is int?", 1 is int );
3119

3220
c := new<Circle>(); // safe by construction
3321
s : * Shape = c.get(); // safe by Lifetime
34-
print("\ns* is Shape? ", s* is Shape );
35-
print( "s* is Circle? ", s* is Circle );
36-
print( "s* is Square? ", s* is Square );
22+
print("\ns* is Shape? ", s* is Shape );
23+
print( "s* is Circle?", s* is Circle );
24+
print( "s* is Square?", s* is Square );
3725
}

regression-tests/pure2-default-arguments.cpp2

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,14 @@ main: (args) = {
1616
f();
1717
f(1);
1818
f(2);
19+
20+
: <V: bool = gcc_clang_msvc_min_versions( 1400, 1600, 1920)> () = {
21+
if constexpr V {
22+
std::cout << "a newer compiler\n";
23+
}
24+
else {
25+
std::cout << "an older compiler\n";
26+
}
27+
} ();
28+
1929
}

regression-tests/pure2-range-operators.cpp2

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,19 @@ main: () = {
3838
std::cout << "((1 ..< 20).contains(21))$\n"; // prints false
3939

4040
// Only run these parts on implementations that support views::take
41-
// : <V: bool = (CPP2_GCC_VER > 1400 || CPP2_CLANG_VER > 1600 || CPP2_MSVC_VER > 1920)> () = {
42-
// if constexpr V
43-
// {
44-
// using std::views::_ ;
41+
: <V: bool = gcc_clang_msvc_min_versions(1400, 1600, 1920)> () = {
42+
if constexpr V
43+
{
44+
using std::views::_ ;
4545

46-
// std::cout << "\nMake sure views::take works:\n";
47-
// for (1 ..= 100).take(5) do (e) {
48-
// std::cout << e << " ";
49-
// } // prints: 1 2 3 4 5
50-
51-
// for (2 ..< 5).take(5) do (e) {
52-
// std::cout << e << " ";
53-
// } // prints: 2 3 4
54-
// }
55-
// } ();
46+
std::cout << "\nMake sure views::take works:\n";
47+
for (1 ..= 100).take(5) do (e) {
48+
std::cout << e << " ";
49+
} // prints: 1 2 3 4 5
50+
51+
for (2 ..< 5).take(5) do (e) {
52+
std::cout << e << " ";
53+
} // prints: 2 3 4
54+
}
55+
} ();
5656
}

regression-tests/test-results/gcc-10-c++20/mixed-type-safety-1.cpp.output

Lines changed: 0 additions & 5 deletions
This file was deleted.

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:46: error: expect
66
In file included from pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:7:
77
../../../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.
88
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:4:1: note: in expansion of macro ‘CPP2_REQUIRES_’
9-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:95&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type>’
9+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:98&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type>’
1010
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:11: note: candidates are: ‘element::element(const element&)’
11-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template<class auto:93> element::element(auto:93&&)’
11+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template<class auto:96> element::element(auto:96&&)’
1212
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here
1313
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:78: error: expected unqualified-id before ‘{’ token
14-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:96&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type>’
14+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:99&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type>’
1515
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:6:16: note: candidates are: ‘void element::operator=(const element&)’
16-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:94> element& element::operator=(auto:94&&)’
16+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:97> element& element::operator=(auto:97&&)’
1717
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here
Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,66 @@
11
In file included from pure2-default-arguments.cpp:7:
22
../../../include/cpp2util.h:2086:28: error: local variable ‘obj’ may not appear in this context
3-
2086 |
4-
| ^
3+
2086 | auto as( std::variant<Ts...> & x ) -> decltype(auto) {
4+
| ^~~
55
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
6-
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return operator_as<8>(x); }
6+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<15>(x)), T >) { if (x.index() == 15) return true; }
77
| ^~~~~~~~~~~
88
../../../include/cpp2util.h:2086:15: note: in expansion of macro ‘CPP2_FORWARD’
9-
2086 |
10-
| ^
9+
2086 | auto as( std::variant<Ts...> & x ) -> decltype(auto) {
10+
| ^~~~~~~~~~~~
1111
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
12-
2107 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
12+
2107 | Throw( std::bad_variant_access(), "'as' cast failed for 'variant'");
1313
| ^~~~~~~~~~~~~~~~~~~~~~~~
1414
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
15-
2137 | }
15+
2137 | // std::any is and as
1616
| ^
1717
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
1818
../../../include/cpp2util.h:2086:92: error: local variable ‘params’ may not appear in this context
19-
2086 |
19+
2086 | auto as( std::variant<Ts...> & x ) -> decltype(auto) {
2020
| ^
2121
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
22-
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return operator_as<8>(x); }
22+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<15>(x)), T >) { if (x.index() == 15) return true; }
2323
| ^~~~~~~~~~~
2424
../../../include/cpp2util.h:2086:79: note: in expansion of macro ‘CPP2_FORWARD’
25-
2086 |
25+
2086 | auto as( std::variant<Ts...> & x ) -> decltype(auto) {
2626
| ^
2727
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
28-
2107 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
28+
2107 | Throw( std::bad_variant_access(), "'as' cast failed for 'variant'");
2929
| ^~~~~~~~~~~~~~~~~~~~~~~~
3030
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
31-
2137 | }
31+
2137 | // std::any is and as
3232
| ^
3333
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
3434
../../../include/cpp2util.h:2087:74: error: local variable ‘obj’ may not appear in this context
35-
2087 | template<typename T, typename... Ts>
36-
| ^
35+
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) { if (x.index() == 0) return operator_as<0>(x); }
36+
| ^~~
3737
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
38-
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return operator_as<8>(x); }
38+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<15>(x)), T >) { if (x.index() == 15) return true; }
3939
| ^~~~~~~~~~~
4040
../../../include/cpp2util.h:2087:61: note: in expansion of macro ‘CPP2_FORWARD’
41-
2087 | template<typename T, typename... Ts>
42-
| ^
41+
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) { if (x.index() == 0) return operator_as<0>(x); }
42+
| ^~~~~~~~~~~~
4343
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
44-
2107 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
44+
2107 | Throw( std::bad_variant_access(), "'as' cast failed for 'variant'");
4545
| ^~~~~~~~~~~~~~~~~~~~~~~~
4646
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
47-
2137 | }
47+
2137 | // std::any is and as
4848
| ^
4949
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
5050
../../../include/cpp2util.h:2087:93: error: local variable ‘params’ may not appear in this context
51-
2087 | template<typename T, typename... Ts>
52-
| ^
51+
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) { if (x.index() == 0) return operator_as<0>(x); }
52+
| ^~~~~~
5353
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
54-
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return operator_as<8>(x); }
54+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<15>(x)), T >) { if (x.index() == 15) return true; }
5555
| ^~~~~~~~~~~
5656
../../../include/cpp2util.h:2087:80: note: in expansion of macro ‘CPP2_FORWARD’
57-
2087 | template<typename T, typename... Ts>
58-
| ^
57+
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 0>(x)), T >) { if (x.index() == 0) return operator_as<0>(x); }
58+
| ^~~~~~~~~~~~
5959
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
60-
2107 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
60+
2107 | Throw( std::bad_variant_access(), "'as' cast failed for 'variant'");
6161
| ^~~~~~~~~~~~~~~~~~~~~~~~
6262
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
63-
2137 | }
63+
2137 | // std::any is and as
6464
| ^
6565
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
6666
pure2-default-arguments.cpp2:6:61: error: ‘std::source_location’ has not been declared

regression-tests/test-results/gcc-10-c++20/pure2-print.cpp.output

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ pure2-print.cpp2:68:1: note: in expansion of macro ‘CPP2_REQUIRES_’
99
pure2-print.cpp2:97:1: note: in expansion of macro ‘CPP2_REQUIRES_’
1010
pure2-print.cpp2:9:41: error: ‘constexpr const T outer::object_alias’ is not a static data member of ‘class outer’
1111
pure2-print.cpp2:9:48: error: template definition of non-template ‘constexpr const T outer::object_alias’
12-
pure2-print.cpp2:67:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:94& ...) requires (is_convertible_v<typename std::remove_cv<typename std::remove_reference<decltype(outer::mytype::variadic::x)>::type>::type, int> && ...)’
13-
pure2-print.cpp2:67:29: note: candidate is: ‘template<class ... auto:93> static void outer::mytype::variadic(const auto:93& ...)’
12+
pure2-print.cpp2:67:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:97& ...) requires (is_convertible_v<typename std::remove_cv<typename std::remove_reference<decltype(outer::mytype::variadic::x)>::type>::type, int> && ...)’
13+
pure2-print.cpp2:67:29: note: candidate is: ‘template<class ... auto:96> static void outer::mytype::variadic(const auto:96& ...)’
1414
pure2-print.cpp2:10:19: note: ‘class outer::mytype’ defined here
1515
pure2-print.cpp2:96:37: error: no declaration matches ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’
1616
pure2-print.cpp2:96:37: note: no functions named ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:105&, cpp2::impl::in<int>, cpp2::impl::in<int>) [with auto:105 = std::vector<int>; cpp2::impl::in<int> = const int]: Bounds safety violation
1+
mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:108&, cpp2::impl::in<int>, cpp2::impl::in<int>) [with auto:108 = std::vector<int>; cpp2::impl::in<int> = const int]: Bounds safety violation

0 commit comments

Comments
 (0)