From 133bdc5caa26c9fc9f1726a350881db47784f8bb Mon Sep 17 00:00:00 2001 From: ClausKlein Date: Sun, 20 Apr 2025 19:52:35 +0200 Subject: [PATCH 1/4] Add tuple examples --- CMakeLists.txt | 35 ++++----- cmake/WarningsAsErrors.cmake | 2 +- example/CMakeLists.txt | 1 + example/tuple.cpp | 144 +++++++++++++++++++++++++++++++++++ include/boost/ut.hpp | 2 +- 5 files changed, 162 insertions(+), 22 deletions(-) create mode 100644 example/tuple.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f60c439..e28e8fd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,12 +7,13 @@ option(BOOST_UT_DISABLE_MODULE "Disable ut module" ON) if(NOT BOOST_UT_DISABLE_MODULE) -cmake_minimum_required(VERSION 4.0.0) -set(CMAKE_CXX_STANDARD 23) -set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "a9e1cf81-9932-4810-974b-6eccaf14e457") -set(CMAKE_CXX_MODULE_STD 1) + cmake_minimum_required(VERSION 4.0.0) + set(CMAKE_CXX_STANDARD 23) + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "a9e1cf81-9932-4810-974b-6eccaf14e457") + set(CMAKE_CXX_MODULE_STD 1) + set(CMAKE_CXX_SCAN_FOR_MODULES 1) else() -cmake_minimum_required(VERSION 3.21...3.25) + cmake_minimum_required(VERSION 3.21...4.0) endif() project( ut @@ -40,12 +41,12 @@ option(BOOST_UT_USE_WARNINGS_AS_ERORS "Build the tests" ${PROJECT_IS_TOP_LEVEL}) add_library(ut INTERFACE) if(NOT BOOST_UT_DISABLE_MODULE) -add_library(ut_module) + add_library(ut_module) endif() target_include_directories(ut INTERFACE $) target_compile_features(ut INTERFACE cxx_std_20) if(NOT BOOST_UT_DISABLE_MODULE) -target_compile_features(ut_module INTERFACE cxx_std_23) + target_compile_features(ut_module INTERFACE cxx_std_23) endif() if(BOOST_UT_USE_WARNINGS_AS_ERORS) @@ -77,7 +78,7 @@ if(BOOST_UT_DISABLE_MODULE) endif() if(NOT BOOST_UT_DISABLE_MODULE) -target_sources(ut_module PUBLIC FILE_SET CXX_MODULES FILES include/boost/ut.cppm) + target_sources(ut_module PUBLIC FILE_SET CXX_MODULES FILES include/boost/ut.cppm) endif() if(NOT TARGET Boost::ut) @@ -85,21 +86,19 @@ if(NOT TARGET Boost::ut) endif() if(NOT BOOST_UT_DISABLE_MODULE) -add_library(Boost::ut_module ALIAS ut_module) + add_library(Boost::ut_module ALIAS ut_module) endif() include(GNUInstallDirs) include(CMakePackageConfigHelpers) -install( - FILES include/boost/ut.hpp include/boost/ut.cppm - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/boost -) +install(FILES include/boost/ut.hpp include/boost/ut.cppm DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/boost) install( TARGETS ut EXPORT ut_Targets - INCLUDES DESTINATION include + INCLUDES + DESTINATION include ) install( EXPORT ut_Targets @@ -111,13 +110,9 @@ install( write_basic_package_version_file( "utConfigVersion.cmake" VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion - ARCH_INDEPENDENT -) -install( - FILES "${PROJECT_BINARY_DIR}/utConfigVersion.cmake" - DESTINATION lib/cmake/ut + COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT ) +install(FILES "${PROJECT_BINARY_DIR}/utConfigVersion.cmake" DESTINATION lib/cmake/ut) if(EMSCRIPTEN) set(CMAKE_EXECUTABLE_SUFFIX ".js") diff --git a/cmake/WarningsAsErrors.cmake b/cmake/WarningsAsErrors.cmake index c277462b..ee0d7a32 100644 --- a/cmake/WarningsAsErrors.cmake +++ b/cmake/WarningsAsErrors.cmake @@ -1,7 +1,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_SIMULATE_ID MATCHES "MSVC") add_compile_options(/W4 /WX) elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - add_compile_options(-Wall -Wextra -Wpedantic -Werror -Wshadow) + add_compile_options(-Wall -Wextra -Wpedantic -Werror -Wshadow -Wextra-semi) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 10.0) add_compile_options(-Wdeprecated-copy-dtor -Wnewline-eof) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index db1b067e..6b720c47 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -89,4 +89,5 @@ example(tag tag) example(terse terse) example(test _test) example(tmp tmp) +example(tuple tuple) example(using using) diff --git a/example/tuple.cpp b/example/tuple.cpp new file mode 100644 index 00000000..d0d0a818 --- /dev/null +++ b/example/tuple.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +std::optional> findUserById(int id) { + if (id == 42) { + return std::make_tuple(42, "Alice"); + } else { + return std::nullopt; + } +} + +std::tuple get_student(int id) { + switch (id) { + case 0: + return {3.8, 'A', "Lisa Simpson"}; + case 1: + return {2.9, 'C', "Milhouse Van Houten"}; + case 2: + return {1.7, 'D', "Ralph Wiggum"}; + case 3: + return {0.6, 'F', "Bart Simpson"}; + } + + throw std::invalid_argument("id"); +} + +struct S { + int n; + std::string s; + float d; + + friend bool operator<(const S& lhs, const S& rhs) noexcept { + // compares lhs.n to rhs.n, + // then lhs.s to rhs.s, + // then lhs.d to rhs.d + // in that order, first non-equal result is returned + // or false if all elements are equal + return std::tie(lhs.n, lhs.s, lhs.d) < std::tie(rhs.n, rhs.s, rhs.d); + } +}; + +std::tuple divide(int a, int b) { + return std::make_tuple(a / b, a % b); // quotient and remainder +} + +} // namespace + +int main() { + using namespace boost::ut; + + const auto student0 = get_student(0); + std::cout << "ID: 0, " + << "GPA: " << std::get<0>(student0) << ", " + << "grade: " << std::get<1>(student0) << ", " + << "name: " << std::get<2>(student0) << '\n'; + expect(std::tuple_size::value == 3); + expect(std::get<2>(student0) == "Lisa Simpson"); + + const auto student1 = get_student(1); + std::cout << "ID: 1, " + << "GPA: " << std::get(student1) << ", " + << "grade: " << std::get(student1) << ", " + << "name: " << std::get(student1) << '\n'; + expect(std::get<2>(student1) == "Milhouse Van Houten"); + + double gpa2{}; + char grade2{}; + std::string name2; + std::tie(gpa2, grade2, name2) = get_student(2); + std::cout << "ID: 2, " + << "GPA: " << gpa2 << ", " + << "grade: " << grade2 << ", " + << "name: " << name2 << '\n'; + expect(get_student(2) == + std::tuple{1.7, 'D', "Ralph Wiggum"}); + + // C++17 structured binding: + const auto [gpa3, grade3, name3] = get_student(3); + std::cout << "ID: 3, " + << "GPA: " << gpa3 << ", " + << "grade: " << grade3 << ", " + << "name: " << name3 << '\n'; + expect(get_student(3) == + std::tuple{0.6, 'F', "Bart Simpson"}); + + // Lexicographical comparison demo: + std::set set_of_s; + + S value{42, "Test", 3.14}; + std::set::iterator iter; + bool is_inserted{}; + + // Unpack a pair: + std::tie(iter, is_inserted) = set_of_s.insert(value); + expect(is_inserted); + + // std::tie and structured bindings: + auto position = [](int w) { return std::tuple(1 * w, 2 * w); }; + + auto [x, y] = position(1); + expect(x == 1 && y == 2); + std::tie(x, y) = position(2); // reuse x, y with tie + expect(x == 2 && y == 4); + + // Implicit conversions are permitted: + std::tuple coordinates(6, 9); + std::tie(x, y) = coordinates; + expect(x == 6 && y == 9); + + // Skip an element: + std::string z; + std::tie(x, std::ignore, z) = std::tuple(1, 2.0, "Test"); + expect(x == 1 && z == "Test"); + + // Multiple Return Values + auto [quotient, remainder] = divide(10, 3); + expect(quotient == 3); + expect(remainder == 1); + + expect(std::tuple(2, 1) == divide(5, 2)); + + std::atomic test_value{false}; + expect(!test_value); + + { + auto result = findUserById(42); + //FIXME: expect(result); + if (result) { + // To unpack, use *result or result.value(). + auto [userId, username] = *result; // unpack the tuple + std::cout << "Found user: " << userId << ", " << username << "\n"; + } else { + std::cout << "User not found.\n"; + } + expect(result != std::nullopt); + } +} diff --git a/include/boost/ut.hpp b/include/boost/ut.hpp index b39e964d..ca370fe1 100644 --- a/include/boost/ut.hpp +++ b/include/boost/ut.hpp @@ -2626,7 +2626,7 @@ constexpr auto operator""_b(const char* name, decltype(sizeof("")) size) { return "rd"; } return "th"; -}; +} template inline std::string format_test_parameter([[maybe_unused]] const TArg& arg, From b01721a40de33a202c915d4c2174b41977c997ad Mon Sep 17 00:00:00 2001 From: ClausKlein Date: Sun, 20 Apr 2025 20:29:02 +0200 Subject: [PATCH 2/4] Prevent compiler warning with MSVC --- example/tuple.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/tuple.cpp b/example/tuple.cpp index d0d0a818..f364edd5 100644 --- a/example/tuple.cpp +++ b/example/tuple.cpp @@ -34,7 +34,7 @@ std::tuple get_student(int id) { struct S { int n; std::string s; - float d; + double d; friend bool operator<(const S& lhs, const S& rhs) noexcept { // compares lhs.n to rhs.n, From 3e2fb672be3c6a2852c4c569a766dbb252cc158e Mon Sep 17 00:00:00 2001 From: ClausKlein Date: Sun, 20 Apr 2025 20:42:26 +0200 Subject: [PATCH 3/4] Prevent warning C4244 --- example/tuple.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/example/tuple.cpp b/example/tuple.cpp index f364edd5..f44b1704 100644 --- a/example/tuple.cpp +++ b/example/tuple.cpp @@ -109,10 +109,11 @@ int main() { std::tie(x, y) = position(2); // reuse x, y with tie expect(x == 2 && y == 4); + // Prevent warning C4244: 'initializing': conversion from '_Ty' to '_Ty', possible loss of data // Implicit conversions are permitted: - std::tuple coordinates(6, 9); - std::tie(x, y) = coordinates; - expect(x == 6 && y == 9); + // std::tuple coordinates(6, 9); + // std::tie(x, y) = coordinates; + // expect(x == 6 && y == 9); // Skip an element: std::string z; From f7194e583556a5e9c1b3aa407a403a4bc1863de7 Mon Sep 17 00:00:00 2001 From: ClausKlein Date: Mon, 21 Apr 2025 21:25:39 +0200 Subject: [PATCH 4/4] Add test for std::optional::operator bool --- example/tuple.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/example/tuple.cpp b/example/tuple.cpp index f44b1704..48639f64 100644 --- a/example/tuple.cpp +++ b/example/tuple.cpp @@ -7,6 +7,36 @@ #include #include +void test_optional() { + using namespace boost::ut; + + std::cout << std::boolalpha; + + std::optional opt; + std::cout << opt.has_value() << '\n'; + expect(!opt); + + opt = 43; + // Checks whether *this contains a value. + if (opt) { + std::cout << "value set to " << opt.value() << '\n'; + } else { + std::cout << "value not set\n"; + } + expect(opt.has_value()); + expect(opt == 43); + expect(*opt); + + opt.reset(); + if (opt.has_value()) { + std::cout << "value still set to " << opt.value() << '\n'; + } else { + std::cout << "value no longer set\n"; + } + expect(!opt.has_value()); + expect(!opt); +} + namespace { std::optional> findUserById(int id) { if (id == 42) { @@ -130,6 +160,7 @@ int main() { std::atomic test_value{false}; expect(!test_value); + // test std::optional> { auto result = findUserById(42); //FIXME: expect(result); @@ -142,4 +173,6 @@ int main() { } expect(result != std::nullopt); } + + test_optional(); }