From e84736668bbfc2df49a102240869b40f7a02207c Mon Sep 17 00:00:00 2001 From: "Peter G. Jensen" Date: Thu, 28 Dec 2023 11:25:44 +0100 Subject: [PATCH 01/17] corrected version-check --- src/SimpleTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SimpleTree.cpp b/src/SimpleTree.cpp index b30ce34..35345f5 100644 --- a/src/SimpleTree.cpp +++ b/src/SimpleTree.cpp @@ -51,7 +51,7 @@ SimpleTree SimpleTree::parse(std::istream& input, bool simplify, bool subsumptio if(!raw.is_object()) throw base_error("Input JSON not well formatted"); - if(!raw["version"].is_number_float() || raw["version"].get() != 1.0) + if(!raw["version"].is_number() || raw["version"].get() != 1.0) throw base_error("Input version not supported"); if(!raw["type"].is_string() || raw["type"].get() != "state->regressor") From 5e0266c5be1c66c5c1801c5a9b146cdecb971af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Mon, 11 May 2026 11:37:54 +0200 Subject: [PATCH 02/17] updated build scripts --- CMakePresets.json | 94 +++++++++++++++++++++++++++++++++++++++------- README.md | 6 +-- src/SimpleTree.cpp | 2 +- 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 0dd7936..0e20268 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -8,47 +8,115 @@ "include": ["cmake/CommonPresets.json"], "configurePresets": [ { - "name": "libonly", + "name": "quick", "inherits": "multi", - "binaryDir": "${sourceDir}/build-libonly", + "binaryDir": "${sourceDir}/build-quick", "cacheVariables": { "LIBSTRATEGY_TESTS": "OFF" , "LIBSTRATEGY_OnlyLibrary": "ON" }, - "displayName": "Configure Library-Only for Debug and Release" + "displayName": "Configure quick build (no utilities, no testing) for Debug and Release" + }, + { + "name": "quick-san", + "inherits": "multi-san", + "binaryDir": "${sourceDir}/build-quick-san", + "cacheVariables": { + "LIBSTRATEGY_TESTS": "OFF" , + "LIBSTRATEGY_OnlyLibrary": "ON" + }, + "displayName": "Configure quick build (no utilities, no testing) with Sanitizers for Debug and Release" } ], "buildPresets": [ { - "name": "libonly-release", + "name": "quick-release", "inherits": "release", - "configurePreset": "libonly", + "configurePreset": "quick", + "displayName": "Build Library-Only for Release" + }, + { + "name": "quick-debug", + "inherits": "debug", + "configurePreset": "quick", "displayName": "Build Library-Only for Debug" }, { - "name": "libonly-debug", + "name": "quick-san-release", + "inherits": "release", + "configurePreset": "quick-san", + "displayName": "Build Library-Only with Sanitizers for Release" + }, + { + "name": "quick-san-debug", "inherits": "debug", - "configurePreset": "libonly", - "displayName": "Build Library-Only for Release" + "configurePreset": "quick-san", + "displayName": "Build Library-Only with Sanitizers for Debug" } ], "testPresets": [], "workflowPresets": [ { - "name": "libonly", - "displayName": "Configure and Build Library-Only for Debug and Release (no testing)", + "name": "quick", + "displayName": "Configure and Build Library-Only (no testing) for Debug and Release", "steps": [ { "type": "configure", - "name": "libonly" + "name": "quick" + }, + { + "type": "build", + "name": "quick-release" }, { "type": "build", - "name": "libonly-release" + "name": "quick-debug" + } + ] + }, + { + "name": "quick-release", + "displayName": "Configure and Build Library-Only (no testing) for Release", + "steps": [ + { + "type": "configure", + "name": "quick" + }, + { + "type": "build", + "name": "quick-release" + } + ] + }, + { + "name": "quick-san", + "displayName": "Configure and Build Library-Only (no testing) with Sanitizers for Debug and Release", + "steps": [ + { + "type": "configure", + "name": "quick-san" + }, + { + "type": "build", + "name": "quick-san-release" + }, + { + "type": "build", + "name": "quick-san-debug" + } + ] + }, + { + "name": "quick-san-release", + "displayName": "Configure and Build Library-Only (no testing) for Release", + "steps": [ + { + "type": "configure", + "name": "quick-san" }, { "type": "build", - "name": "libonly-debug" + "name": "quick-san-release" } ] } diff --git a/README.md b/README.md index 2d2eed3..fdecd35 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,13 @@ brew install cmake ninja boost ``` ## Compile and Install -Run minimal compilation (just the library) with `Debug` and `Release` settings into `build-libonly/lib`: +Run minimal compilation (just the library) with `Debug` and `Release` settings into `build-quick/lib`: ```shell -cmake --workflow libonly +cmake --workflow --preset quick-release ``` Install the `Release` build of `build-libonly` into `$PWD/local` path: ```shell -cmake --install build-libonly --config Release --prefix $PWD/local +cmake --install build-quick --config Release --prefix $PWD/local ``` ## Other Presets diff --git a/src/SimpleTree.cpp b/src/SimpleTree.cpp index 12b18e9..5d4b86f 100644 --- a/src/SimpleTree.cpp +++ b/src/SimpleTree.cpp @@ -703,7 +703,7 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act consistent(key.size() + 1); } -void SimpleTree::node_t::consistent(size_t prefix) const +void SimpleTree::node_t::consistent(size_t prefix [[maybe_unused]]) const { return; #ifndef NDEBUG From ef835a6b16dd44da8f97d078ab432bcbb2f515dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Mon, 11 May 2026 11:39:55 +0200 Subject: [PATCH 03/17] Fixed cmake build scripts to fetch latest boost --- CMakeLists.txt | 15 ++++++--------- cmake/boost.cmake | 36 ++++++++++++++++++++++++++++++++++++ cmake/nlohmann_json.cmake | 1 + cmake/ptrie.cmake | 1 + src/CMakeLists.txt | 11 +++-------- test/CMakeLists.txt | 1 - 6 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 cmake/boost.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 67c4927..f14932e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,7 @@ cmake_minimum_required(VERSION 3.28) +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.30.0") + cmake_policy(SET CMP0167 NEW) # use FindBoost from boost installation (as opposed to CMake own) +endif () project(libstrategy VERSION 1.0.9 LANGUAGES CXX C) set(CMAKE_CXX_STANDARD 17) @@ -6,26 +9,20 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) include(cmake/warnings.cmake) include(cmake/sanitizers.cmake) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - option(LIBSTRATEGY_TESTS "libstrategy Unit Tests" ON) option(LIBSTRATEGY_OnlyLibrary "Build only the library (no binary utilities)" OFF) -include(cmake/ptrie.cmake) +include(cmake/boost.cmake) include(cmake/nlohmann_json.cmake) +include(cmake/ptrie.cmake) if(NOT LIBSTRATEGY_OnlyLibrary) cmake_policy(SET CMP0069 NEW) # INTERPROCEDURAL_OPTIMIZATIONS flags - if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.30.0") - cmake_policy(SET CMP0167 NEW) # use FindBoost from boost installation (as opposed to CMake own) - endif () - find_package(Boost 1.83 COMPONENTS program_options REQUIRED) endif() add_subdirectory(src) diff --git a/cmake/boost.cmake b/cmake/boost.cmake new file mode 100644 index 0000000..b492435 --- /dev/null +++ b/cmake/boost.cmake @@ -0,0 +1,36 @@ +#set(FETCHCONTENT_QUIET ON) +#set(FETCHCONTENT_UPDATES_DISCONNECTED ON) +include(FetchContent) +FetchContent_Declare(Boost + # URL https://github.com/boostorg/boost/releases/download/boost-1.91.0-1/boost-1.91.0-1-cmake.7z + URL https://people.cs.aau.dk/~marius/mirrors/boost/boost-1.91.0-1-cmake.7z + URL_HASH SHA256=29c7d4f4ac36ad853b6765d03571ea60d90286775df026b4efd9f3281131972b + #GIT_REPOSITORY https://github.com/boostorg/boost.git + #GIT_TAG boost-1.91.0-1 + #GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) + #GIT_PROGRESS TRUE # show download progress in Ninja + USES_TERMINAL_DOWNLOAD TRUE + EXCLUDE_FROM_ALL # don't build if not used + FIND_PACKAGE_ARGS 1.91.0) + +set(BOOST_ENABLE_MPI OFF CACHE BOOL "Boost.MPI and its dependents (requires MPI, CMake 3.10)") +set(BOOST_ENABLE_PYTHON OFF CACHE BOOL "Boost.Python and its dependents (requires Python, CMake 3.14)") +set(BUILD_TESTING OFF CACHE BOOL "Build the tests.") +set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries") + + +FetchContent_MakeAvailable(Boost) + +if (Boost_FOUND) # find_package + message(STATUS "Found Boost: ${Boost_INCLUDE_DIRS}") +else (Boost_program_options_FOUND) # fetch_content + message(STATUS "Got Boost: ${Boost_SOURCE_DIR}") +endif (Boost_FOUND) + +if (TARGET Boost::program_options) + message(STATUS " Available target: Boost::program_options") +endif () +if (TARGET Boost::unit_test_framework) + message(STATUS " Available target: Boost::unit_test_framework") +endif () + diff --git a/cmake/nlohmann_json.cmake b/cmake/nlohmann_json.cmake index cd031cb..9e7346f 100644 --- a/cmake/nlohmann_json.cmake +++ b/cmake/nlohmann_json.cmake @@ -9,6 +9,7 @@ FetchContent_Declare(nlohmann_json GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) GIT_PROGRESS TRUE # show download progress in Ninja USES_TERMINAL_DOWNLOAD TRUE + EXCLUDE_FROM_ALL # don't build if not used FIND_PACKAGE_ARGS 3.12.0) set(NLOHMANN_JSON_BUILD_MODULES OFF CACHE BOOL "Build C++ modules support") diff --git a/cmake/ptrie.cmake b/cmake/ptrie.cmake index 854633a..079f191 100644 --- a/cmake/ptrie.cmake +++ b/cmake/ptrie.cmake @@ -7,6 +7,7 @@ FetchContent_Declare(ptrie GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) GIT_PROGRESS TRUE # show download progress in Ninja USES_TERMINAL_DOWNLOAD TRUE + EXCLUDE_FROM_ALL # don't build if not used FIND_PACKAGE_ARGS 1.1.2) set(PTRIE_BuildTests OFF CACHE BOOL "Build the unit tests when BUILD_TESTING is enabled.") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a08a070..67f30a3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(SimpleTree OBJECT SimpleTree.cpp) -target_link_libraries(SimpleTree PUBLIC ptrie::ptrie nlohmann_json::nlohmann_json) +target_link_libraries(SimpleTree PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) add_library(strategy SHARED libz2s.cpp ZonotopStrategy.cpp) target_link_libraries(strategy PUBLIC SimpleTree) @@ -13,10 +13,5 @@ if(NOT LIBSTRATEGY_OnlyLibrary) target_link_libraries(z2s PUBLIC SimpleTree Boost::program_options) endif() -install(TARGETS strategy strategyStatic - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) -install (FILES errors.h SimpleTree.h ZonotopStrategy.h DESTINATION include/libstrategy) - - +install(TARGETS strategy strategyStatic) +install(FILES errors.h SimpleTree.h ZonotopStrategy.h DESTINATION include/libstrategy) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 620da41..5f11c4b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,7 +4,6 @@ include_directories (${CMAKE_SOURCE_DIR}/src) add_executable (utilities_test utilities_test.cpp) add_test(NAME utilities_test COMMAND utilities_test) - add_executable (SimpleTree_test SimpleTree_test.cpp) target_link_libraries(SimpleTree_test PRIVATE SimpleTree Boost::unit_test_framework) add_test(NAME SimpleTree_test COMMAND SimpleTree_test) From ec6d69cbad95a9c931d0c2ccd3ce113f43ac7663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Mon, 11 May 2026 14:15:42 +0200 Subject: [PATCH 04/17] Fixed CMake installation script --- CMakeLists.txt | 1 - README.md | 8 ++++--- src/CMakeLists.txt | 38 ++++++++++++++++++++++++++++------ src/libstrategyConfig.cmake.in | 3 +++ 4 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 src/libstrategyConfig.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index f14932e..c8035cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) include(cmake/warnings.cmake) include(cmake/sanitizers.cmake) diff --git a/README.md b/README.md index fdecd35..4983604 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,14 @@ Full project requires: For Ubuntu 24.04 install build tools and library dependencies: ```shell -sudo apt install cmake ninja-build g++ libboost-program-options-dev libboost-test-dev +sudo apt install cmake ninja-build g++ +sudo apt install libboost-program-options-dev libboost-test-dev # optional ``` For macOS install build tools and library dependencies: ```shell -brew install cmake ninja boost +brew install cmake ninja gcc +brew install boost # optional ``` ## Compile and Install @@ -39,7 +41,7 @@ cmake --workflow --list-presets For example, configure, build and **test** for Development with **Sanitizers** (GCC/Clang/AppleClang): ```shell -cmake --workflow debug-san +cmake --workflow --preset debug-san ``` Other configuration presets: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 67f30a3..64fdd4f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,17 +1,43 @@ add_library(SimpleTree OBJECT SimpleTree.cpp) target_link_libraries(SimpleTree PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) +add_library(libstrategy::SimpleTree ALIAS SimpleTree) -add_library(strategy SHARED libz2s.cpp ZonotopStrategy.cpp) -target_link_libraries(strategy PUBLIC SimpleTree) +add_library(strategy SHARED libz2s.cpp ZonotopStrategy.cpp $) +target_link_libraries(strategy PUBLIC ptrie::ptrie) +set_target_properties(strategy PROPERTIES VISIBILITY_INLINE_HIDDEN ON) +add_library(libstrategy::strategy ALIAS strategy) -add_library(strategyStatic STATIC libz2s.cpp ZonotopStrategy.cpp) -target_link_libraries(strategyStatic PUBLIC SimpleTree) +add_library(strategyStatic STATIC libz2s.cpp ZonotopStrategy.cpp $) +target_link_libraries(strategyStatic PUBLIC ptrie::ptrie) set_target_properties(strategyStatic PROPERTIES OUTPUT_NAME strategy) +add_library(libstrategy::strategyStatic ALIAS strategyStatic) if(NOT LIBSTRATEGY_OnlyLibrary) add_executable(z2s main.cpp ZonotopStrategy.cpp) target_link_libraries(z2s PUBLIC SimpleTree Boost::program_options) endif() -install(TARGETS strategy strategyStatic) -install(FILES errors.h SimpleTree.h ZonotopStrategy.h DESTINATION include/libstrategy) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +set(CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/libstrategy) + +configure_package_config_file(libstrategyConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/libstrategyConfig.cmake + INSTALL_DESTINATION ${CONFIG_INSTALL_DIR}) +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/libstrategyConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) + +install(TARGETS strategy strategyStatic + EXPORT libstrategyTargets) +install(FILES errors.h SimpleTree.h ZonotopStrategy.h + DESTINATION include/libstrategy) +install(EXPORT libstrategyTargets + FILE libstrategyTargets.cmake + NAMESPACE libstrategy:: + DESTINATION ${CONFIG_INSTALL_DIR}) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/libstrategyConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libstrategyConfigVersion.cmake + DESTINATION ${CONFIG_INSTALL_DIR}) diff --git a/src/libstrategyConfig.cmake.in b/src/libstrategyConfig.cmake.in new file mode 100644 index 0000000..2d161b2 --- /dev/null +++ b/src/libstrategyConfig.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/libstrategyTargets.cmake") +check_required_components(libstrategy) From e288f7367f506d89405cbf9e81dfd10ba1e77cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Mon, 11 May 2026 15:08:38 +0200 Subject: [PATCH 05/17] Fixed library build scripts, added usage example --- CMakeLists.txt | 14 ++++++++------ README.md | 12 ++++++++++++ cmake/libstrategy.cmake | 26 ++++++++++++++++++++++++++ cmake/nlohmann_json.cmake | 4 +--- cmake/ptrie.cmake | 5 ++--- example/CMakeLists.txt | 10 ++++++++++ example/example.cpp | 18 ++++++++++++++++++ test/inconsistent_lookup.cpp | 17 ++++++++--------- 8 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 cmake/libstrategy.cmake create mode 100644 example/CMakeLists.txt create mode 100644 example/example.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c8035cd..d635938 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,15 +10,17 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -include(cmake/warnings.cmake) -include(cmake/sanitizers.cmake) - option(LIBSTRATEGY_TESTS "libstrategy Unit Tests" ON) option(LIBSTRATEGY_OnlyLibrary "Build only the library (no binary utilities)" OFF) -include(cmake/boost.cmake) -include(cmake/nlohmann_json.cmake) -include(cmake/ptrie.cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +include(warnings) +include(sanitizers) +include(nlohmann_json) +include(ptrie) +if (LIBSTRATEGY_TESTS OR NOT LIBSTRATEGY_OnlyLibrary) + include(boost) +endif() if(NOT LIBSTRATEGY_OnlyLibrary) cmake_policy(SET CMP0069 NEW) # INTERPROCEDURAL_OPTIMIZATIONS flags diff --git a/README.md b/README.md index 4983604..e34e5f2 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,18 @@ Install the `Release` build of `build-libonly` into `$PWD/local` path: cmake --install build-quick --config Release --prefix $PWD/local ``` +## Example Usage +Directory [example](example) contains minimal example demonstrating how to use the library. + +Test the example with libstrategy installed into [local](local) directory: +```shell +cmake -S example -B build-example -DCMAKE_PREFIX_PATH=$PWD/local -DCMAKE_MODULE_PATH=$PWD/cmake +``` +```shell +cmake --build build-example +ctest --test-dit build-example --output-on-failure +``` + ## Other Presets Inspect workflow presets: ```shell diff --git a/cmake/libstrategy.cmake b/cmake/libstrategy.cmake new file mode 100644 index 0000000..0b4d821 --- /dev/null +++ b/cmake/libstrategy.cmake @@ -0,0 +1,26 @@ +include(FetchContent) +FetchContent_Declare(libstrategy + GIT_REPOSITORY https://github.com/DEIS-Tools/libstrategy + GIT_TAG fixing_version_check + GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) + GIT_PROGRESS TRUE # show download progress in Ninja + USES_TERMINAL_DOWNLOAD TRUE + EXCLUDE_FROM_ALL # don't build if not used + FIND_PACKAGE_ARGS 1.1.2) + +set(LIBSTRATEGY_TESTS OFF CACHE BOOL "libstrategy Unit Tests") +set(LIBSTRATEGY_OnlyLibrary ON CACHE BOOL "Build only the library (no binary utilities)") +FetchContent_MakeAvailable(libstrategy) + +if (libstrategy_FOUND) # find_package + message(STATUS "Found libstrategy: ${libstrategy_DIR}") +else (libstrategy_FOUND) # fetch_content + message(STATUS "Fetched libstrategy: ${libstrategy_SOURCE_DIR}") +endif (libstrategy_FOUND) + +if (TARGET libstrategy::strategy) + message(STATUS " Available target: libstrategy::strategy") +endif () +if (TARGET libstrategy::strategyStatic) + message(STATUS " Available target: libstrategy::strategyStatic") +endif () diff --git a/cmake/nlohmann_json.cmake b/cmake/nlohmann_json.cmake index 9e7346f..35f27b8 100644 --- a/cmake/nlohmann_json.cmake +++ b/cmake/nlohmann_json.cmake @@ -26,11 +26,9 @@ set(JSON_MultipleHeaders ON CACHE BOOL "Use non-amalgamated version set(JSON_SystemInclude OFF CACHE BOOL "Include as system headers (skip for clang-tidy).") FetchContent_MakeAvailable(nlohmann_json) -message(STATUS "Got nlohmann_json: ${nlohmann_json_SOURCE_DIR}") if (nlohmann_json_FOUND) # find_package - get_target_property(nlohmann_json_INCLUDE_DIRS nlohmann_json::nlohmann_json INTERFACE_INCLUDE_DIRECTORIES) - message(STATUS "Found nlohmann_json: ${nlohmann_json_INCLUDE_DIRS}") + message(STATUS "Found nlohmann_json: ${nlohmann_json_DIRS}") else (nlohmann_json_FOUND) # FetchContent message(STATUS "Fetched nlohmann_json: ${nlohmann_json_SOURCE_DIR}") endif (nlohmann_json_FOUND) diff --git a/cmake/ptrie.cmake b/cmake/ptrie.cmake index 079f191..4e1b5fe 100644 --- a/cmake/ptrie.cmake +++ b/cmake/ptrie.cmake @@ -15,10 +15,9 @@ set(PTRIE_BuildBenchmark OFF CACHE BOOL "Build the simple benchmark suite") FetchContent_MakeAvailable(ptrie) if (ptrie_FOUND) # find_package - get_target_property(ptrie_INCLUDE_DIRS ptrie::ptrie INTERFACE_INCLUDE_DIRECTORIES) - message(STATUS "Found ptrie: ${ptrie_INCLUDE_DIRS}") + message(STATUS "Found ptrie: ${ptrie_DIR}") else (ptrie_FOUND) # fetch_content - message(STATUS "Got ptrie: ${ptrie_SOURCE_DIR}") + message(STATUS "Fetched ptrie: ${ptrie_SOURCE_DIR}") # Workaround until ptrie exports proper cmake config: add_library(ptrie::ptrie INTERFACE IMPORTED GLOBAL) target_include_directories(ptrie::ptrie INTERFACE ${ptrie_SOURCE_DIR}/src) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..7ab20f8 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.28) +project(example VERSION 1.0.9) + +include(nlohmann_json) +include(ptrie) +include(libstrategy) + +add_executable(example example.cpp) +target_link_libraries(example PRIVATE libstrategy::strategy) +add_test(NAME example COMMAND example) diff --git a/example/example.cpp b/example/example.cpp new file mode 100644 index 0000000..6240dcf --- /dev/null +++ b/example/example.cpp @@ -0,0 +1,18 @@ +#include + +#include +#include + +int main(int argc, const char* argv[]) +{ + if (argc < 2) { + std::cout << "Expecting a path to a strategy as an argument\n"; + return 1; + } + for (auto i = 1; i < argc; ++i) { + auto strategy_path = argv[i]; + auto is = std::ifstream{strategy_path}; + auto tree = SimpleTree::parse(is, true, false); + tree.print(std::cout); + } +} diff --git a/test/inconsistent_lookup.cpp b/test/inconsistent_lookup.cpp index bd20e7a..d276b7c 100644 --- a/test/inconsistent_lookup.cpp +++ b/test/inconsistent_lookup.cpp @@ -21,14 +21,17 @@ #include #include +#include + +namespace fs = std::filesystem; BOOST_AUTO_TEST_CASE(DirectoryTest) { BOOST_REQUIRE(getenv("STRATEGY_DIR")); } +const auto strategy_path = fs::path{getenv("STRATEGY_DIR")} / "inconsistent1.strategy"; + BOOST_AUTO_TEST_CASE(Inconsistent1) { - std::string strategy = getenv("STRATEGY_DIR"); - strategy += "/inconsistent1.strategy"; - std::ifstream in(strategy); + auto in = std::ifstream{strategy_path}; auto tree = SimpleTree::parse(in, false, false); double vars[] = {10}; auto act18 = tree.value(vars, nullptr, 0); @@ -38,9 +41,7 @@ BOOST_AUTO_TEST_CASE(Inconsistent1) BOOST_AUTO_TEST_CASE(Inconsistent1Simplify) { - std::string strategy = getenv("STRATEGY_DIR"); - strategy += "/inconsistent1.strategy"; - std::ifstream in(strategy); + auto in = std::ifstream{strategy_path}; auto tree = SimpleTree::parse(in, true, false); double vars[] = {10}; BOOST_REQUIRE_LT(tree.value(vars, nullptr, 0), tree.value(vars, nullptr, 1)); @@ -48,9 +49,7 @@ BOOST_AUTO_TEST_CASE(Inconsistent1Simplify) BOOST_AUTO_TEST_CASE(Inconsistent1SimplifySubsumption) { - std::string strategy = getenv("STRATEGY_DIR"); - strategy += "/inconsistent1.strategy"; - std::ifstream in(strategy); + auto in = std::ifstream{strategy_path}; auto tree = SimpleTree::parse(in, true, true); double vars[] = {10}; BOOST_REQUIRE_LT(tree.value(vars, nullptr, 0), tree.value(vars, nullptr, 1)); From ff83ab64fc9ef92194a5daf4fe7ffedadb225086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Tue, 30 Jun 2026 11:21:44 +0000 Subject: [PATCH 06/17] Fixed missing nlohmann_json include path for strategy and strategyStatic targets $ only contributes compiled objects, not transitive include directories, so strategy/strategyStatic could not find nlohmann/json_fwd.hpp. Since nlohmann_json is header-only, pull in its include dirs directly via target_include_directories to avoid creating an unexportable link dependency. Co-Authored-By: Claude Sonnet 4.6 --- src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 64fdd4f..f8cfc4d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,11 +4,13 @@ add_library(libstrategy::SimpleTree ALIAS SimpleTree) add_library(strategy SHARED libz2s.cpp ZonotopStrategy.cpp $) target_link_libraries(strategy PUBLIC ptrie::ptrie) +target_include_directories(strategy PRIVATE $) set_target_properties(strategy PROPERTIES VISIBILITY_INLINE_HIDDEN ON) add_library(libstrategy::strategy ALIAS strategy) add_library(strategyStatic STATIC libz2s.cpp ZonotopStrategy.cpp $) target_link_libraries(strategyStatic PUBLIC ptrie::ptrie) +target_include_directories(strategyStatic PRIVATE $) set_target_properties(strategyStatic PROPERTIES OUTPUT_NAME strategy) add_library(libstrategy::strategyStatic ALIAS strategyStatic) From 714f3395396bb429e85bacbd3f11e741b14890ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Tue, 30 Jun 2026 11:32:55 +0000 Subject: [PATCH 07/17] Fixed README inaccuracies and example ctest registration - Corrected --test-dit typo to --test-dir in ctest command - Fixed stale build-libonly reference in prose (renamed to build-quick) - Corrected description: quick-release builds Release only, not Debug+Release - Added enable_testing() to example/CMakeLists.txt so ctest can discover tests Co-Authored-By: Claude Sonnet 4.6 --- README.md | 6 +++--- example/CMakeLists.txt | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e34e5f2..29d7fba 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,11 @@ brew install boost # optional ``` ## Compile and Install -Run minimal compilation (just the library) with `Debug` and `Release` settings into `build-quick/lib`: +Run minimal compilation (just the library) with `Release` settings into `build-quick/lib`: ```shell cmake --workflow --preset quick-release ``` -Install the `Release` build of `build-libonly` into `$PWD/local` path: +Install the `Release` build of `build-quick` into `$PWD/local` path: ```shell cmake --install build-quick --config Release --prefix $PWD/local ``` @@ -42,7 +42,7 @@ cmake -S example -B build-example -DCMAKE_PREFIX_PATH=$PWD/local -DCMAKE_MODULE_ ``` ```shell cmake --build build-example -ctest --test-dit build-example --output-on-failure +ctest --test-dir build-example --output-on-failure ``` ## Other Presets diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 7ab20f8..4c4617f 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -7,4 +7,5 @@ include(libstrategy) add_executable(example example.cpp) target_link_libraries(example PRIVATE libstrategy::strategy) +enable_testing() add_test(NAME example COMMAND example) From 5c155ffcb936daeeb3dcf4d6ae6738d7f9943c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Tue, 30 Jun 2026 12:19:13 +0000 Subject: [PATCH 08/17] Removed nlohmann_json from public headers and fixed install interface - Moved SimpleTree::node_t definition to SimpleTree.cpp so that SimpleTree.h no longer depends on nlohmann_json (avoids leaking the dependency to consumers of the installed library) - Added Rule of Five to SimpleTree: copy disabled, move noexcept; destructor and move assignment defined in .cpp where node_t is complete - Added INSTALL_INTERFACE include dirs to strategy/strategyStatic targets so installed consumers find libstrategy headers via cmake - Fixed FIND_PACKAGE_ARGS version (1.1.2 -> 1.0.9) in libstrategy.cmake so the example uses the locally installed library instead of fetching - Removed nlohmann_json from example/CMakeLists.txt (no longer needed) Co-Authored-By: Claude Sonnet 4.6 --- cmake/libstrategy.cmake | 2 +- example/CMakeLists.txt | 1 - src/CMakeLists.txt | 11 ++++-- src/SimpleTree.cpp | 71 +++++++++++++++++++++++++++++++++++++++ src/SimpleTree.h | 74 +++-------------------------------------- 5 files changed, 85 insertions(+), 74 deletions(-) diff --git a/cmake/libstrategy.cmake b/cmake/libstrategy.cmake index 0b4d821..9cd602f 100644 --- a/cmake/libstrategy.cmake +++ b/cmake/libstrategy.cmake @@ -6,7 +6,7 @@ FetchContent_Declare(libstrategy GIT_PROGRESS TRUE # show download progress in Ninja USES_TERMINAL_DOWNLOAD TRUE EXCLUDE_FROM_ALL # don't build if not used - FIND_PACKAGE_ARGS 1.1.2) + FIND_PACKAGE_ARGS 1.0.9) set(LIBSTRATEGY_TESTS OFF CACHE BOOL "libstrategy Unit Tests") set(LIBSTRATEGY_OnlyLibrary ON CACHE BOOL "Build only the library (no binary utilities)") diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 4c4617f..5bd12fb 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.28) project(example VERSION 1.0.9) -include(nlohmann_json) include(ptrie) include(libstrategy) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f8cfc4d..cc4dfad 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,16 +1,22 @@ +include(GNUInstallDirs) + add_library(SimpleTree OBJECT SimpleTree.cpp) target_link_libraries(SimpleTree PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) add_library(libstrategy::SimpleTree ALIAS SimpleTree) add_library(strategy SHARED libz2s.cpp ZonotopStrategy.cpp $) target_link_libraries(strategy PUBLIC ptrie::ptrie) -target_include_directories(strategy PRIVATE $) +target_include_directories(strategy + PUBLIC $ + PRIVATE $) set_target_properties(strategy PROPERTIES VISIBILITY_INLINE_HIDDEN ON) add_library(libstrategy::strategy ALIAS strategy) add_library(strategyStatic STATIC libz2s.cpp ZonotopStrategy.cpp $) target_link_libraries(strategyStatic PUBLIC ptrie::ptrie) -target_include_directories(strategyStatic PRIVATE $) +target_include_directories(strategyStatic + PUBLIC $ + PRIVATE $) set_target_properties(strategyStatic PROPERTIES OUTPUT_NAME strategy) add_library(libstrategy::strategyStatic ALIAS strategyStatic) @@ -19,7 +25,6 @@ if(NOT LIBSTRATEGY_OnlyLibrary) target_link_libraries(z2s PUBLIC SimpleTree Boost::program_options) endif() -include(GNUInstallDirs) include(CMakePackageConfigHelpers) set(CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/libstrategy) diff --git a/src/SimpleTree.cpp b/src/SimpleTree.cpp index e6b561e..ba1d5d7 100644 --- a/src/SimpleTree.cpp +++ b/src/SimpleTree.cpp @@ -29,11 +29,82 @@ #include #include +#include +#include +#include +#include #include +#include #include using json = nlohmann::json; +struct SimpleTree::node_t : std::enable_shared_from_this +{ + uint32_t _var = std::numeric_limits::max(); + double _limit = -std::numeric_limits::infinity(); + double _cost = std::numeric_limits::infinity(); + std::pair _cost_bounds; + node_ptr _low; + node_ptr _high; + node_t* _parent{nullptr}; + void insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, bool minimize, + double accuracy, std::vector& exactness); + std::ostream& print(std::ostream& out, size_t tabs = 0) const; + bool is_leaf() const { return _low == nullptr && _high == nullptr; } + node_ptr simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent); + void subsumption_reduction(bool minimization, SimpleTree& parent); + void action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid); + std::pair compute_min_max() const; + bool check_tiles(node_t* start, std::vector&, std::vector>& bounds, double val, + double minval, double maxval, bool minimization, size_t offset); + bool subsumes(const std::vector>& bounds, std::vector>& obounds, + double val, bool minimization, size_t offset, double& best, std::pair& closest) const; + void get_ranks(std::set>& values, node_t* start); + void set_ranks(std::unordered_map& values); + std::ostream& print_c(std::ostream& os, size_t disc, std::unordered_set& printed, + size_t tabs = 0) const; + std::ostream& print_c_nested(std::ostream& os, size_t disc, size_t tabs, std::vector& toprint, + const node_ptr& node) const; + size_t depth() const; + double value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const; + void consistent(size_t) const; + bool cost_intersect(const node_t& other) const; + double midcost(const node_t& other, double minval, double maxval) const; + bool operator==(const node_t& other) const + { + if (_limit != other._limit) + return false; + if (_var != other._var) + return false; + if (_low != other._low) + return false; + if (_high != other._high) + return false; + return true; + } + bool operator<(const node_t& other) const + { + if (_limit != other._limit) + return _limit < other._limit; + if (_var != other._var) + return _var < other._var; + if (_low != other._low) + return _low < other._low; + return _high < other._high; + } + node_ptr& operator[](bool b) { return b ? _high : _low; } + operator signature_t() const + { + if (is_leaf()) + return signature_t{_var, _limit, _low.get(), _high.get()}; + return signature_t{0, _cost, nullptr, nullptr}; + } +}; + +SimpleTree& SimpleTree::operator=(SimpleTree&&) noexcept = default; +SimpleTree::~SimpleTree() = default; + const std::vector& SimpleTree::actions() const { return _actions; } SimpleTree SimpleTree::parse(std::istream& input, bool simplify, bool subsumption, double accuracy) diff --git a/src/SimpleTree.h b/src/SimpleTree.h index c0ec46e..a38b208 100644 --- a/src/SimpleTree.h +++ b/src/SimpleTree.h @@ -25,19 +25,20 @@ #ifndef SIMPLETREE_H #define SIMPLETREE_H -#include #include // includes iostream :-( #include #include -#include -#include #include class SimpleTree { public: - SimpleTree(const SimpleTree& orig) = default; + SimpleTree(const SimpleTree&) = delete; + SimpleTree(SimpleTree&&) noexcept = default; + SimpleTree& operator=(const SimpleTree&) = delete; + SimpleTree& operator=(SimpleTree&&) noexcept; + ~SimpleTree(); static SimpleTree parse(std::istream&, bool simplify = false, bool subsumption = false, double accuracy = 0); static SimpleTree parse(std::istream&, bool simplify, bool subsumption, double accuracy, std::vector& exactness); @@ -63,73 +64,8 @@ class SimpleTree friend struct ptrie::byte_iterator; using nodemap_t = ptrie::map; - using json = nlohmann::json; SimpleTree() = default; - struct node_t : std::enable_shared_from_this - { - uint32_t _var = std::numeric_limits::max(); - double _limit = -std::numeric_limits::infinity(); - double _cost = std::numeric_limits::infinity(); - std::pair _cost_bounds; - node_ptr _low; - node_ptr _high; - node_t* _parent{nullptr}; - void insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, - bool minimize, double accuracy, std::vector& exactness); - std::ostream& print(std::ostream& out, size_t tabs = 0) const; - bool is_leaf() const { return _low == nullptr && _high == nullptr; } - node_ptr simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent); - void subsumption_reduction(bool minimization, SimpleTree& parent); - void action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid); - std::pair compute_min_max() const; - bool check_tiles(node_t* start, std::vector&, std::vector>& bounds, - double val, double minval, double maxval, bool minimization, size_t offset); - bool subsumes(const std::vector>& bounds, - std::vector>& obounds, double val, bool minimization, size_t offset, - double& best, std::pair& closest) const; - void get_ranks(std::set>& values, node_t* start); - void set_ranks(std::unordered_map& values); - std::ostream& print_c(std::ostream& os, size_t disc, std::unordered_set& printed, - size_t tabs = 0) const; - std::ostream& print_c_nested(std::ostream& os, size_t disc, size_t tabs, std::vector& toprint, - const node_ptr& node) const; - size_t depth() const; - double value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const; - void consistent(size_t) const; - bool cost_intersect(const node_t& other) const; - double midcost(const node_t& other, double minval, double maxval) const; - bool operator==(const node_t& other) const - { - if (_limit != other._limit) - return false; - if (_var != other._var) - return false; - if (_low != other._low) - return false; - if (_high != other._high) - return false; - return true; - } - bool operator<(const node_t& other) const - { - if (_limit != other._limit) - return _limit < other._limit; - if (_var != other._var) - return _var < other._var; - if (_low != other._low) - return _low < other._low; - return _high < other._high; - } - node_ptr& operator[](bool b) { return b ? _high : _low; } - operator signature_t() const - { ///< support implicit conversion for map keys - if (is_leaf()) - return signature_t{_var, _limit, _low.get(), _high.get()}; - return signature_t{0, _cost, nullptr, nullptr}; - } - }; - std::vector _actions; std::vector _statevars; std::vector _pointvars; From b74c356ff4aca2bb4741bec801879db68aee7074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Tue, 30 Jun 2026 13:23:07 +0000 Subject: [PATCH 09/17] Fixed CMake install: include dirs, missing headers, visibility, static name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added INSTALL_INTERFACE include dirs to strategy and strategyStatic so installed consumers get the libstrategy headers on their include path - Added find_dependency(ptrie) to config template so downstream find_package(libstrategy) resolves ptrie::ptrie without extra setup - Added libz2s.h to install(FILES) — it declares the public C/FFI API - Replaced VISIBILITY_INLINE_HIDDEN (typo, no-op) with CMAKE_VISIBILITY_INLINES_HIDDEN so the flag applies to OBJECT targets - Renamed static archive to strategy-static to avoid strategy.lib collision with the shared library import lib on Windows - Split ZonotopStrategy and libz2s into separate OBJECT libraries so each source unit declares its own dependencies explicitly Co-Authored-By: Claude Sonnet 4.6 --- src/CMakeLists.txt | 30 ++++++++++++++++-------------- src/libstrategyConfig.cmake.in | 1 + 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cc4dfad..4a0ab07 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,28 +1,30 @@ include(GNUInstallDirs) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + add_library(SimpleTree OBJECT SimpleTree.cpp) target_link_libraries(SimpleTree PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) -add_library(libstrategy::SimpleTree ALIAS SimpleTree) -add_library(strategy SHARED libz2s.cpp ZonotopStrategy.cpp $) +add_library(ZonotopStrategy OBJECT ZonotopStrategy.cpp) +target_link_libraries(ZonotopStrategy PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) + +add_library(libz2s OBJECT libz2s.cpp) +target_link_libraries(libz2s PUBLIC ptrie::ptrie) + +add_library(strategy SHARED $ $ $) target_link_libraries(strategy PUBLIC ptrie::ptrie) -target_include_directories(strategy - PUBLIC $ - PRIVATE $) -set_target_properties(strategy PROPERTIES VISIBILITY_INLINE_HIDDEN ON) +target_include_directories(strategy PUBLIC $) add_library(libstrategy::strategy ALIAS strategy) -add_library(strategyStatic STATIC libz2s.cpp ZonotopStrategy.cpp $) +add_library(strategyStatic STATIC $ $ $) target_link_libraries(strategyStatic PUBLIC ptrie::ptrie) -target_include_directories(strategyStatic - PUBLIC $ - PRIVATE $) -set_target_properties(strategyStatic PROPERTIES OUTPUT_NAME strategy) +target_include_directories(strategyStatic PUBLIC $) +set_target_properties(strategyStatic PROPERTIES OUTPUT_NAME strategy-static) add_library(libstrategy::strategyStatic ALIAS strategyStatic) if(NOT LIBSTRATEGY_OnlyLibrary) - add_executable(z2s main.cpp ZonotopStrategy.cpp) - target_link_libraries(z2s PUBLIC SimpleTree Boost::program_options) + add_executable(z2s main.cpp) + target_link_libraries(z2s PUBLIC ZonotopStrategy SimpleTree Boost::program_options) endif() include(CMakePackageConfigHelpers) @@ -38,7 +40,7 @@ write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/libstrategyConfigVe install(TARGETS strategy strategyStatic EXPORT libstrategyTargets) -install(FILES errors.h SimpleTree.h ZonotopStrategy.h +install(FILES errors.h libz2s.h SimpleTree.h ZonotopStrategy.h DESTINATION include/libstrategy) install(EXPORT libstrategyTargets FILE libstrategyTargets.cmake diff --git a/src/libstrategyConfig.cmake.in b/src/libstrategyConfig.cmake.in index 2d161b2..9b57da7 100644 --- a/src/libstrategyConfig.cmake.in +++ b/src/libstrategyConfig.cmake.in @@ -1,3 +1,4 @@ @PACKAGE_INIT@ +find_dependency(ptrie) include("${CMAKE_CURRENT_LIST_DIR}/libstrategyTargets.cmake") check_required_components(libstrategy) From eee94099acb1ad0346efe1e611594fa2cb0aa73d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Tue, 30 Jun 2026 13:39:23 +0000 Subject: [PATCH 10/17] Add MinGW-w64 cross-compilation toolchain for Windows x86-64 Targets x86_64-w64-mingw32-gcc/g++ with windres for resource files. Sets CMAKE_FIND_ROOT_PATH to /usr/x86_64-w64-mingw32 with NEVER/ONLY modes so host tools are used as-is while libraries and headers are searched only in the target sysroot. Usage: cmake --preset multi -DCMAKE_TOOLCHAIN_FILE=$PWD/cmake/mingw-w64-x86_64.cmake Co-Authored-By: Claude Sonnet 4.6 --- cmake/mingw-w64-x86_64.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 cmake/mingw-w64-x86_64.cmake diff --git a/cmake/mingw-w64-x86_64.cmake b/cmake/mingw-w64-x86_64.cmake new file mode 100644 index 0000000..2fc3a27 --- /dev/null +++ b/cmake/mingw-w64-x86_64.cmake @@ -0,0 +1,12 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) +set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) + +set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) From ce5451497a1a66a05f5bc83d2af9a080e96902e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 1 Jul 2026 06:00:51 +0000 Subject: [PATCH 11/17] Fix Boost component targets missing when system Boost is found via BoostConfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CMP0167 NEW (CMake ≥3.30), find_package(Boost) uses Boost's own BoostConfig.cmake instead of CMake's legacy FindBoost. BoostConfig only creates compiled-library targets (Boost::program_options, etc.) for explicitly requested COMPONENTS; without them the find succeeds on headers alone, FetchContent treats Boost as "already found", and no component targets are ever defined. Adding COMPONENTS to FIND_PACKAGE_ARGS ensures the targets exist whether Boost is found in-tree or fetched. Co-Authored-By: Claude Sonnet 4.6 --- cmake/boost.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/boost.cmake b/cmake/boost.cmake index b492435..f6299c8 100644 --- a/cmake/boost.cmake +++ b/cmake/boost.cmake @@ -11,7 +11,7 @@ FetchContent_Declare(Boost #GIT_PROGRESS TRUE # show download progress in Ninja USES_TERMINAL_DOWNLOAD TRUE EXCLUDE_FROM_ALL # don't build if not used - FIND_PACKAGE_ARGS 1.91.0) + FIND_PACKAGE_ARGS 1.91.0 COMPONENTS program_options unit_test_framework) set(BOOST_ENABLE_MPI OFF CACHE BOOL "Boost.MPI and its dependents (requires MPI, CMake 3.10)") set(BOOST_ENABLE_PYTHON OFF CACHE BOOL "Boost.Python and its dependents (requires Python, CMake 3.14)") From edf76d08a0554d57f54a90159c30790817cafaf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 1 Jul 2026 06:19:21 +0000 Subject: [PATCH 12/17] Fix DLL-not-found crash for tests on Windows (0xc0000135) Without CMAKE_RUNTIME_OUTPUT_DIRECTORY, Ninja Multi-Config places each target's DLL/exe in its subdirectory's binary dir (src/Debug/, test/Debug/, ...). Windows DLL search checks the application directory first, so test executables in test/Debug/ can't find strategy.dll in src/Debug/. Setting CMAKE_RUNTIME_OUTPUT_DIRECTORY="${CMAKE_BINARY_DIR}" makes all DLLs and executables land in the same per-config directory (Debug/, Release/) so Windows loads the DLL from the application directory. Co-Authored-By: Claude Sonnet 4.6 --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d635938..150a10b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") option(LIBSTRATEGY_TESTS "libstrategy Unit Tests" ON) option(LIBSTRATEGY_OnlyLibrary "Build only the library (no binary utilities)" OFF) From 7484e7c4f9f6b8484e512704270150842470450c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 1 Jul 2026 07:43:10 +0000 Subject: [PATCH 13/17] Upgraded ptrie to 1.1.3 which has a lot of quality fixes --- cmake/ptrie.cmake | 10 ++++++---- src/SimpleTree.h | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cmake/ptrie.cmake b/cmake/ptrie.cmake index 4e1b5fe..e8d9209 100644 --- a/cmake/ptrie.cmake +++ b/cmake/ptrie.cmake @@ -3,12 +3,12 @@ include(FetchContent) FetchContent_Declare(ptrie GIT_REPOSITORY https://github.com/DEIS-Tools/ptrie - GIT_TAG v1.1.2 + GIT_TAG v1.1.3 GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) GIT_PROGRESS TRUE # show download progress in Ninja USES_TERMINAL_DOWNLOAD TRUE EXCLUDE_FROM_ALL # don't build if not used - FIND_PACKAGE_ARGS 1.1.2) + FIND_PACKAGE_ARGS 1.1.3) set(PTRIE_BuildTests OFF CACHE BOOL "Build the unit tests when BUILD_TESTING is enabled.") set(PTRIE_BuildBenchmark OFF CACHE BOOL "Build the simple benchmark suite") @@ -19,8 +19,10 @@ if (ptrie_FOUND) # find_package else (ptrie_FOUND) # fetch_content message(STATUS "Fetched ptrie: ${ptrie_SOURCE_DIR}") # Workaround until ptrie exports proper cmake config: - add_library(ptrie::ptrie INTERFACE IMPORTED GLOBAL) - target_include_directories(ptrie::ptrie INTERFACE ${ptrie_SOURCE_DIR}/src) + if (NOT TARGET ptrie::ptrie) + add_library(ptrie::ptrie INTERFACE IMPORTED GLOBAL) + target_include_directories(ptrie::ptrie INTERFACE ${ptrie_SOURCE_DIR}/src) + endif() endif (ptrie_FOUND) if (TARGET ptrie::ptrie) diff --git a/src/SimpleTree.h b/src/SimpleTree.h index a38b208..64d490c 100644 --- a/src/SimpleTree.h +++ b/src/SimpleTree.h @@ -91,7 +91,8 @@ struct ptrie::byte_iterator return sizeof(SimpleTree::signature_t); } - static constexpr bool continious() { return true; } + [[deprecated]] static constexpr bool continious() { return true; } + static constexpr bool continuous() { return true; } // add read_blob, write_blob }; From c6391dea408683a3442e9f305614ebb389f6e0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 1 Jul 2026 09:17:00 +0000 Subject: [PATCH 14/17] Replace boost::program_options with custom CLI parsing in main.cpp Removes the z2s executable's dependency on Boost.ProgramOptions, using a small string_view/from_chars-based argument parser instead. Boost is now only fetched/linked for the unit_test_framework used by the test suite. --- CMakeLists.txt | 2 +- cmake/boost.cmake | 5 +- src/CMakeLists.txt | 2 +- src/main.cpp | 134 +++++++++++++++++++++++++++++---------------- 4 files changed, 91 insertions(+), 52 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 150a10b..fcb3e8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ include(warnings) include(sanitizers) include(nlohmann_json) include(ptrie) -if (LIBSTRATEGY_TESTS OR NOT LIBSTRATEGY_OnlyLibrary) +if (LIBSTRATEGY_TESTS) include(boost) endif() diff --git a/cmake/boost.cmake b/cmake/boost.cmake index f6299c8..b7f77b6 100644 --- a/cmake/boost.cmake +++ b/cmake/boost.cmake @@ -11,7 +11,7 @@ FetchContent_Declare(Boost #GIT_PROGRESS TRUE # show download progress in Ninja USES_TERMINAL_DOWNLOAD TRUE EXCLUDE_FROM_ALL # don't build if not used - FIND_PACKAGE_ARGS 1.91.0 COMPONENTS program_options unit_test_framework) + FIND_PACKAGE_ARGS 1.91.0 COMPONENTS unit_test_framework) set(BOOST_ENABLE_MPI OFF CACHE BOOL "Boost.MPI and its dependents (requires MPI, CMake 3.10)") set(BOOST_ENABLE_PYTHON OFF CACHE BOOL "Boost.Python and its dependents (requires Python, CMake 3.14)") @@ -27,9 +27,6 @@ else (Boost_program_options_FOUND) # fetch_content message(STATUS "Got Boost: ${Boost_SOURCE_DIR}") endif (Boost_FOUND) -if (TARGET Boost::program_options) - message(STATUS " Available target: Boost::program_options") -endif () if (TARGET Boost::unit_test_framework) message(STATUS " Available target: Boost::unit_test_framework") endif () diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a0ab07..bb5bccb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,7 @@ add_library(libstrategy::strategyStatic ALIAS strategyStatic) if(NOT LIBSTRATEGY_OnlyLibrary) add_executable(z2s main.cpp) - target_link_libraries(z2s PUBLIC ZonotopStrategy SimpleTree Boost::program_options) + target_link_libraries(z2s PUBLIC ZonotopStrategy SimpleTree) endif() include(CMakePackageConfigHelpers) diff --git a/src/main.cpp b/src/main.cpp index d62bb68..8796460 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,21 +1,21 @@ /* * Copyright (C) 2020 Peter G. Jensen - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -/* +/* * Author: Peter G. Jensen * * Created on December 13, 2018, 2:11 PM @@ -23,77 +23,119 @@ #include "ZonotopStrategy.h" -#include - #include #include +#include #include #include +#include +#include +#include + +namespace { + +void print_help(const char* prog) +{ + std::cout << "Usage: " << prog + << " [options]\n\n" + "Options:\n" + " -h, --help produce help message\n" + " -i, --input Input of synthesized controller.\n" + " -l, --learned Input of learned controller.\n" + " -c, --choice Output function name for choice.\n" + " -p, --pattern Output function name for pattern.\n" + " -a, --accuracy \n" + " -e, --exactness ...\n"; +} -namespace po = boost::program_options; +// A token counts as a value (not the start of a new option) unless it +// looks like "-x" or "--xxx" (as opposed to a negative number like "-1.5"). +bool is_option(std::string_view s) +{ + return s.size() > 1 && s[0] == '-' && !(std::isdigit(static_cast(s[1])) || s[1] == '.'); +} + +double to_double(std::string_view s) +{ + double value; + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); + if (ec != std::errc() || ptr != s.data() + s.size()) { + std::cerr << "Invalid number: " << s << std::endl; + std::exit(-1); + } + return value; +} -int main(int argc, char **argv) { +} // namespace - po::options_description opts; +int main(int argc, char** argv) +{ std::string infile; std::string learned; std::string learned_name = "choice"; std::string pattern_name = "pattern"; double accuracy = 0; std::vector exactness; - opts.add_options() - ("help,h", "produce help message") - ("input,i", po::value(&infile), - "Input of synthesized controller.") - ("learned,l", po::value(&learned), - "Input of learned controller.") - ("choice,c", po::value(&learned_name), - "Output function name for choice.") - ("pattern,p", po::value(&pattern_name), - "Output function name for pattern.") - ("accuracy,a", po::value(&accuracy)) - ("exactness,e", po::value>(&exactness)->multitoken()) - ; - po::variables_map vm; - po::store(po::parse_command_line(argc, argv, opts), vm); - po::notify(vm); - - if (vm.count("help")) { - std::cout << opts << "\n"; - return 1; + std::vector args(argv + 1, argv + argc); + for (size_t i = 0; i < args.size(); ++i) { + std::string_view arg = args[i]; + auto next_value = [&]() -> std::string_view { + if (i + 1 >= args.size() || is_option(args[i + 1])) { + std::cerr << "Missing value for option " << arg << std::endl; + std::exit(-1); + } + return args[++i]; + }; + if (arg == "-h" || arg == "--help") { + print_help(argv[0]); + return 1; + } else if (arg == "-i" || arg == "--input") { + infile = next_value(); + } else if (arg == "-l" || arg == "--learned") { + learned = next_value(); + } else if (arg == "-c" || arg == "--choice") { + learned_name = next_value(); + } else if (arg == "-p" || arg == "--pattern") { + pattern_name = next_value(); + } else if (arg == "-a" || arg == "--accuracy") { + accuracy = to_double(next_value()); + } else if (arg == "-e" || arg == "--exactness") { + while (i + 1 < args.size() && !is_option(args[i + 1])) { + exactness.push_back(to_double(args[++i])); + } + } else { + std::cerr << "Unknown option: " << arg << std::endl; + print_help(argv[0]); + return -1; + } } std::cout << "#include \"sub_ctrl.h\"\n"; - if(!infile.empty()) - { + if (!infile.empty()) { std::ifstream instream(infile); - if(instream.fail()) - { + if (instream.fail()) { std::cerr << "Could not open infile for reading : " << infile << std::endl; - return -1; + return -1; } auto strategy = ZonotopStrategy::parse(instream); - std::cout << "// From \"" << infile << "\"" << std::endl; + std::cout << "// From \"" << infile << "\"" << std::endl; strategy.print_c(std::cout, pattern_name); - } - if(learned.empty()) - { + } + if (learned.empty()) { return 0; } std::ifstream lstream(learned); - if(lstream.fail()) - { + if (lstream.fail()) { std::cerr << "Could not open learned for reading : " << learned << std::endl; - return -1; + return -1; } - std::cout << "// From \"" << learned << "\"" << std::endl; + std::cout << "// From \"" << learned << "\"" << std::endl; std::cerr << "EXACTNESS "; - for(auto e : exactness) - std::cerr << e<< " , "; + for (auto e : exactness) + std::cerr << e << " , "; std::cerr << std::endl; auto learned_strategy = SimpleTree::parse(lstream, true, true, accuracy, exactness); - //strategy.filter(learned_strategy); + // strategy.filter(learned_strategy); learned_strategy.print_c(std::cout, learned_name); return 0; } - From 1861172bc1d239a620411d08da98d2723c497e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 1 Jul 2026 12:48:24 +0000 Subject: [PATCH 15/17] Converted Boost.Test into Doctest and removed dependency for Boost --- CMakeLists.txt | 13 ++++----- cmake/doctest.cmake | 33 +++++++++++++++++++++++ test/CMakeLists.txt | 9 +++---- test/SimpleTree_test.cpp | 50 +++++++++++++++++----------------- test/inconsistent_lookup.cpp | 52 ++++++++++++++++++------------------ test/unordered_load.cpp | 38 ++++++++++++++------------ 6 files changed, 113 insertions(+), 82 deletions(-) create mode 100644 cmake/doctest.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index fcb3e8c..f6ced1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,14 +14,10 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") option(LIBSTRATEGY_TESTS "libstrategy Unit Tests" ON) option(LIBSTRATEGY_OnlyLibrary "Build only the library (no binary utilities)" OFF) -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -include(warnings) -include(sanitizers) -include(nlohmann_json) -include(ptrie) -if (LIBSTRATEGY_TESTS) - include(boost) -endif() +include(cmake/warnings.cmake) +include(cmake/sanitizers.cmake) +include(cmake/nlohmann_json.cmake) +include(cmake/ptrie.cmake) if(NOT LIBSTRATEGY_OnlyLibrary) cmake_policy(SET CMP0069 NEW) # INTERPROCEDURAL_OPTIMIZATIONS flags @@ -30,6 +26,7 @@ endif() add_subdirectory(src) if (LIBSTRATEGY_TESTS) + include(cmake/doctest.cmake) enable_testing() add_subdirectory(test) endif() diff --git a/cmake/doctest.cmake b/cmake/doctest.cmake new file mode 100644 index 0000000..58287ae --- /dev/null +++ b/cmake/doctest.cmake @@ -0,0 +1,33 @@ +# Downloads and compiles DocTest unit testing framework +include(FetchContent) +#set(FETCHCONTENT_QUIET ON) +#set(FETCHCONTENT_UPDATES_DISCONNECTED ON) + +FetchContent_Declare(doctest + GIT_REPOSITORY https://github.com/doctest/doctest.git + GIT_TAG v2.5.2 # "main" for latest + GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) + GIT_PROGRESS TRUE # show download progress in Ninja + EXCLUDE_FROM_ALL ON # don't build if not used + FIND_PACKAGE_ARGS 2.4.12) + +set(DOCTEST_WITH_TESTS OFF CACHE BOOL "Build tests/examples") +set(DOCTEST_WITH_MAIN_IN_STATIC_LIB ON CACHE BOOL "Build a static lib for doctest::doctest_with_main") +set(DOCTEST_NO_INSTALL OFF CACHE BOOL "Skip the installation process") +set(DOCTEST_USE_STD_HEADERS OFF CACHE BOOL "Use std headers") + +FetchContent_MakeAvailable(doctest) + +if(doctest_FOUND) # find_package + message(STATUS "Found doctest: ${doctest_DIR}") +else(doctest_FOUND) # FetchContent + message(STATUS "Fetched doctest: ${doctest_SOURCE_DIR}") +endif(doctest_FOUND) + +if (TARGET doctest::doctest) + message(STATUS " Available target: doctest::doctest") +endif () + +if (TARGET doctest::doctest_with_main) + message(STATUS " Available target: doctest::doctest_with_main") +endif () diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5f11c4b..010c7c4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,18 +1,17 @@ -find_package (Boost COMPONENTS unit_test_framework REQUIRED) -include_directories (${CMAKE_SOURCE_DIR}/src) +include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (utilities_test utilities_test.cpp) add_test(NAME utilities_test COMMAND utilities_test) add_executable (SimpleTree_test SimpleTree_test.cpp) -target_link_libraries(SimpleTree_test PRIVATE SimpleTree Boost::unit_test_framework) +target_link_libraries(SimpleTree_test PRIVATE SimpleTree doctest::doctest_with_main) add_test(NAME SimpleTree_test COMMAND SimpleTree_test) add_executable (unordered_load unordered_load.cpp) -target_link_libraries(unordered_load PRIVATE strategy Boost::unit_test_framework) +target_link_libraries(unordered_load PRIVATE strategy doctest::doctest_with_main) add_test(NAME unordered_load COMMAND unordered_load) add_executable (inconsistent_lookup inconsistent_lookup.cpp) -target_link_libraries(inconsistent_lookup PRIVATE strategy Boost::unit_test_framework) +target_link_libraries(inconsistent_lookup PRIVATE strategy doctest::doctest_with_main) add_test(NAME inconsistent_lookup COMMAND inconsistent_lookup) set_tests_properties(inconsistent_lookup PROPERTIES ENVIRONMENT STRATEGY_DIR=${CMAKE_CURRENT_SOURCE_DIR}/strategies) diff --git a/test/SimpleTree_test.cpp b/test/SimpleTree_test.cpp index b5fce7b..bdd7b42 100644 --- a/test/SimpleTree_test.cpp +++ b/test/SimpleTree_test.cpp @@ -1,46 +1,44 @@ -#define BOOST_TEST_MODULE UnorderedLoad - #include "SimpleTree.h" #include "errors.h" #include "utilities.hpp" -#include +#include -BOOST_AUTO_TEST_CASE(SimpleTreeParseKey) +TEST_CASE("SimpleTree::parse_key") { const auto res_blank = parse_key("()"); - BOOST_CHECK(res_blank.empty()); + CHECK(res_blank.empty()); const auto res_one = parse_key("(1)"); - BOOST_REQUIRE_EQUAL(res_one.size(), 1); - BOOST_CHECK_EQUAL(res_one[0], 1); + REQUIRE(res_one.size() == 1); + CHECK(res_one[0] == 1); const auto res_two = parse_key("(2,1)"); - BOOST_REQUIRE_EQUAL(res_two.size(), 2); - BOOST_CHECK_EQUAL(res_two[0], 2); - BOOST_CHECK_EQUAL(res_two[1], 1); + REQUIRE(res_two.size() == 2); + CHECK(res_two[0] == 2); + CHECK(res_two[1] == 1); const auto res_three = parse_key("(3,2,1)"); - BOOST_REQUIRE_EQUAL(res_three.size(), 3); - BOOST_CHECK_EQUAL(res_three[0], 3); - BOOST_CHECK_EQUAL(res_three[1], 2); - BOOST_CHECK_EQUAL(res_three[2], 1); + REQUIRE(res_three.size() == 3); + CHECK(res_three[0] == 3); + CHECK(res_three[1] == 2); + CHECK(res_three[2] == 1); const auto res_float = parse_key("(3.141)"); - BOOST_REQUIRE_EQUAL(res_float.size(), 1); - BOOST_CHECK_EQUAL(res_float[0], 3.141); + REQUIRE(res_float.size() == 1); + CHECK(res_float[0] == 3.141); const auto res_floats = parse_key("(4.3,2.1)"); - BOOST_REQUIRE_EQUAL(res_floats.size(), 2); - BOOST_CHECK_EQUAL(res_floats[0], 4.3); - BOOST_CHECK_EQUAL(res_floats[1], 2.1); + REQUIRE(res_floats.size() == 2); + CHECK(res_floats[0] == 4.3); + CHECK(res_floats[1] == 2.1); // a few negative tests: - BOOST_CHECK_THROW(parse_key(""), base_error); - BOOST_CHECK_THROW(parse_key("1"), base_error); - BOOST_CHECK_THROW(parse_key(")"), base_error); - BOOST_CHECK_THROW(parse_key("("), base_error); - BOOST_CHECK_THROW(parse_key("(1"), base_error); - BOOST_CHECK_THROW(parse_key("(2,"), base_error); - BOOST_CHECK_THROW(parse_key("(2,1"), base_error); + CHECK_THROWS_AS(parse_key(""), base_error); + CHECK_THROWS_AS(parse_key("1"), base_error); + CHECK_THROWS_AS(parse_key(")"), base_error); + CHECK_THROWS_AS(parse_key("("), base_error); + CHECK_THROWS_AS(parse_key("(1"), base_error); + CHECK_THROWS_AS(parse_key("(2,"), base_error); + CHECK_THROWS_AS(parse_key("(2,1"), base_error); } diff --git a/test/inconsistent_lookup.cpp b/test/inconsistent_lookup.cpp index d276b7c..f311314 100644 --- a/test/inconsistent_lookup.cpp +++ b/test/inconsistent_lookup.cpp @@ -15,42 +15,42 @@ * along with this program. If not, see . */ -#define BOOST_TEST_MODULE UnorderedLoad - #include "SimpleTree.h" -#include +#include + #include #include -namespace fs = std::filesystem; - -BOOST_AUTO_TEST_CASE(DirectoryTest) { BOOST_REQUIRE(getenv("STRATEGY_DIR")); } +TEST_SUITE_BEGIN("Unordered Load"); -const auto strategy_path = fs::path{getenv("STRATEGY_DIR")} / "inconsistent1.strategy"; +namespace fs = std::filesystem; -BOOST_AUTO_TEST_CASE(Inconsistent1) +TEST_CASE("Inconsistent1") { - auto in = std::ifstream{strategy_path}; - auto tree = SimpleTree::parse(in, false, false); - double vars[] = {10}; - auto act18 = tree.value(vars, nullptr, 0); - auto act19 = tree.value(vars, nullptr, 1); - BOOST_REQUIRE_LT(act18, act19); -} + const auto strategy_dir = getenv("STRATEGY_DIR"); + REQUIRE(strategy_dir != nullptr); -BOOST_AUTO_TEST_CASE(Inconsistent1Simplify) -{ + const auto strategy_path = fs::path{strategy_dir} / "inconsistent1.strategy"; auto in = std::ifstream{strategy_path}; - auto tree = SimpleTree::parse(in, true, false); double vars[] = {10}; - BOOST_REQUIRE_LT(tree.value(vars, nullptr, 0), tree.value(vars, nullptr, 1)); + SUBCASE("Plain") + { + auto tree = SimpleTree::parse(in, false, false); + auto act18 = tree.value(vars, nullptr, 0); + auto act19 = tree.value(vars, nullptr, 1); + REQUIRE(act18 < act19); + } + SUBCASE("Simplify") + { + auto tree = SimpleTree::parse(in, true, false); + REQUIRE(tree.value(vars, nullptr, 0) < tree.value(vars, nullptr, 1)); + } + SUBCASE("Simplify Subsumption") + { + auto tree = SimpleTree::parse(in, true, true); + REQUIRE(tree.value(vars, nullptr, 0) < tree.value(vars, nullptr, 1)); + } } -BOOST_AUTO_TEST_CASE(Inconsistent1SimplifySubsumption) -{ - auto in = std::ifstream{strategy_path}; - auto tree = SimpleTree::parse(in, true, true); - double vars[] = {10}; - BOOST_REQUIRE_LT(tree.value(vars, nullptr, 0), tree.value(vars, nullptr, 1)); -} +TEST_SUITE_END(); diff --git a/test/unordered_load.cpp b/test/unordered_load.cpp index 22ec8f6..83bbcf6 100644 --- a/test/unordered_load.cpp +++ b/test/unordered_load.cpp @@ -15,11 +15,13 @@ * along with this program. If not, see . */ -#define BOOST_TEST_MODULE UnorderedLoad - #include "SimpleTree.h" -#include +#include + +#include + +TEST_SUITE_BEGIN("unordered_load"); const std::string simple_unordered_strategy = "{\"version\":1.0,\"type\":\"state->regressor\",\"representation\":\"map\"," @@ -320,36 +322,38 @@ const std::string unordered_strategy = "{\"version\":1.0,\"type\":\"state->regre " }" "}"; -BOOST_AUTO_TEST_CASE(SimpleUnorderedKeyLoad) +TEST_CASE("simple_unordered_strategy key load") { auto is = std::istringstream{simple_unordered_strategy}; auto strategy = SimpleTree::parse(is, false, false, 0); double disc[1] = {15.0}; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 2.855407148131672); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.352930166302546); + CHECK(strategy.value(disc, nullptr, 11) == 2.855407148131672); + CHECK(strategy.value(disc, nullptr, 12) == 1.352930166302546); disc[0] = 210; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 1.009248500699941); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.028772620412916); + CHECK(strategy.value(disc, nullptr, 11) == 1.009248500699941); + CHECK(strategy.value(disc, nullptr, 12) == 1.028772620412916); disc[0] = 30; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 1.294636921229531); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 3.091107032150207); + CHECK(strategy.value(disc, nullptr, 11) == 1.294636921229531); + CHECK(strategy.value(disc, nullptr, 12) == 3.091107032150207); } -BOOST_AUTO_TEST_CASE(UnorderedKeyLoad) +TEST_CASE("unordered_strategy key load") { auto is = std::stringstream{unordered_strategy}; auto strategy = SimpleTree::parse(is, false, false, 0); double disc[1] = {15.0}; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 2.855407148131672); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.352930166302546); + CHECK(strategy.value(disc, nullptr, 11) == 2.855407148131672); + CHECK(strategy.value(disc, nullptr, 12) == 1.352930166302546); disc[0] = 210; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 1.009248500699941); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.028772620412916); + CHECK(strategy.value(disc, nullptr, 11) == 1.009248500699941); + CHECK(strategy.value(disc, nullptr, 12) == 1.028772620412916); disc[0] = 30; - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 1.294636921229531); - BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 3.091107032150207); + CHECK(strategy.value(disc, nullptr, 11) == 1.294636921229531); + CHECK(strategy.value(disc, nullptr, 12) == 3.091107032150207); } + +TEST_SUITE_END(); From fd598b51bb38639f5de1939619c78bf2da31cfac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 1 Jul 2026 14:06:06 +0000 Subject: [PATCH 16/17] Added compiler warnings and fixed some, renamed node_t to Node --- cmake/warnings.cmake | 16 +++-- src/CMakeLists.txt | 5 +- src/SimpleTree.cpp | 138 ++++++++++++++++++++-------------------- src/SimpleTree.h | 12 ++-- src/ZonotopStrategy.cpp | 24 +++---- src/ZonotopStrategy.h | 20 +++--- src/libz2s.cpp | 54 +++++++--------- 7 files changed, 135 insertions(+), 134 deletions(-) diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake index 409ab03..3da2ddf 100644 --- a/cmake/warnings.cmake +++ b/cmake/warnings.cmake @@ -1,9 +1,13 @@ +# Consider: -Wconversion -Wsign-conversion -Wshadow -Weffc++ +set(LIBSTRATEGY_COMMON_WARNINGS -Wpedantic -Wall -Wextra -Wold-style-cast -Wcast-align -Wformat=2 -Wimplicit-fallthrough -Wnon-virtual-dtor -Woverloaded-virtual -Wnull-dereference -Wmisleading-indentation) if (CMAKE_CXX_COMPILER_ID MATCHES GNU) - add_compile_options(-Wpedantic -Wall -Wextra) -elseif (CMAKE_CXX_COMPILER_ID MATCHES AppleClang) - add_compile_options(-Wpedantic -Wall -Wextra) + set(LIBSTRATEGY_WARNINGS ${LIBSTRATEGY_COMMON_WARNINGS} -Wuseless-cast -Wduplicated-cond -Wduplicated-branches -Wlogical-op) elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang) - add_compile_options(-Wpedantic -Wall -Wextra) + set(LIBSTRATEGY_WARNINGS ${LIBSTRATEGY_COMMON_WARNINGS}) +elseif (CMAKE_CXX_COMPILER_ID MATCHES AppleClang) + set(LIBSTRATEGY_WARNINGS ${LIBSTRATEGY_COMMON_WARNINGS}) elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) - add_compile_options(/W4) -endif () \ No newline at end of file + set(LIBSTRATEGY_WARNINGS /W4 /w14619 /w14265 /w14545 /w14546 /w14640 /w14905 /w14906 /w14928) +endif () +# Use target_compile_options to avoid warnings on third party code +# add_compile_options(${LIBSTRATEGY_WARNINGS}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb5bccb..5ab994c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,12 +3,14 @@ include(GNUInstallDirs) set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) add_library(SimpleTree OBJECT SimpleTree.cpp) -target_link_libraries(SimpleTree PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) +target_link_libraries(SimpleTree PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) +target_compile_options(SimpleTree PUBLIC ${LIBSTRATEGY_WARNINGS}) add_library(ZonotopStrategy OBJECT ZonotopStrategy.cpp) target_link_libraries(ZonotopStrategy PUBLIC ptrie::ptrie PRIVATE nlohmann_json::nlohmann_json) add_library(libz2s OBJECT libz2s.cpp) +target_compile_options(libz2s PUBLIC ${LIBSTRATEGY_WARNINGS}) target_link_libraries(libz2s PUBLIC ptrie::ptrie) add_library(strategy SHARED $ $ $) @@ -24,6 +26,7 @@ add_library(libstrategy::strategyStatic ALIAS strategyStatic) if(NOT LIBSTRATEGY_OnlyLibrary) add_executable(z2s main.cpp) + target_compile_options(z2s PUBLIC ${LIBSTRATEGY_WARNINGS}) target_link_libraries(z2s PUBLIC ZonotopStrategy SimpleTree) endif() diff --git a/src/SimpleTree.cpp b/src/SimpleTree.cpp index ba1d5d7..be67290 100644 --- a/src/SimpleTree.cpp +++ b/src/SimpleTree.cpp @@ -39,39 +39,39 @@ using json = nlohmann::json; -struct SimpleTree::node_t : std::enable_shared_from_this +struct SimpleTree::Node : std::enable_shared_from_this { uint32_t _var = std::numeric_limits::max(); double _limit = -std::numeric_limits::infinity(); double _cost = std::numeric_limits::infinity(); std::pair _cost_bounds; - node_ptr _low; - node_ptr _high; - node_t* _parent{nullptr}; + NodePtr _low; + NodePtr _high; + Node* _parent{nullptr}; void insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, bool minimize, double accuracy, std::vector& exactness); std::ostream& print(std::ostream& out, size_t tabs = 0) const; bool is_leaf() const { return _low == nullptr && _high == nullptr; } - node_ptr simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent); + NodePtr simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent); void subsumption_reduction(bool minimization, SimpleTree& parent); - void action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid); + void action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid); std::pair compute_min_max() const; - bool check_tiles(node_t* start, std::vector&, std::vector>& bounds, double val, + bool check_tiles(Node* start, std::vector&, std::vector>& bounds, double val, double minval, double maxval, bool minimization, size_t offset); bool subsumes(const std::vector>& bounds, std::vector>& obounds, double val, bool minimization, size_t offset, double& best, std::pair& closest) const; - void get_ranks(std::set>& values, node_t* start); + void get_ranks(std::set>& values, Node* start); void set_ranks(std::unordered_map& values); - std::ostream& print_c(std::ostream& os, size_t disc, std::unordered_set& printed, + std::ostream& print_c(std::ostream& os, size_t disc, std::unordered_set& printed, size_t tabs = 0) const; - std::ostream& print_c_nested(std::ostream& os, size_t disc, size_t tabs, std::vector& toprint, - const node_ptr& node) const; + std::ostream& print_c_nested(std::ostream& os, size_t disc, size_t tabs, std::vector& toprint, + const NodePtr& node) const; size_t depth() const; double value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const; void consistent(size_t) const; - bool cost_intersect(const node_t& other) const; - double midcost(const node_t& other, double minval, double maxval) const; - bool operator==(const node_t& other) const + bool cost_intersect(const Node& other) const; + double midcost(const Node& other, double minval, double maxval) const; + bool operator==(const Node& other) const { if (_limit != other._limit) return false; @@ -83,7 +83,7 @@ struct SimpleTree::node_t : std::enable_shared_from_this return false; return true; } - bool operator<(const node_t& other) const + bool operator<(const Node& other) const { if (_limit != other._limit) return _limit < other._limit; @@ -93,7 +93,7 @@ struct SimpleTree::node_t : std::enable_shared_from_this return _low < other._low; return _high < other._high; } - node_ptr& operator[](bool b) { return b ? _high : _low; } + NodePtr& operator[](bool b) { return b ? _high : _low; } operator signature_t() const { if (is_leaf()) @@ -134,7 +134,7 @@ SimpleTree SimpleTree::parse(std::istream& input, bool simplify [[maybe_unused]] // store actions SimpleTree tree; if (tree._root == nullptr) { - tree._root = std::make_shared(); + tree._root = std::make_shared(); tree._root->_limit = -std::numeric_limits::infinity(); tree._root->_cost = std::numeric_limits::infinity(); tree._root->_var = std::numeric_limits::max(); @@ -197,7 +197,7 @@ SimpleTree SimpleTree::parse(std::istream& input, bool simplify [[maybe_unused]] throw base_error("Action-index is out of bounds"); if (tree._root == nullptr) { - tree._root = std::make_shared(); + tree._root = std::make_shared(); tree._root->_limit = -std::numeric_limits::infinity(); tree._root->_cost = std::numeric_limits::infinity(); tree._root->_cost = (is_minimize ? 1 : -1) * std::numeric_limits::infinity(); @@ -221,7 +221,7 @@ SimpleTree SimpleTree::parse(std::istream& input, bool simplify [[maybe_unused]] return tree; } -std::pair SimpleTree::node_t::compute_min_max() const +std::pair SimpleTree::Node::compute_min_max() const { double mincost = std::numeric_limits::infinity(); double maxcost = -mincost; @@ -243,7 +243,7 @@ std::pair SimpleTree::node_t::compute_min_max() const return std::make_pair(mincost, maxcost); } -void SimpleTree::node_t::action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid) +void SimpleTree::Node::action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid) { if (_var != varid) { if (!is_leaf() || !(std::isnan(_cost) || std::isinf(_cost))) { @@ -260,7 +260,7 @@ void SimpleTree::node_t::action_nodes(std::vector& nodes, uint32_t low } } -void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& parent) +void SimpleTree::Node::subsumption_reduction(bool minimization, SimpleTree& parent) { if (_var < parent._statevars.size()) { if (_low) @@ -272,11 +272,11 @@ void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& pa return; } size_t prev_vals = std::numeric_limits::max(); - size_t p = 0; + size_t p [[maybe_unused]] = 0; while (true) { ++p; bool outer = false; - std::vector nodes; + std::vector nodes; action_nodes(nodes, 0, parent._actions.size() - 1, parent._statevars.size()); auto val = std::numeric_limits::infinity(); if (!minimization) @@ -305,9 +305,9 @@ void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& pa outer |= n->check_tiles(n.get(), nodes, bounds, val, best, worst, minimization, parent._statevars.size() + 1); } - std::set> values; + std::set> values; { - auto nodes = std::vector(parent._actions.size()); + auto nodes = std::vector(parent._actions.size()); action_nodes(nodes, 0, parent._actions.size() - 1, parent._statevars.size()); for (auto& n : nodes) { if (n == nullptr) @@ -331,7 +331,7 @@ void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& pa } } -void SimpleTree::node_t::set_ranks(std::unordered_map& values) +void SimpleTree::Node::set_ranks(std::unordered_map& values) { if (is_leaf()) { if (std::isinf(_cost) || std::isnan(_cost)) @@ -345,7 +345,7 @@ void SimpleTree::node_t::set_ranks(std::unordered_map& values) } } -void SimpleTree::node_t::get_ranks(std::set>& values, node_t* start) +void SimpleTree::Node::get_ranks(std::set>& values, Node* start) { if (is_leaf()) { if (std::isinf(_cost) || std::isnan(_cost)) @@ -359,10 +359,10 @@ void SimpleTree::node_t::get_ranks(std::set>& values, } } -bool SimpleTree::node_t::subsumes(const std::vector>& bounds, - std::vector>& obounds, const double val, - const bool minimization, size_t offset, double& best, - std::pair& closest) const +bool SimpleTree::Node::subsumes(const std::vector>& bounds, + std::vector>& obounds, const double val, + const bool minimization, size_t offset, double& best, + std::pair& closest) const { if (is_leaf()) { // TODO: continue in other trees here, maybe combination is @@ -410,9 +410,9 @@ bool SimpleTree::node_t::subsumes(const std::vector>& } } -bool SimpleTree::node_t::check_tiles(node_t* start, std::vector& nodes, - std::vector>& bounds, double val, double best_val, - double worst_val, bool minimization, size_t offset) +bool SimpleTree::Node::check_tiles(Node* start, std::vector& nodes, + std::vector>& bounds, double val, double best_val, + double worst_val, bool minimization, size_t offset) { auto obounds = bounds; if (is_leaf()) { @@ -453,7 +453,7 @@ bool SimpleTree::node_t::check_tiles(node_t* start, std::vector& nodes if (!is_leaf()) { double org = _limit; auto& bnd = bounds[_var - offset]; - node_ptr switchnode = nullptr; + NodePtr switchnode = nullptr; if (bnd.second <= _limit) { switchnode = _low; } @@ -506,7 +506,7 @@ bool SimpleTree::node_t::check_tiles(node_t* start, std::vector& nodes // check if one is leaf and sibiling has leaf on same side which // can be merged: double nc = 0; - node_ptr node, other; + NodePtr node, other; if (!_high->is_leaf() && _high->_var == _var && _high->_low->cost_intersect(*_low) && _low->is_leaf() && _high->_low->is_leaf() && std::isinf(_low->_cost) == std::isinf(_high->_low->_cost)) { node = _high; @@ -551,12 +551,12 @@ bool SimpleTree::node_t::check_tiles(node_t* start, std::vector& nodes return false; } -bool SimpleTree::node_t::cost_intersect(const node_t& other) const +bool SimpleTree::Node::cost_intersect(const Node& other) const { return _cost_bounds.first < other._cost_bounds.second && other._cost_bounds.first < _cost_bounds.second; } -double SimpleTree::node_t::midcost(const node_t& other, double minval, double maxval) const +double SimpleTree::Node::midcost(const Node& other, double minval, double maxval) const { if (_cost == other._cost) return _cost; @@ -576,8 +576,8 @@ double SimpleTree::node_t::midcost(const node_t& other, double minval, double ma return c; } -void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, - bool minimize, double accuracy, std::vector& exactness) +void SimpleTree::Node::insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, + bool minimize, double accuracy, std::vector& exactness) { if ((tree.is_number() || tree.is_string()) && key.size() < prefix) { if (is_leaf()) { @@ -603,8 +603,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act if (std::isinf(_limit)) { // assert(std::isinf(_cost)); assert(_var == std::numeric_limits::max()); - _low = std::make_shared(); - _high = std::make_shared(); + _low = std::make_shared(); + _high = std::make_shared(); _low->_cost = _high->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); if (!std::isinf(_cost)) { std::swap(_low->_cost, _cost); @@ -678,8 +678,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act } else { assert(_low->_var > prefix); if (_limit > action) { - auto tmp = std::make_shared(); - tmp->_low = std::make_shared(); + auto tmp = std::make_shared(); + tmp->_low = std::make_shared(); tmp->_cost = tmp->_low->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); tmp->_high = shared_from_this(); tmp->_limit = action; @@ -701,8 +701,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act consistent(key.size() + 1); } else if (_high->_var > prefix) { assert(std::isinf(_high->_cost)); - _high->_low = std::make_shared(); - _high->_high = std::make_shared(); + _high->_low = std::make_shared(); + _high->_high = std::make_shared(); _high->_low->_cost = _high->_high->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); _high->_limit = action; @@ -714,8 +714,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act _high->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); consistent(key.size() + 1); } else if (_high->_var == prefix && _high->_limit > action) { - auto tmp = std::make_shared(); - tmp->_low = std::make_shared(); + auto tmp = std::make_shared(); + tmp->_low = std::make_shared(); tmp->_low->_cost = tmp->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); tmp->_parent = this; tmp->_high = _high; @@ -748,8 +748,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act auto branch = (*this)[b]; if (branch->_var != prefix) { // we need to inject a node here - auto next = std::make_shared(); - (*next)[false] = std::make_shared(); + auto next = std::make_shared(); + (*next)[false] = std::make_shared(); (*next)[false]->_cost = next->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); next->_parent = this; next->_var = prefix; @@ -774,7 +774,7 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act consistent(key.size() + 1); } -void SimpleTree::node_t::consistent(size_t prefix [[maybe_unused]]) const +void SimpleTree::Node::consistent(size_t prefix [[maybe_unused]]) const { return; #ifndef NDEBUG @@ -801,7 +801,7 @@ double SimpleTree::value(const double* disc, const double* cont, uint32_t action return std::numeric_limits::infinity(); } -double SimpleTree::node_t::value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const +double SimpleTree::Node::value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const { if (is_leaf()) return _cost; @@ -820,7 +820,7 @@ double SimpleTree::node_t::value(const double* disc, const double* cont, uint32_ return _high ? _high->value(disc, cont, action, ndisc) : _cost; } -SimpleTree::node_ptr SimpleTree::node_t::simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent) +SimpleTree::NodePtr SimpleTree::Node::simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent) { if (_low) _low = _low->simplify(make_dd, nodemap, parent); @@ -904,7 +904,7 @@ SimpleTree::node_ptr SimpleTree::node_t::simplify(bool make_dd, nodemap_t& nodem return shared_from_this(); } -/*void SimpleTree::node_t::rec_insert(double value, +/*void SimpleTree::Node::rec_insert(double value, std::vector>& handled, std::vector >& bounds, bool minimize) { auto nid = _var; assert(nid < bounds.size()); bool reset_low = false; bool reset_high = false; bool best = @@ -942,7 +942,7 @@ false; if( std::isnan(_cost) || (minimize && value < _cost) || } if(_low == nullptr) { - _low = std::make_shared(); + _low = std::make_shared(); _low->_var = nid; _low->_parent = this; _low->_limit = (!handled[nid].first ? bounds[nid].first : @@ -977,7 +977,7 @@ bounds[nid].second); } if(_high == nullptr) { - _high = std::make_shared(); + _high = std::make_shared(); _high->_parent = this; _high->_var = nid; _high->_limit = (!handled[nid].first ? bounds[nid].first : @@ -1000,7 +1000,7 @@ handled[_var].first = false; if(reset_high) handled[_var].second = false; } if(_low == nullptr) { - _low = std::make_shared(); + _low = std::make_shared(); _low->_var = nid; _low->_parent = this; _low->_limit = (!handled[nid].first ? bounds[nid].first : @@ -1008,7 +1008,7 @@ bounds[nid].second); } if(_high == nullptr) { - _high = std::make_shared(); + _high = std::make_shared(); _high->_parent = this; _high->_var = nid; _high->_limit = (!handled[nid].first ? bounds[nid].first : @@ -1028,7 +1028,7 @@ std::ostream& SimpleTree::print(std::ostream& stream) const return stream; } -std::ostream& SimpleTree::node_t::print(std::ostream& out, size_t tabs) const +std::ostream& SimpleTree::Node::print(std::ostream& out, size_t tabs) const { for (size_t i = 0; i < tabs; ++i) out << "\t"; @@ -1064,8 +1064,8 @@ std::ostream& SimpleTree::print_c(std::ostream& os, const std::string& name) con os << "\treturn 0 ; \n}\n"; return os; } - auto _idnode = std::unordered_map{}; - auto _nodeid = std::unordered_map{}; + auto _idnode = std::unordered_map{}; + auto _nodeid = std::unordered_map{}; _idnode[0] = _root.get(); _nodeid[_root.get()] = 0; @@ -1126,8 +1126,8 @@ std::ostream& SimpleTree::print_c(std::ostream& os, const std::string& name) con os << "\t// Disc = " << _statevars.size() << std::endl; os << "\t// Cont = " << _pointvars.size() << std::endl; os << "\t// Nodes = " << lid << std::endl; - /*std::unordered_set printed; - std::vector toprint; + /*std::unordered_set printed; + std::vector toprint; if(_root) _root->print_c_nested(stream, _statevars.size(), 1, toprint, _root); auto mm = _root->compute_min_max(); @@ -1158,7 +1158,7 @@ std::ostream& SimpleTree::print_c(std::ostream& os, const std::string& name) con return os; } -size_t SimpleTree::node_t::depth() const +size_t SimpleTree::Node::depth() const { if (_low && _high == nullptr) return 1 + _low->depth(); @@ -1169,15 +1169,15 @@ size_t SimpleTree::node_t::depth() const return 1 + std::max(_low->depth(), _high->depth()); } -std::ostream& SimpleTree::node_t::print_c(std::ostream& os, size_t disc, std::unordered_set& printed, - size_t tabs) const +std::ostream& SimpleTree::Node::print_c(std::ostream& os, size_t disc, std::unordered_set& printed, + size_t tabs) const { if (printed.count(this) > 0) return os; printed.insert(this); // for(size_t i = 0; i < tabs; ++i) stream << "\t"; os << "l" << this << ":\n"; - std::vector toprint; + std::vector toprint; if (is_leaf()) { // for(size_t i = 0; i < tabs+1; ++i) stream << "\t"; if (!std::isinf(_cost)) @@ -1226,8 +1226,8 @@ std::ostream& SimpleTree::node_t::print_c(std::ostream& os, size_t disc, std::un return os; } -std::ostream& SimpleTree::node_t::print_c_nested(std::ostream& os, size_t disc, size_t tabs, - std::vector& toprint, const node_ptr& node) const +std::ostream& SimpleTree::Node::print_c_nested(std::ostream& os, size_t disc, size_t tabs, + std::vector& toprint, const NodePtr& node) const { assert(node.get() == this); if (is_leaf() && std::isinf(_limit)) { diff --git a/src/SimpleTree.h b/src/SimpleTree.h index 64d490c..fb0a37d 100644 --- a/src/SimpleTree.h +++ b/src/SimpleTree.h @@ -52,24 +52,24 @@ class SimpleTree const std::vector& continous_features() const { return _pointvars; } private: - struct node_t; - using node_ptr = std::shared_ptr; + struct Node; + using NodePtr = std::shared_ptr; struct signature_t { uint32_t _var{0}; double _limit{0}; - node_t* _low{nullptr}; - node_t* _high{nullptr}; + Node* _low{nullptr}; + Node* _high{nullptr}; } __attribute__((packed)); friend struct ptrie::byte_iterator; - using nodemap_t = ptrie::map; + using nodemap_t = ptrie::map; SimpleTree() = default; std::vector _actions; std::vector _statevars; std::vector _pointvars; - node_ptr _root; + NodePtr _root; bool _is_minimization = true; }; diff --git a/src/ZonotopStrategy.cpp b/src/ZonotopStrategy.cpp index 22fc42b..b6b8107 100644 --- a/src/ZonotopStrategy.cpp +++ b/src/ZonotopStrategy.cpp @@ -118,7 +118,7 @@ ZonotopStrategy ZonotopStrategy::parse(std::istream& input) bool ZonotopStrategy::add(std::vector& bounds, size_t state) { if (_root == nullptr) { - _root = std::make_shared(); + _root = std::make_shared(); _root->_varid = 0; _root->_limit = bounds[0]._lower; } @@ -126,8 +126,8 @@ bool ZonotopStrategy::add(std::vector& bounds, size_t state) return rec_insert(_root.get(), bounds, handled, state); } -bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, - std::vector>& handled, size_t state) +bool ZonotopStrategy::rec_insert(Node* node, std::vector& bounds, std::vector>& handled, + size_t state) { auto nid = node->_varid; assert(nid < bounds.size()); @@ -152,7 +152,7 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, } if (node->_low == nullptr) { - node->_low = std::make_shared(); + node->_low = std::make_shared(); node->_low->_varid = nid; node->_low->_parent = node; node->_low->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); @@ -179,7 +179,7 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, } if (node->_high == nullptr) { - node->_high = std::make_shared(); + node->_high = std::make_shared(); node->_high->_parent = node; node->_high->_varid = nid; node->_high->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); @@ -189,13 +189,13 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, handled[node->_varid].first = false; } else { if (node->_low == nullptr) { - node->_low = std::make_shared(); + node->_low = std::make_shared(); node->_low->_varid = nid; node->_low->_parent = node; node->_low->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); } if (node->_high == nullptr) { - node->_high = std::make_shared(); + node->_high = std::make_shared(); node->_high->_parent = node; node->_high->_varid = nid; node->_high->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); @@ -207,7 +207,7 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, return col; } -void ZonotopStrategy::try_merge(node_t* node, size_t state) +void ZonotopStrategy::try_merge(Node* node, size_t state) { if (node == nullptr) return; @@ -234,7 +234,7 @@ void ZonotopStrategy::try_merge(node_t* node, size_t state) } } -std::ostream& ZonotopStrategy::node_t::print(std::ostream& os, const ZonotopStrategy* parent, size_t tabs) const +std::ostream& ZonotopStrategy::Node::print(std::ostream& os, const ZonotopStrategy* parent, size_t tabs) const { os << Tabs{tabs}; os << "{\"var\":" << _varid << ",\"bound\":" << _limit; @@ -365,7 +365,7 @@ double ZonotopStrategy::get_max(size_t dimen) const { return _root->get_max(dime double ZonotopStrategy::get_min(size_t dimen) const { return _root->get_min(dimen); } -double ZonotopStrategy::node_t::get_max(size_t dimen) const +double ZonotopStrategy::Node::get_max(size_t dimen) const { double maxval = _varid == dimen ? _limit : -std::numeric_limits::infinity(); if (_high) @@ -375,7 +375,7 @@ double ZonotopStrategy::node_t::get_max(size_t dimen) const return maxval; } -double ZonotopStrategy::node_t::get_min(size_t dimen) const +double ZonotopStrategy::Node::get_min(size_t dimen) const { double minval = _varid == dimen ? _limit : std::numeric_limits::infinity(); if (_high) @@ -420,7 +420,7 @@ std::ostream& ZonotopStrategy::print_c(std::ostream& stream, std::string functio return stream; } -std::ostream& ZonotopStrategy::node_t::print_c(std::ostream& os, size_t tabs) const +std::ostream& ZonotopStrategy::Node::print_c(std::ostream& os, size_t tabs) const { if (_low != nullptr || !_low_patterns.empty()) { os << Tabs{tabs}; diff --git a/src/ZonotopStrategy.h b/src/ZonotopStrategy.h index b6dd261..a11303a 100644 --- a/src/ZonotopStrategy.h +++ b/src/ZonotopStrategy.h @@ -35,6 +35,9 @@ class ZonotopStrategy { public: + ~ZonotopStrategy() = default; + ZonotopStrategy(const ZonotopStrategy&) = delete; + ZonotopStrategy& operator=(const ZonotopStrategy&) = delete; ZonotopStrategy(ZonotopStrategy&&) = default; ZonotopStrategy& operator=(ZonotopStrategy&&) = default; static ZonotopStrategy parse(std::istream&); @@ -51,23 +54,22 @@ class ZonotopStrategy ZonotopStrategy() = default; ptrie::set_stable<> _states; size_t _max_length = 0; - struct node_t; - using node_ptr = std::shared_ptr; - struct node_t : std::enable_shared_from_this + struct Node : std::enable_shared_from_this { + using Ptr = std::shared_ptr; uint32_t _varid = 0; double _limit = 0; - node_ptr _low = nullptr; - node_ptr _high = nullptr; + Ptr _low = nullptr; + Ptr _high = nullptr; std::vector _low_patterns; std::vector _high_patterns; - node_t* _parent{nullptr}; + Node* _parent{nullptr}; std::ostream& print(std::ostream& os, const ZonotopStrategy* parent, size_t tabs = 0) const; std::ostream& print_c(std::ostream& os, size_t tabs = 0) const; double get_min(size_t dimen) const; double get_max(size_t dimen) const; }; - node_ptr _root; + Node::Ptr _root; struct bound_t { double _lower = 0; @@ -77,9 +79,9 @@ class ZonotopStrategy }; static std::vector get_bounds(const std::string& zonotop, size_t& vars); bool add(std::vector& bounds, size_t state); - bool rec_insert(node_t* node, std::vector& bounds, std::vector>& handled, + bool rec_insert(Node* node, std::vector& bounds, std::vector>& handled, size_t state); - void try_merge(node_t* node, size_t state); + void try_merge(Node* node, size_t state); }; #endif /* ZONOTOPSTRATEGY_H */ diff --git a/src/libz2s.cpp b/src/libz2s.cpp index f2a3174..f80117c 100644 --- a/src/libz2s.cpp +++ b/src/libz2s.cpp @@ -1,16 +1,16 @@ /* * Copyright (C) 2020 Peter G. Jensen - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ @@ -24,16 +24,14 @@ void* parse_strategy(const char* file) { - if(file == nullptr) - { + if (file == nullptr) { std::cerr << "Input file must be specified" << std::endl; exit(-1); return nullptr; } std::string fn(file); std::ifstream instream(fn); - if(instream.fail()) - { + if (instream.fail()) { std::cerr << "Could not open infile for reading : " << fn << std::endl; exit(-1); return nullptr; @@ -47,13 +45,13 @@ void* parse_strategy(const char* file) void destroy_strategy(void* ptr) { std::cerr << "destroy " << ptr << std::endl; - delete (ZonotopStrategy*)ptr; + delete static_cast(ptr); } int num_patterns(void* ptr) { - std::cerr << "num_patterns " << ptr << std::endl; - auto pl = ((ZonotopStrategy*)ptr)->num_patterns(); + std::cerr << "num_patterns " << ptr << std::endl; + auto pl = static_cast(ptr)->num_patterns(); std::cerr << pl << std::endl; return pl; } @@ -61,50 +59,47 @@ int num_patterns(void* ptr) int max_pattern_length(void* ptr) { std::cerr << "max_pattern_length " << ptr << std::endl; - auto pl = ((ZonotopStrategy*)ptr)->max_pattern_length(); + auto pl = static_cast(ptr)->max_pattern_length(); std::cerr << pl << std::endl; return pl; } void active(void* ptr, const double* sample, bool* write) { -// std::cerr << "active " << ptr << std::endl; - ((ZonotopStrategy*)ptr)->active(sample, write); + // std::cerr << "active " << ptr << std::endl; + static_cast(ptr)->active(sample, write); } int get_pattern(void* ptr, int el, int* write) { -// std::cerr << "get_pattern " << ptr << std::endl; - return ((ZonotopStrategy*)ptr)->get_pattern(el, write); + // std::cerr << "get_pattern " << ptr << std::endl; + return static_cast(ptr)->get_pattern(el, write); } double get_min(void* ptr, int dimen) { - auto val = ((ZonotopStrategy*)ptr)->get_min(dimen); -std::cerr << "min for " << dimen << " is " << val << std::endl; + auto val = static_cast(ptr)->get_min(dimen); + std::cerr << "min for " << dimen << " is " << val << std::endl; return val; } double get_max(void* ptr, int dimen) { - auto val = ((ZonotopStrategy*)ptr)->get_max(dimen); - std::cerr << "max for " << dimen << " is " << val << std::endl; - return val; + auto val = static_cast(ptr)->get_max(dimen); + std::cerr << "max for " << dimen << " is " << val << std::endl; + return val; } - void* parse_learned(const char* file) { - if(file == nullptr) - { + if (file == nullptr) { std::cerr << "Input file must be specified" << std::endl; exit(-1); return nullptr; } std::string fn(file); std::ifstream instream(fn); - if(instream.fail()) - { + if (instream.fail()) { std::cerr << "Could not open infile for reading : " << fn << std::endl; exit(-1); return nullptr; @@ -115,12 +110,9 @@ void* parse_learned(const char* file) return res; } -void destroy_learned(void* ptr) -{ - delete (SimpleTree*)ptr; -} +void destroy_learned(void* ptr) { delete static_cast(ptr); } double weight(void* ptr, const double* disc, const double* cont, int a) { - return ((SimpleTree*)ptr)->value(disc, cont, a); -} \ No newline at end of file + return static_cast(ptr)->value(disc, cont, a); +} From 794013482ee61220957ad453ff36cc7755ad8776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 1 Jul 2026 14:14:53 +0000 Subject: [PATCH 17/17] Fix to_double for AppleClang missing floating-point std::from_chars AppleClang's libc++ doesn't implement the double overload of std::from_chars yet, so gate it behind __cpp_lib_to_chars and fall back to std::stod otherwise. Co-Authored-By: Claude Sonnet 5 --- src/main.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 8796460..89b5225 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -55,6 +55,7 @@ bool is_option(std::string_view s) return s.size() > 1 && s[0] == '-' && !(std::isdigit(static_cast(s[1])) || s[1] == '.'); } +#if defined(__cpp_lib_to_chars) double to_double(std::string_view s) { double value; @@ -65,6 +66,26 @@ double to_double(std::string_view s) } return value; } +#else +// AppleClang's libc++ does not implement the floating-point std::from_chars +// overloads yet, so fall back to std::stod there. +double to_double(std::string_view s) +{ + std::string str(s); + size_t pos = 0; + double value = 0; + try { + value = std::stod(str, &pos); + } catch (...) { + pos = 0; + } + if (pos != str.size()) { + std::cerr << "Invalid number: " << s << std::endl; + std::exit(-1); + } + return value; +} +#endif } // namespace