diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e762b7372..fca085636 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -91,6 +91,7 @@ jobs: # =================== linux: runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental || false }} strategy: fail-fast: false matrix: @@ -111,6 +112,24 @@ jobs: install_compiler: true compiler_package: g++-10 + - name: "clang-14, C++23 (early)" + os: ubuntu-22.04 + cc: clang-14 + cxx: clang++-14 + cxx_standard: "-DSQLITE_ORM_ENABLE_CXX_23=ON" + install_compiler: true + compiler_package: clang-14 + experimental: true + + - name: "gcc-11, C++23 (early)" + os: ubuntu-22.04 + cc: gcc-11 + cxx: g++-11 + cxx_standard: "-DSQLITE_ORM_ENABLE_CXX_23=ON" + install_compiler: true + compiler_package: g++-11 + experimental: true + name: Linux - ${{ matrix.name }} env: diff --git a/.gitignore b/.gitignore index 129aed371..6b9916a4d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,6 @@ cmake-build-debug/ build/ build-xcode/ build-code-edit/ +build-local/ *.sqlite -*.db \ No newline at end of file +*.db diff --git a/dev/functional/config.h b/dev/functional/config.h index 64a62c3bd..e2a3d4973 100644 --- a/dev/functional/config.h +++ b/dev/functional/config.h @@ -55,6 +55,10 @@ #define SQLITE_ORM_CPP20_RANGES_SUPPORTED #endif +#if __cpp_lib_ranges >= 202110L && !defined(SQLITE_ORM_BROKEN_CPP20_VIEWS) +#define SQLITE_ORM_CPP20_VIEWS_SUPPORTED +#endif + #if __cpp_lib_generator >= 202207L #define SQLITE_ORM_CPP23_GENERATOR_SUPPORTED #endif diff --git a/dev/functional/cxx_compiler_quirks.h b/dev/functional/cxx_compiler_quirks.h index 37bdf77ff..68a13d723 100644 --- a/dev/functional/cxx_compiler_quirks.h +++ b/dev/functional/cxx_compiler_quirks.h @@ -72,3 +72,7 @@ #if defined(SQLITE_ORM_CONCEPTS_SUPPORTED) && (defined(__clang__) && (__clang_major__ == 10)) #define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS #endif + +#if defined(__clang__) && (__clang_major__ <= 15) +#define SQLITE_ORM_BROKEN_CPP20_VIEWS +#endif diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index fc7cc4e7a..4b906c584 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1797,7 +1797,7 @@ namespace sqlite_orm::internal { const Ctx&) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; ss << "SET "; -#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED +#ifdef SQLITE_ORM_CPP20_VIEWS_SUPPORTED ss << streaming_serialized(statement | std::views::transform(&dynamic_set_entry::serialized_value)); #else int index = 0; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 261b47294..b75dfc5e6 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -221,6 +221,10 @@ using std::nullptr_t; #define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS #endif +#if defined(__clang__) && (__clang_major__ <= 15) +#define SQLITE_ORM_BROKEN_CPP20_VIEWS +#endif + // #include "platform_definitions.h" #if defined(_WIN32) @@ -313,6 +317,10 @@ using std::nullptr_t; #define SQLITE_ORM_CPP20_RANGES_SUPPORTED #endif +#if __cpp_lib_ranges >= 202110L && !defined(SQLITE_ORM_BROKEN_CPP20_VIEWS) +#define SQLITE_ORM_CPP20_VIEWS_SUPPORTED +#endif + #if __cpp_lib_generator >= 202207L #define SQLITE_ORM_CPP23_GENERATOR_SUPPORTED #endif @@ -23457,7 +23465,7 @@ namespace sqlite_orm::internal { const Ctx&) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; ss << "SET "; -#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED +#ifdef SQLITE_ORM_CPP20_VIEWS_SUPPORTED ss << streaming_serialized(statement | std::views::transform(&dynamic_set_entry::serialized_value)); #else int index = 0; diff --git a/tests/iterate.cpp b/tests/iterate.cpp index 0b9e7f490..62db3bcd1 100644 --- a/tests/iterate.cpp +++ b/tests/iterate.cpp @@ -76,6 +76,8 @@ TEST_CASE("Iterate select statement") { }; #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED constexpr orm_table_reference auto test_table = c(); +#endif +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES constexpr orm_table_alias auto test_alias = "t"_alias.for_(); #endif @@ -91,26 +93,32 @@ TEST_CASE("Iterate select statement") { db.replace(expected); std::vector expected_vec{expected}; +#ifdef SQLITE_ORM_CPP20_VIEWS_SUPPORTED SECTION("range-based for") { for (Test&& obj: db.iterate(select(object()))) { REQUIRE(obj == expected); } } +#endif #ifdef SQLITE_ORM_STL_HAS_DEFAULT_SENTINEL +#ifdef SQLITE_ORM_CPP20_VIEWS_SUPPORTED SECTION("borrowed iterator") { std::input_iterator auto begin = db.iterate(select(object())).begin(); REQUIRE(*begin == expected); REQUIRE(++begin == std::default_sentinel); } #endif +#endif #if __cpp_lib_containers_ranges >= 202202L +#ifdef SQLITE_ORM_CPP20_VIEWS_SUPPORTED SECTION("from range") { std::ranges::view auto view = db.iterate(select(object())); REQUIRE(std::vector{std::from_range, view} == expected_vec); } #endif +#endif #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -128,14 +136,18 @@ TEST_CASE("Iterate select statement") { auto view = db.yield(); REQUIRE(std::vector{std::from_range, view} == expected_vec); } +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED SECTION("object generator, table reference") { auto view = db.yield(); REQUIRE(std::vector{std::from_range, view} == expected_vec); } +#endif +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES SECTION("object generator, alias") { auto view = db.yield(); REQUIRE(std::vector{std::from_range, view} == expected_vec); } +#endif SECTION("select generator") { auto view = db.yield(select(object())); REQUIRE(std::vector{std::from_range, view} == expected_vec); diff --git a/tests/static_tests/function_static_tests.cpp b/tests/static_tests/function_static_tests.cpp index 69cd35a86..598d9d749 100644 --- a/tests/static_tests/function_static_tests.cpp +++ b/tests/static_tests/function_static_tests.cpp @@ -24,6 +24,8 @@ concept storage_aggregate_callable = requires(S& storage) { { storage.template create_aggregate_function() }; { storage.template delete_aggregate_function() }; }; + +constexpr auto clamp_int_ptr = &std::clamp; #endif TEST_CASE("function static") { @@ -343,16 +345,20 @@ TEST_CASE("function static") { } #endif SECTION("freestanding function") { - constexpr auto quotedScalar = "f"_scalar.quote(std::clamp); - using quoted_type = decltype("f"_scalar.quote(std::clamp)); +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 12) + SKIP("GCC < 12 cannot use this function pointer as NTTP in this test."); +#else + constexpr auto quotedScalar = "f"_scalar.quote(clamp_int_ptr); + using quoted_type = decltype("f"_scalar.quote(clamp_int_ptr)); + using expected_callable_type = std::remove_cv_t; STATIC_REQUIRE(quotedScalar._nme[0] == 'f' && quotedScalar._nme[1] == '\0'); STATIC_REQUIRE(std::is_same_v), + const quoted_scalar_function>); - STATIC_REQUIRE(std::is_same_v)>); + STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v::return_type, int>); @@ -368,18 +374,25 @@ TEST_CASE("function static") { using storage_type = decltype(make_storage("")); STATIC_REQUIRE(storage_scalar_callable); +#endif } SECTION("template function") { - constexpr auto quotedScalar = "f"_scalar.quote(std::clamp); - using quoted_type = decltype("f"_scalar.quote(std::clamp)); +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 12) + SKIP("GCC < 12 cannot use this function pointer as NTTP in this test."); +#else + constexpr auto quotedScalar = + "f"_scalar.quote(clamp_int_ptr); + using quoted_type = + decltype("f"_scalar.quote(clamp_int_ptr)); + using expected_callable_type = std::remove_cv_t; STATIC_REQUIRE(quotedScalar._nme[0] == 'f' && quotedScalar._nme[1] == '\0'); STATIC_REQUIRE(std::is_same_v), + const quoted_scalar_function>); - STATIC_REQUIRE(std::is_same_v)>); + STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v::return_type, int>); @@ -395,6 +408,7 @@ TEST_CASE("function static") { using storage_type = decltype(make_storage("")); STATIC_REQUIRE(storage_scalar_callable); +#endif } SECTION("lambda") { constexpr auto lambda = [](unsigned long errcode) { diff --git a/tests/static_tests/iterator_t.cpp b/tests/static_tests/iterator_t.cpp index 66abe8fb4..76dce29d8 100644 --- a/tests/static_tests/iterator_t.cpp +++ b/tests/static_tests/iterator_t.cpp @@ -126,8 +126,10 @@ concept storage_yield_result_set = requires(S& storage_type, Select select) { namespace { struct Object {}; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#if defined(SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED) && defined(SQLITE_ORM_WITH_CPP20_ALIASES) constexpr orm_table_alias auto object_alias = "o"_alias.for_(); +#endif +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED constexpr orm_table_reference auto object_table = c(); #endif } @@ -177,9 +179,15 @@ TEST_CASE("can view and iterate mapped") { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED STATIC_REQUIRE(storage_iterate_mapped); +#ifdef SQLITE_ORM_CPP20_VIEWS_SUPPORTED STATIC_REQUIRE(storage_iterate_mapped_ref); +#endif +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#ifdef SQLITE_ORM_CPP20_VIEWS_SUPPORTED STATIC_REQUIRE(storage_iterate_mapped_ref); #endif +#endif +#endif #ifdef SQLITE_ORM_CPP23_GENERATOR_SUPPORTED STATIC_REQUIRE(storage_yield_mapped); @@ -188,7 +196,7 @@ TEST_CASE("can view and iterate mapped") { #endif } -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#if defined(SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED) && defined(SQLITE_ORM_CPP20_VIEWS_SUPPORTED) TEST_CASE("can view and iterate result set") { struct Object {}; using empty_storage_type = decltype(make_storage("")); diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index 67d25abb0..47bf12226 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -569,6 +569,9 @@ TEST_CASE("generalized scalar udf") { storage.sync_schema(); SECTION("freestanding function") { +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 12) + SKIP("GCC < 12 cannot use this function as NTTP in quoted scalar tests."); +#else constexpr auto err_fatal_error_f = "ERR_FATAL_ERROR"_scalar.quote(ERR_FATAL_ERROR); storage.create_scalar_function(); { @@ -577,6 +580,7 @@ TEST_CASE("generalized scalar udf") { REQUIRE(rows == expected); } storage.delete_scalar_function(); +#endif } SECTION("stateless lambda") { constexpr auto is_fatal_error_f = "is_fatal_error"_scalar.quote([](unsigned long errcode) { @@ -659,6 +663,9 @@ TEST_CASE("generalized scalar udf") { } #endif SECTION("specialized template function") { +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 12) + SKIP("GCC < 12 cannot use std::clamp specialization as NTTP."); +#else constexpr auto clamp_int_f = "clamp_int"_scalar.quote(std::clamp); storage.create_scalar_function(); { @@ -667,8 +674,12 @@ TEST_CASE("generalized scalar udf") { REQUIRE(rows == expected); } storage.delete_scalar_function(); +#endif } SECTION("overloaded template function") { +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 12) + SKIP("GCC < 12 cannot use std::clamp overload as NTTP."); +#else constexpr auto clamp_int_f = "clamp_int"_scalar.quote(std::clamp); storage.create_scalar_function(); @@ -678,6 +689,7 @@ TEST_CASE("generalized scalar udf") { REQUIRE(rows == expected); } storage.delete_scalar_function(); +#endif } SECTION("non-copyable function object") { // note: unlike msvc, gcc+clang require a constant template parameter to be copyable (and probably rightly so); @@ -703,6 +715,9 @@ TEST_CASE("generalized scalar udf") { storage.delete_scalar_function(); } SECTION("escaped function identifier") { +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 12) + SKIP("GCC < 12 cannot use std::clamp specialization as NTTP."); +#else constexpr auto clamp_f = R"("clamp int")"_scalar.quote(std::clamp); storage.create_scalar_function(); { @@ -711,6 +726,7 @@ TEST_CASE("generalized scalar udf") { REQUIRE(rows == expected); } storage.delete_scalar_function(); +#endif } } #endif