From 8f00d5f23901146f858bc137ca5d818a8f2989e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Fri, 13 Mar 2026 17:00:53 +0100 Subject: [PATCH 01/12] Restructured files, updated the libraries and refactored error checking and logging --- .clangd | 2 + CMakeLists.txt | 8 +- cmake/doctest.cmake | 45 ++- cmake/stdcpp.cmake | 11 +- table_input.csv => examples/table_input.csv | 0 {src => include}/csvtable.hpp | 36 +- {src => include}/dynlib.h | 0 {src => include}/library.hpp | 31 +- src/errors.hpp => include/logging.h | 11 +- src/CMakeLists.txt | 23 +- src/{errors.cpp => logging.cpp} | 9 +- src/table.cpp | 343 ++++++++++-------- tests/CMakeLists.txt | 12 + src/test_errors.cpp => tests/logging_test.cpp | 15 +- src/test_table.cpp => tests/table_test.cpp | 64 +++- 15 files changed, 340 insertions(+), 270 deletions(-) create mode 100644 .clangd rename table_input.csv => examples/table_input.csv (100%) rename {src => include}/csvtable.hpp (72%) rename {src => include}/dynlib.h (100%) rename {src => include}/library.hpp (78%) rename src/errors.hpp => include/logging.h (74%) rename src/{errors.cpp => logging.cpp} (90%) create mode 100644 tests/CMakeLists.txt rename src/test_errors.cpp => tests/logging_test.cpp (70%) rename src/test_table.cpp => tests/table_test.cpp (65%) diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..ef516ad --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: -ferror-limit=0 diff --git a/CMakeLists.txt b/CMakeLists.txt index b53d656..9c0343c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,11 +5,13 @@ project(uppaal_libs) include(cmake/stdcpp.cmake) include(cmake/sanitizers.cmake) -option(UPPAALLIBS_WITH_TESTS "UPPAAL LIBS Unit Tests" ON) +option(UPPAALLIBS_WITH_TESTS "UPPAAL LIBS Unit Tests" ${PROJECT_IS_TOP_LEVEL}) + +include_directories(include) +add_subdirectory(src) if (UPPAALLIBS_WITH_TESTS) include(cmake/doctest.cmake) enable_testing() + add_subdirectory(tests) endif (UPPAALLIBS_WITH_TESTS) - -add_subdirectory(src) diff --git a/cmake/doctest.cmake b/cmake/doctest.cmake index 6c410c1..83c8e58 100644 --- a/cmake/doctest.cmake +++ b/cmake/doctest.cmake @@ -1,17 +1,46 @@ # Downloads and compiles DocTest unit testing framework include(FetchContent) -set(FETCHCONTENT_QUIET ON) -set(FETCHCONTENT_UPDATES_DISCONNECTED ON) +#set(FETCHCONTENT_QUIET ON) +#set(FETCHCONTENT_UPDATES_DISCONNECTED ON) -set(DOCTEST_WITH_TESTS OFF CACHE BOOL "doctest tests and examples") -set(DOCTEST_WITH_MAIN_IN_STATIC_LIB ON CACHE BOOL "enable 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") +set(CMAKE_POLICY_VERSION_MINIMUM 3.10) # turn off the CMake warnings FetchContent_Declare(doctest GIT_REPOSITORY https://github.com/doctest/doctest.git - GIT_TAG v2.4.11 # "main" for latest + GIT_TAG v2.4.12 # "main" for latest 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 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 () + +macro(add_debug_test TEST_NAME) + get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(isMultiConfig) + add_test(NAME ${TEST_NAME} COMMAND ${ARGN} CONFIGURATIONS Debug RelWithDebInfo) + else() + if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + add_test(NAME ${TEST_NAME} COMMAND ${ARGN}) + endif() + endif() +endmacro() diff --git a/cmake/stdcpp.cmake b/cmake/stdcpp.cmake index f05c045..12d785a 100644 --- a/cmake/stdcpp.cmake +++ b/cmake/stdcpp.cmake @@ -5,14 +5,11 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON) # -fPIC set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # for clang-tidy -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - message(STATUS "Extra warnings for ${CMAKE_CXX_COMPILER_ID} compiler") - add_compile_options(-Wpedantic -Wall -Wextra -Wno-cast-function-type) -elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - message(STATUS "Extra warnings for ${CMAKE_CXX_COMPILER_ID} compiler") - add_compile_options(-Wpedantic -Wall -Wextra -Wno-cast-function-type) +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(STATUS "Enabled extra warnings for ${CMAKE_CXX_COMPILER}") + add_compile_options(-Wpedantic -Wall -Wextra -Wconversion -Wno-cast-function-type) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - message(STATUS "Extra warnings for ${CMAKE_CXX_COMPILER_ID} compiler") + message(STATUS "Enabled extra warnings for ${CMAKE_CXX_COMPILER}") add_compile_options( /permissive- # strict standards /Wall -wd4191 -wd4668 -wd4710 -wd4711 -wd5045) diff --git a/table_input.csv b/examples/table_input.csv similarity index 100% rename from table_input.csv rename to examples/table_input.csv diff --git a/src/csvtable.hpp b/include/csvtable.hpp similarity index 72% rename from src/csvtable.hpp rename to include/csvtable.hpp index 8b4b3b4..a878a51 100644 --- a/src/csvtable.hpp +++ b/include/csvtable.hpp @@ -10,10 +10,10 @@ #include #include -using elem_t = double; -using row_t = std::vector; -using table_t = std::vector; -using dictionary_t = std::unordered_map>; +using Elem = double; +using Row = std::vector; +using Table = std::vector; +using Dictionary = std::unordered_map>; inline void skip_line(std::istream& is) { @@ -28,14 +28,14 @@ inline void skip_comments(std::istream& is) skip_line(is); } -[[nodiscard]] table_t table_read_csv(std::istream& is, int skip_lines) +[[nodiscard]] Table table_read_csv(std::istream& is, int skip_lines) { auto sep = char{}; while (is && skip_lines-- > 0) skip_line(is); skip_comments(is); - auto table = table_t{}; - auto elem = elem_t{}; + auto table = Table{}; + auto elem = Elem{}; while (is >> elem) { auto& row = table.emplace_back(); row.push_back(elem); @@ -49,10 +49,10 @@ inline void skip_comments(std::istream& is) return table; } -[[nodiscard]] dictionary_t dictionary_read_csv(std::istream& is) +[[nodiscard]] Dictionary dictionary_read_csv(std::istream& is) { - auto dictionary = dictionary_t{}; - auto elem = elem_t{}; + auto dictionary = Dictionary{}; + auto elem = Elem{}; auto sep = char{}; while (is >> elem) { auto& row = dictionary[elem]; @@ -63,20 +63,20 @@ inline void skip_comments(std::istream& is) return dictionary; } -[[nodiscard]] double interpolate(const table_t& table, const elem_t key, int key_column, +[[nodiscard]] Elem interpolate(const Table& table, const Elem key, int key_column, int value_column) { using namespace std::string_literals; if (key_column < 0) - throw std::runtime_error("negative key column"); + throw std::logic_error{"negative key column"}; if (key_column >= static_cast(table.front().size())) - throw std::runtime_error("key column overflow"); + throw std::logic_error{"key column overflow"}; if (value_column < 0) - throw std::runtime_error("negative value column"); + throw std::logic_error{"negative value column"}; if (value_column >= static_cast(table.front().size())) - throw std::runtime_error("value column overflow"); + throw std::logic_error{"value column overflow"}; auto it2 = std::lower_bound(std::begin(table), std::end(table), key, - [=](const row_t& row, const elem_t& key) { + [=](const Row& row, const Elem& key) { return row[static_cast(key_column)] < key; }); if (it2 == std::end(table)) @@ -96,7 +96,7 @@ inline void skip_comments(std::istream& is) return y1 + (y2 - y1) / (x2 - x1) * (key - x1); // linear interpolation } -std::ostream& table_write_csv(std::ostream& os, const table_t& table, const char sep = ',') +std::ostream& table_write_csv(std::ostream& os, const Table& table, const char sep = ',') { for (auto& row : table) { auto b = std::begin(row), e = std::end(row); @@ -110,7 +110,7 @@ std::ostream& table_write_csv(std::ostream& os, const table_t& table, const char return os; } -std::ostream& dictionary_write_csv(std::ostream& os, const dictionary_t& dictionary, +std::ostream& dictionary_write_csv(std::ostream& os, const Dictionary& dictionary, const char sep = ',') { for (auto& row : dictionary) { diff --git a/src/dynlib.h b/include/dynlib.h similarity index 100% rename from src/dynlib.h rename to include/dynlib.h diff --git a/src/library.hpp b/include/library.hpp similarity index 78% rename from src/library.hpp rename to include/library.hpp index b5c9c1e..e4cb4d5 100644 --- a/src/library.hpp +++ b/include/library.hpp @@ -13,11 +13,8 @@ /** Wrapper for opening Library files. * Methods may throw runtime_error upon errors. */ -class Library +struct Library { - void* handle; // library handle - -public: Library(const char* filepath): handle{dlopen(filepath, RTLD_LAZY | RTLD_LOCAL)} { if (!handle) @@ -35,26 +32,24 @@ class Library * auto fn = lib.lookup(fn_name); * fn(arg1, arg2); * Where fn_type ::= return_type (*)(arg1_type, arg2_type); - * */ + */ template FnType lookup(const char* fn_name) { - auto res = (FnType)(dlsym(handle, fn_name)); - if (res == nullptr) - throw std::runtime_error(dlerror()); - return res; + if (auto res = reinterpret_cast(dlsym(handle, fn_name)); res) + return res; + throw std::runtime_error{dlerror()}; } +private: + void* handle{}; ///< library handle }; #elif defined(_WIN32) || defined(__MINGW32__) #include #include -class Library +struct Library { - HMODULE handle; // library handle - -public: Library(const char* filepath): handle{LoadLibrary(TEXT(filepath))} { if (!handle) { @@ -78,12 +73,12 @@ class Library template FnType lookup(const char* fn_name) { - auto res = reinterpret_cast(GetProcAddress(handle, fn_name)); - if (res == nullptr) - throw std::runtime_error{"Failed symbol lookup with error " + - std::to_string(GetLastError())}; - return res; + if (auto res = reinterpret_cast(GetProcAddress(handle, fn_name)); res != nullptr) + return res; + throw std::runtime_error{"Failed symbol lookup: " + std::to_string(GetLastError())}; } +private: + HMODULE handle{}; ///< library handle }; #else diff --git a/src/errors.hpp b/include/logging.h similarity index 74% rename from src/errors.hpp rename to include/logging.h index 99c4ca6..5270256 100644 --- a/src/errors.hpp +++ b/include/logging.h @@ -1,10 +1,10 @@ /** - * Error reporting + * Error logging * Author: Marius Mikucionis */ -#ifndef _ERRORS_HPP_ -#define _ERRORS_HPP_ +#ifndef INCLUDE_LOGGING_H +#define INCLUDE_LOGGING_H #include "dynlib.h" @@ -19,10 +19,11 @@ #define log_err(format, ...) #endif // NDEBUG -/** Set the file path for errors, returns 0 always */ +/// Set the file path for errors, returns 0 always C_PUBLIC int set_error_path(const char* err_path); +/// Returns the path to the log file, "error.log" by default C_PUBLIC const char* get_error_path(); void log_error(const char* function, const char* path, int line, const char* format, ...); -#endif /* _ERRORS_HPP_ */ +#endif // INCLUDE_LOGGING_H \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 76e6d0c..bb0de8e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,23 +1,4 @@ -add_custom_target(data - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/table_input.csv ${CMAKE_CURRENT_BINARY_DIR}/table_input.csv - DEPENDS ${PROJECT_SOURCE_DIR}/table_input.csv - BYPRODUCTS table_input.csv) - -add_library(errors OBJECT errors.cpp) +add_library(logging OBJECT logging.cpp) add_library(table SHARED table.cpp) -target_link_libraries(table PRIVATE errors) -add_dependencies(table data) - -if (UPPAALLIBS_WITH_TESTS) - add_executable(test_table test_table.cpp) - target_link_libraries(test_table PRIVATE doctest::doctest_with_main) - add_dependencies(test_table table) - add_test(NAME test_table COMMAND test_table) - - if (CMAKE_BUILD_TYPE STREQUAL "Debug") - add_executable(test_errors test_errors.cpp) - target_link_libraries(test_errors PRIVATE errors doctest::doctest_with_main) - add_test(NAME test_errors COMMAND test_errors) - endif() -endif (UPPAALLIBS_WITH_TESTS) \ No newline at end of file +target_link_libraries(table PRIVATE logging) diff --git a/src/errors.cpp b/src/logging.cpp similarity index 90% rename from src/errors.cpp rename to src/logging.cpp index 513de6e..4546f16 100644 --- a/src/errors.cpp +++ b/src/logging.cpp @@ -2,7 +2,7 @@ * Error reporting * Author: Marius Mikucionis */ -#include "errors.hpp" +#include "logging.h" #include #include @@ -22,7 +22,7 @@ C_PUBLIC int set_error_path(const char* path) C_PUBLIC const char* get_error_path() { return error_path.c_str(); } -FILE* open_error_file() +static FILE* open_log_file() { const auto path = get_error_path(); FILE* file = nullptr; @@ -54,8 +54,9 @@ FILE* open_error_file() void log_error(const char* function, const char* path, int line, const char* format, ...) { - auto file = open_error_file(); - const auto time = std::chrono::system_clock::now().time_since_epoch(); + auto file = open_log_file(); + using Clock = std::chrono::system_clock; + const auto time = Clock::now().time_since_epoch(); const auto sec = std::chrono::duration_cast(time); const auto usec = std::chrono::duration_cast(time - sec); #if (defined(__APPLE__) && defined(__MACH__)) || defined(_WIN32) diff --git a/src/table.cpp b/src/table.cpp index a367b25..60864f5 100644 --- a/src/table.cpp +++ b/src/table.cpp @@ -3,37 +3,60 @@ * Author: Marius Mikucionis */ #include "csvtable.hpp" -#include "errors.hpp" +#include "logging.h" #include "dynlib.h" #include #include // to_string/MSVC +#include + #include // nan +/// creates a new table rows x cols and populates with zeros +C_PUBLIC int table_new(int rows, int cols); +/// creates a new table rows x cols and populates with integer value C_PUBLIC int table_new_int(int rows, int cols, int value); +/// creates a new table rows x cols and populates with floating point value C_PUBLIC int table_new_double(int rows, int cols, double value); +/// Resizes the table to a given rows x cols size. Returns id on success +C_PUBLIC int table_resize(int id, int rows, int cols); +/// Resizes the table to a given rows x cols size. Returns id on success C_PUBLIC int table_resize_double(int id, int rows, int cols, double value); +/// Resizes the table to a given rows x cols size. Returns id on success C_PUBLIC int table_resize_int(int id, int rows, int cols, int value); +/// Loads the table from CSV file, returns the table id, or -1 on error C_PUBLIC int table_read_csv(const char* csv_path, int skip_lines); +/// Writes the table to CSV file, returns the number of rows, or -1 on error C_PUBLIC int table_write_csv(int id, const char* csv_path); +/// Creates an independent copy of table with id and returns the ID of the new table C_PUBLIC int table_copy(int id); +/// Clears selected table to 0x0 and releases associated resources C_PUBLIC int table_clear(int id); +/// Returns the number of rows in the table with given id C_PUBLIC int table_rows(int id); +/// Returns the number of columns in the table with given id C_PUBLIC int table_cols(int id); +/// Returns an integer value stored at given row and column of table with given id C_PUBLIC int read_int(int id, int row, int col); +/// Returns a floating point value stored at given row and column of table with given id C_PUBLIC double read_double(int id, int row, int col); -C_PUBLIC void write_int(int id, int row, int col, int value); -C_PUBLIC void write_double(int id, int row, int col, double value); -C_PUBLIC double interpolate(int id, double key, int key_col, int valu_col); -C_PUBLIC void read_int_col(int id, int row, int col, int* items, int offset, int count); -C_PUBLIC void read_int_row(int id, int row, int col, int* items, int offset, int count); +/// Writes an integer value into given table at given row and column +C_PUBLIC int write_int(int id, int row, int col, int value); +/// Writes a floating point value into given table at given row and column +C_PUBLIC int write_double(int id, int row, int col, double value); +/// Maps key from key column to value column by using interpolation +C_PUBLIC double interpolate(int id, double key, int key_col, int value_col); +/// Copies column of count integers starting with given row and col into items starting with offset. +C_PUBLIC int read_int_col(int id, int row, int col, int* items, int offset, int count); +/// Copies row of count integers starting with given row and col into items starting with offset. +C_PUBLIC int read_int_row(int id, int row, int col, int* items, int offset, int count); using namespace std::string_literals; -static std::vector tables{}; +static std::vector tables{}; -C_PUBLIC int table_new_int(int rows, int cols, int value) +C_PUBLIC int table_new_double(int rows, int cols, double value) { - log_err("table_new(%d, %d, %d)", rows, cols, value); + log_err("table_new(%d, %d, %f)", rows, cols, value); auto& t = tables.emplace_back(); t.resize(static_cast(rows)); for (auto& row : t) @@ -43,19 +66,17 @@ C_PUBLIC int table_new_int(int rows, int cols, int value) return res; } -C_PUBLIC int table_new_double(int rows, int cols, double value) +C_PUBLIC int table_new(int rows, int cols) { - log_err("table_new(%d, %d, %f)", rows, cols, value); - auto& t = tables.emplace_back(); - t.resize(static_cast(rows)); - for (auto& row : t) - row.resize(static_cast(cols), value); - const auto res = static_cast(tables.size()) - 1; - log_err("table_new: ", res); - return res; + return table_new_double(rows, cols, 0.0); +} + +C_PUBLIC int table_new_int(int rows, int cols, int value) +{ + return table_new_double(rows, cols, value); } -static table_t load(const std::string& path, int skip_lines) +static Table load(const std::string& path, int skip_lines) { #ifdef ENABLE_CSV_CACHE static auto cache = std::unordered_map{}; @@ -83,7 +104,6 @@ static table_t load(const std::string& path, int skip_lines) #endif } -/** loads the table from CSV file, returns the table id, or -1 on error */ C_PUBLIC int table_read_csv(const char* csv_path, int skip_lines) { log_err("table_read_csv(%s, %d)", csv_path, skip_lines); @@ -93,109 +113,110 @@ C_PUBLIC int table_read_csv(const char* csv_path, int skip_lines) return res; } -/** writes the table to CSV file, returns the number of rows, or -1 on error */ +static void validate_table_id(int id) +{ + if (id < 0) + throw std::underflow_error{std::format("table id is too low: {}", id)}; + if (id >= static_cast(tables.size())) + throw std::overflow_error{std::format("table id is too high: {}", id)}; +} + +static Table& get_table(int id) +{ + validate_table_id(id); + return tables[static_cast(id)]; +} + C_PUBLIC int table_write_csv(const int id, const char* csv_path) { - log_err("table_write_csv(%d, %s)", id, csv_path); - if (id < 0) { - log_err("table id is too low: %d", id); - return -1; - } - if (id >= static_cast(tables.size())) { - log_err("table id is too high: %d", id); - return -1; - } - auto os = std::ofstream{csv_path}; - if (!os) { - log_err("failed to write: %s", csv_path); - return -1; + try { + log_err("table_write_csv(%d, %s)", id, csv_path); + const auto& table = get_table(id); + auto os = std::ofstream{csv_path}; + if (!table_write_csv(os, table, ',')) + throw std::runtime_error{std::format("failed to write file: {}", csv_path)}; + const auto rows = table.size(); + log_err("table_write_csv: %zu rows", rows); + return static_cast(rows); + } catch (const std::exception& e) { + log_err("%s", e.what()); } - table_write_csv(os, tables[static_cast(id)], ','); - auto res = static_cast(tables[static_cast(id)].size()); - log_err("table_write_csv: %d (rows)", res); - return res; + return -1; } C_PUBLIC int table_copy(const int id) { - log_err("table_copy(%d)", id); - if (id < 0) { - log_err("table id is too low: %d", id); - return -1; - } - if (id >= static_cast(tables.size())) { - log_err("table id is too high: %d", id); - return -1; + try { + log_err("table_copy(%d)", id); + const auto& table = get_table(id); + const auto res = static_cast(tables.size()); + tables.push_back(table); + log_err("table_copy: %d (id)", res); + return res; + } catch (const std::exception& e) { + log_err("%s", e.what()); } - tables.push_back(tables[static_cast(id)]); - auto res = static_cast(tables.size()) - 1; - log_err("table_copy: %d (id)", res); - return res; + return -1; } C_PUBLIC int table_clear(int id) { - log_err("table_clear(%d)", id); - if (id < 0) { - log_err("table id is too low: %d", id); - return -1; - } - if (id >= static_cast(tables.size())) { - log_err("table id is too high: %d", id); - return -1; + try { + log_err("table_clear(%d)", id); + auto& table = get_table(id); + table.clear(); + table.shrink_to_fit(); + log_err("table_clear: %d (id)", id); + return id; + } catch (const std::exception& e) { + log_err("%s", e.what()); } - tables[static_cast(id)].clear(); - tables[static_cast(id)].shrink_to_fit(); - log_err("table_clear: %d (id)", id); - return id; + return -1; } /** User function: get the number of rows in the table */ C_PUBLIC int table_rows(const int id) { - log_err("table_rows(%d)", id); - if (id < 0) { - log_err("table id is too low: %d", id); - return -1; - } - if (id >= static_cast(tables.size())) { - log_err("table id is too high: %d", id); - return -1; + try { + log_err("table_rows(%d)", id); + const auto& table = get_table(id); + const auto res = table.size(); + log_err("table_rows: %zu (rows)", res); + return static_cast(res); + } catch (const std::exception& e) { + log_err("%s", e.what()); } - auto res = static_cast(tables[static_cast(id)].size()); - log_err("table_rows: %d (rows)", res); - return res; + return -1; } /** User function: get the number of columns in the first table row. * Note that some rows may have fewer or more columns (depends on the source of data). */ C_PUBLIC int table_cols(const int id) { - log_err("table_cols(%d)", id); - if (id < 0) { - log_err("table id is too low: %d", id); - return -1; - } - if (id >= static_cast(tables.size())) { - log_err("table id is too high: %d", id); - return -1; - } - if (tables[static_cast(id)].empty()) { - log_err("%s", "table is empty"); - return 0; + try { + log_err("table_cols(%d)", id); + const auto& table = get_table(id); + if (table.empty()) { + log_err("%s", "table is empty"); + return 0; + } + const auto res = table.front().size(); + log_err("table_rows: %zu (cols)", res); + return static_cast(res); + } catch (const std::exception& e) { + log_err("%s", e.what()); } - const auto res = static_cast(tables[static_cast(id)].front().size()); - log_err("table_rows: %d (cols)", res); - return res; + return -1; } -static table_t& get_table(int id) +static auto& get_table_row(int id, int row) { - if (id < 0) - throw std::runtime_error("table id too low: "s + std::to_string(id)); - if (id >= static_cast(tables.size())) - throw std::runtime_error("table id too high: "s + std::to_string(id)); - return tables[static_cast(id)]; + auto& table = get_table(id); + if (row < 0) + throw std::underflow_error{std::format("negative row: {}",row)}; + if (static_cast(table.size()) <= row) + throw std::overflow_error{std::format("row overflow: {}", row)}; + return table[static_cast(row)]; } /** @@ -204,19 +225,14 @@ static table_t& get_table(int id) * @param col the column number * @return the element reference at row:col */ -static elem_t& access(int id, int row, int col) +static Elem& access(int id, int row, int col) { using namespace std::string_literals; - auto& table = get_table(id); - if (row < 0) - throw std::runtime_error("negative row: "s + std::to_string(row)); - if (static_cast(table.size()) <= row) - throw std::runtime_error("row overflow: "s + std::to_string(row)); + auto& table_row = get_table_row(id, row); if (col < 0) - throw std::runtime_error("negative column: "s + std::to_string(col)); - auto& table_row = table[static_cast(row)]; + throw std::underflow_error{std::format("negative column: {}", col)}; if (static_cast(table_row.size()) <= col) - throw std::runtime_error("column overflow: "s + std::to_string(col)); + throw std::overflow_error{std::format("column overflow: {}", col)}; return table_row[static_cast(col)]; } @@ -225,111 +241,118 @@ C_PUBLIC double read_double(int id, int row, int col) { try { return access(id, row, col); - } catch (std::runtime_error& e [[maybe_unused]]) { + } catch (const std::exception& e [[maybe_unused]]) { log_err("%s", e.what()); } return std::nan(""); } -C_PUBLIC int read_int(int id, int row, int col) { return (int)read_double(id, row, col); } +C_PUBLIC int read_int(int id, int row, int col) +{ + const auto res = read_double(id, row, col); + return static_cast(res); +} -/** User function: resize the entire table to a given rectangular size. Return id on success */ C_PUBLIC int table_resize_double(int id, int rows, int cols, double value) { - auto& table = get_table(id); - if (rows < 0) { - log_err("negative row number: %d", rows); - return -1; - } - if (cols < 0) { - log_err("negative column number: %d", cols); - return -1; + try { + auto& table = get_table(id); + if (rows < 0) + throw std::underflow_error{std::format("negative number of rows: {}", rows)}; + if (cols < 0) + throw std::underflow_error{std::format("negative number of columns: {}", cols)}; + table.resize(static_cast(rows)); + for (auto& row : table) + row.resize(static_cast(cols), value); + return id; + } catch (const std::exception& e) { + log_err("%s", e.what()); } - table.resize(static_cast(rows)); - for (auto& row : table) - row.resize(static_cast(cols), value); - return id; + return -1; } -/** User function: resize the entire table to a given rectangular size. Return id on success */ C_PUBLIC int table_resize_int(int id, int rows, int cols, int value) { - auto& table = get_table(id); - if (rows < 0) { - log_err("negative row number: %d", rows); - return -1; - } - if (cols < 0) { - log_err("negative column number: %d", cols); - return -1; - } - table.resize(static_cast(rows)); - for (auto& row : table) - row.resize(static_cast(cols), value); - return id; + return table_resize_double(id, rows, cols, value); } -C_PUBLIC void write_double(int id, int row, int col, double value) +C_PUBLIC int table_resize(int id, int rows, int cols) +{ + return table_resize_double(id, rows, cols, 0); +} + +C_PUBLIC int write_double(int id, int row, int col, double value) { try { access(id, row, col) = value; - } catch (std::runtime_error& e [[maybe_unused]]) { + return 0; + } catch (const std::exception& e [[maybe_unused]]) { log_err("%s", e.what()); } + return -1; } -C_PUBLIC void write_int(int id, int row, int col, int value) { write_double(id, row, col, value); } +C_PUBLIC int write_int(int id, int row, int col, int value) { return write_double(id, row, col, value); } -C_PUBLIC double interpolate(int id, double key, int key_col, int valu_col) +C_PUBLIC double interpolate(int id, double key, int key_col, int value_col) { - auto res = 0.0; try { - auto& table = get_table(id); - res = interpolate(table, key, key_col, valu_col); - } catch (std::runtime_error& e [[maybe_unused]]) { + const auto& table = get_table(id); + const auto res = interpolate(table, key, key_col, value_col); + return res; + } catch (const std::exception& e [[maybe_unused]]) { log_err("%s", e.what()); } - return res; + return std::nan(""); } -C_PUBLIC void read_int_col(int id, int row, int col, int* items, int offset, int count) +C_PUBLIC int read_int_col(int id, int row, int col, int* items, int offset, int count) { try { log_err("read_int_col(%d, %d, %d, %p, %d %d)", id, row, col, items, offset, count); - auto& table = get_table(id); + const auto& table = get_table(id); if (row < 0) - throw std::runtime_error("negative row"); - if (col < 0) - throw std::runtime_error("negative column"); + throw std::underflow_error{std::format("negative row: {}", row)}; if (row + count > static_cast(table.size())) - throw std::runtime_error("row range is beyond table size"); + throw std::overflow_error{std::format("row+count {} is beyond number of rows", row+count)}; + if (col < 0) + throw std::underflow_error{std::format("negative column {}", col)}; if (col >= static_cast(table[static_cast(row)].size())) - throw std::runtime_error("column is beyond table size"); - auto rb = std::next(std::begin(table), row), re = std::end(table); + throw std::overflow_error{std::format("column {} is beyond table size", col)}; + const auto re = std::end(table); + auto rb = std::next(std::begin(table), row); for (auto i = 0; i < count && rb != re; ++i, ++rb) items[offset + i] = static_cast((*rb)[static_cast(col)]); - } catch (std::runtime_error& e [[maybe_unused]]) { + return 0; + } catch (const std::exception& e [[maybe_unused]]) { log_err("%s", e.what()); } + return -1; } -C_PUBLIC void read_int_row(int id, int row, int col, int* items, int offset, int count) +C_PUBLIC int read_int_row(int id, int row, int col, int* items, int offset, int count) { try { log_err("read_int_row(%d, %d, %d, %p, %d %d)", id, row, col, items, offset, count); - auto& table = get_table(id); + const auto& table = get_table(id); if (row < 0) - throw std::runtime_error("negative row"); - if (col < 0) - throw std::runtime_error("negative column"); + throw std::underflow_error{std::format("negative row {}", row)}; if (row >= static_cast(table.size())) - throw std::runtime_error("row is beyond table size"); + throw std::overflow_error{std::format("row {} is beyond table size", row)}; + if (offset < 0) + throw std::underflow_error{std::format("negative offset {}", offset)}; + if (count < 0) + throw std::underflow_error{std::format("negative count {}", count)}; + if (col < 0) + throw std::underflow_error{std::format("negative column {}", col)}; if (col + count > static_cast(table[static_cast(row)].size())) - throw std::runtime_error("column range is beyond table size"); + throw std::overflow_error{std::format("col+count {} is beyond table size", col+count)}; auto rb = std::next(std::begin(table), row); - for (auto i = 0; i < count; ++i) - items[offset + i] = static_cast((*rb)[static_cast(col + i)]); - } catch (std::runtime_error& e [[maybe_unused]]) { + for (auto i = size_t{0}; i < static_cast(count); ++i) + items[offset + i] = static_cast((*rb)[col + i]); + return 0; + } catch (const std::exception& e [[maybe_unused]]) { log_err("%s", e.what()); } + return -1; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..12961ed --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,12 @@ +add_executable(table_test table_test.cpp) +target_link_libraries(table_test PRIVATE doctest::doctest_with_main) +add_dependencies(table_test table) +add_test(NAME table_test COMMAND table_test) + +add_executable(logging_test logging_test.cpp) +target_link_libraries(logging_test PRIVATE logging doctest::doctest_with_main) +if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + add_test(NAME logging_test COMMAND logging_test) +else() # assuming multi-config generator: + add_test(NAME logging_test COMMAND logging_test CONFIGURATION Debug RelWithDebInfo) +endif () diff --git a/src/test_errors.cpp b/tests/logging_test.cpp similarity index 70% rename from src/test_errors.cpp rename to tests/logging_test.cpp index fa204ee..732e180 100644 --- a/src/test_errors.cpp +++ b/tests/logging_test.cpp @@ -1,4 +1,4 @@ -#include "errors.hpp" +#include "logging.h" #include // hack to fix doctest for MSVC #include @@ -8,15 +8,16 @@ TEST_CASE("Error message") { - set_error_path("test_errors.log"); + CHECK(get_error_path() == std::string_view{"error.log"}); + set_error_path("logging_test.log"); log_err("Testing: %s %d %f", "errors", 42, 3.141); const auto error_path = get_error_path(); REQUIRE(error_path != nullptr); auto is = std::ifstream{error_path}; REQUIRE(static_cast(is)); - const auto buffer = - std::string{std::istreambuf_iterator{is}, std::istreambuf_iterator{}}; - auto content = std::string_view{buffer}; + using isbit =std::istreambuf_iterator; + const auto buffer = std::string{isbit{is}, isbit{}}; + const auto content = std::string_view{buffer}; REQUIRE(static_cast(is)); const auto message_pos = content.find(" "); REQUIRE(message_pos != std::string_view::npos); @@ -26,8 +27,8 @@ TEST_CASE("Error message") CHECK(message == "Testing: errors 42 3.141000"); const auto at_pos = content.find(" at ", in_pos + 1); REQUIRE(at_pos != std::string_view::npos); - const auto test_errors_pos = content.find("test_errors.cpp", at_pos + 4); + const auto test_errors_pos = content.find("logging_test.cpp", at_pos + 4); REQUIRE(test_errors_pos != std::string_view::npos); const auto location = content.substr(test_errors_pos); - CHECK(location == "test_errors.cpp:12\n"); + CHECK(location == "logging_test.cpp:13\n"); } \ No newline at end of file diff --git a/src/test_table.cpp b/tests/table_test.cpp similarity index 65% rename from src/test_table.cpp rename to tests/table_test.cpp index cdb04fb..3e60f76 100644 --- a/src/test_table.cpp +++ b/tests/table_test.cpp @@ -4,18 +4,21 @@ */ #include "library.hpp" +#include #include #include #include #include +TEST_SUITE_BEGIN("libtable"); + #if defined(__linux__) -const auto table_path = std::filesystem::current_path() / "libtable.so"; +const auto libtable_path = std::filesystem::current_path() /".."/"src"/ "libtable.so"; #elif defined(__APPLE__) -const auto table_path = std::filesystem::current_path() / "libtable.dylib"; +const auto libtable_path = std::filesystem::current_path() /".."/"src"/ "libtable.dylib"; #elif defined(__MINGW32__) -const auto table_path = std::filesystem::current_path() / "libtable.dll"; +const auto libtable_path = std::filesystem::current_path() /".."/"src"/ "libtable.dll"; #elif defined(_WIN32) const auto table_path = [] { // CMake on Windows puts Release binaries into CMAKE_CURRENT_BINARY_DIR/Release @@ -34,8 +37,13 @@ const auto table_path = [] { #error("Unknown platform") #endif +const auto csv_path = std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; + TEST_CASE("load libtable") { + REQUIRE_MESSAGE(exists(libtable_path), ("Failed to find "+libtable_path.string())); + REQUIRE_MESSAGE(exists(csv_path), ("Failed to find "+csv_path.string())); + using fn_str_int_to_int = int (*)(const char*, int); using fn_int_str_to_int = int (*)(int, const char*); using fn_int_to_int = int (*)(int); @@ -45,12 +53,12 @@ TEST_CASE("load libtable") using fn_int_double_int_int_to_double = double (*)(int, double, int, int); using fn_int_int_int_int = int (*)(int, int, int, int); using fn_int_int_int_double = int (*)(int, int, int, double); - using fn_int_int_int_intp_int_int = void (*)(int, int, int, int*, int, int); + using fn_int_int_int_intp_int_int = int (*)(int, int, int, int*, int, int); auto approx = doctest::Approx{0}.epsilon(0.00001); try { - auto lib_path_str = table_path.string(); + auto lib_path_str = libtable_path.string(); std::cout << "Loading " << lib_path_str << std::endl; auto lib = Library{lib_path_str.c_str()}; // may throw upon errors auto table_new_int [[maybe_unused]] = lib.lookup("table_new_int"); @@ -72,19 +80,37 @@ TEST_CASE("load libtable") auto interpolate = lib.lookup("interpolate"); // read from file: - const auto id = table_read_csv("table_input.csv", 0); - auto rows = table_rows(id); - auto cols = table_cols(id); - REQUIRE(rows != 0); // table should not be empty - REQUIRE(cols != 0); + const auto id = table_read_csv(csv_path.c_str(), 0); + REQUIRE(id >= 0); // success with loading table + const auto rows = table_rows(id); + REQUIRE(rows >= 0); // table should be non-empty + CHECK(table_rows(id+1) == -1); // non-existing table + const auto cols = table_cols(id); + REQUIRE(cols >= 0); // should be some columns + CHECK(table_cols(id+1) == -1); // non-existing table // read access: - for (int i = 0; i < rows; ++i) { - for (int j = 0; j < cols; ++j) - std::cout << read_double(id, i, j) << " "; + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols; ++col) + std::cout << read_double(id, row, col) << " "; std::cout << '\n'; } CHECK(6 == read_double(id, 1, 1)); + // bad arguments: + CHECK(std::isnan(read_double(-1, 1, 1))); // negative table id + CHECK(std::isnan(read_double(id+1, 1, 1))); // non-existing table + CHECK(std::isnan(read_double(id, -1, 1))); // negative row + CHECK(std::isnan(read_double(id, rows, 1))); // row overflow + CHECK(std::isnan(read_double(id, 1, -1))); // negative column + CHECK(std::isnan(read_double(id, 1, cols))); // column overflow + constexpr auto bad_int = std::numeric_limits::lowest(); + CHECK(read_int(-1, 1, 1) == bad_int); // negative table id + CHECK(read_int(id+1, 1, 1) == bad_int); // non-existing table + CHECK(read_int(id, -1, 1) == bad_int); // negative row + CHECK(read_int(id, rows, 1) == bad_int); // row overflow + CHECK(read_int(id, 1, -1) == bad_int); // negative column + CHECK(read_int(id, 1, cols) == bad_int); // column overflow + const auto v1_2 = interpolate(id, 1.2, 0, 1); CHECK(v1_2 == approx(5.2)); const auto v0 = interpolate(id, 0.0, 0, 1); @@ -93,7 +119,7 @@ TEST_CASE("load libtable") CHECK(8.0 == v5_5); // read in bulk: auto column1 = std::vector(static_cast(rows), 0); - read_int_col(id, 0, 1, column1.data(), 0, rows); + REQUIRE(read_int_col(id, 0, 1, column1.data(), 0, rows) == 0); CHECK(column1[0] == 5); CHECK(column1[1] == 6); CHECK(column1[2] == 7); @@ -122,11 +148,11 @@ TEST_CASE("load libtable") CHECK(table_rows(id3) == 3); CHECK(table_cols(id3) == 4); CHECK(read_double(id3, 2, 2) == 3.14); - } catch (std::exception& err) { - std::cerr << "Failed: " << err.what() << std::endl; - CHECK(false); + } catch (const std::exception& err) { + FAIL(err.what()); } catch (...) { - std::cerr << "Failed with unknown exception" << std::endl; - CHECK(false); + FAIL("Failed with unknown exception"); } } + +TEST_SUITE_END(); \ No newline at end of file From 4c53874ecc0a3b34f6d95f2e7addf21ffce110c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Fri, 13 Mar 2026 17:31:05 +0100 Subject: [PATCH 02/12] Fixed mingw/windows-specific issues --- src/table.cpp | 2 +- tests/table_test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/table.cpp b/src/table.cpp index 60864f5..7e9b461 100644 --- a/src/table.cpp +++ b/src/table.cpp @@ -349,7 +349,7 @@ C_PUBLIC int read_int_row(int id, int row, int col, int* items, int offset, int throw std::overflow_error{std::format("col+count {} is beyond table size", col+count)}; auto rb = std::next(std::begin(table), row); for (auto i = size_t{0}; i < static_cast(count); ++i) - items[offset + i] = static_cast((*rb)[col + i]); + items[static_cast(offset) + i] = static_cast((*rb)[static_cast(col) + i]); return 0; } catch (const std::exception& e [[maybe_unused]]) { log_err("%s", e.what()); diff --git a/tests/table_test.cpp b/tests/table_test.cpp index 3e60f76..9a5553b 100644 --- a/tests/table_test.cpp +++ b/tests/table_test.cpp @@ -80,7 +80,7 @@ TEST_CASE("load libtable") auto interpolate = lib.lookup("interpolate"); // read from file: - const auto id = table_read_csv(csv_path.c_str(), 0); + const auto id = table_read_csv(csv_path.string().c_str(), 0); REQUIRE(id >= 0); // success with loading table const auto rows = table_rows(id); REQUIRE(rows >= 0); // table should be non-empty From 1f7500033e4b489fac5bb0a65f63aa7213de6086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Fri, 13 Mar 2026 17:36:03 +0100 Subject: [PATCH 03/12] Fixed formatting --- include/csvtable.hpp | 3 +-- include/library.hpp | 6 ++++-- include/logging.h | 2 +- src/table.cpp | 23 +++++++++++--------- tests/logging_test.cpp | 2 +- tests/table_test.cpp | 49 +++++++++++++++++++++--------------------- 6 files changed, 45 insertions(+), 40 deletions(-) diff --git a/include/csvtable.hpp b/include/csvtable.hpp index a878a51..24929d8 100644 --- a/include/csvtable.hpp +++ b/include/csvtable.hpp @@ -63,8 +63,7 @@ inline void skip_comments(std::istream& is) return dictionary; } -[[nodiscard]] Elem interpolate(const Table& table, const Elem key, int key_column, - int value_column) +[[nodiscard]] Elem interpolate(const Table& table, const Elem key, int key_column, int value_column) { using namespace std::string_literals; if (key_column < 0) diff --git a/include/library.hpp b/include/library.hpp index e4cb4d5..967974e 100644 --- a/include/library.hpp +++ b/include/library.hpp @@ -40,8 +40,9 @@ struct Library return res; throw std::runtime_error{dlerror()}; } + private: - void* handle{}; ///< library handle + void* handle{}; ///< library handle }; #elif defined(_WIN32) || defined(__MINGW32__) @@ -77,8 +78,9 @@ struct Library return res; throw std::runtime_error{"Failed symbol lookup: " + std::to_string(GetLastError())}; } + private: - HMODULE handle{}; ///< library handle + HMODULE handle{}; ///< library handle }; #else diff --git a/include/logging.h b/include/logging.h index 5270256..119db21 100644 --- a/include/logging.h +++ b/include/logging.h @@ -26,4 +26,4 @@ C_PUBLIC const char* get_error_path(); void log_error(const char* function, const char* path, int line, const char* format, ...); -#endif // INCLUDE_LOGGING_H \ No newline at end of file +#endif // INCLUDE_LOGGING_H \ No newline at end of file diff --git a/src/table.cpp b/src/table.cpp index 7e9b461..5b98855 100644 --- a/src/table.cpp +++ b/src/table.cpp @@ -9,7 +9,7 @@ #include // to_string/MSVC #include -#include // nan +#include // nan /// creates a new table rows x cols and populates with zeros C_PUBLIC int table_new(int rows, int cols); @@ -66,10 +66,7 @@ C_PUBLIC int table_new_double(int rows, int cols, double value) return res; } -C_PUBLIC int table_new(int rows, int cols) -{ - return table_new_double(rows, cols, 0.0); -} +C_PUBLIC int table_new(int rows, int cols) { return table_new_double(rows, cols, 0.0); } C_PUBLIC int table_new_int(int rows, int cols, int value) { @@ -213,7 +210,7 @@ static auto& get_table_row(int id, int row) { auto& table = get_table(id); if (row < 0) - throw std::underflow_error{std::format("negative row: {}",row)}; + throw std::underflow_error{std::format("negative row: {}", row)}; if (static_cast(table.size()) <= row) throw std::overflow_error{std::format("row overflow: {}", row)}; return table[static_cast(row)]; @@ -292,7 +289,10 @@ C_PUBLIC int write_double(int id, int row, int col, double value) return -1; } -C_PUBLIC int write_int(int id, int row, int col, int value) { return write_double(id, row, col, value); } +C_PUBLIC int write_int(int id, int row, int col, int value) +{ + return write_double(id, row, col, value); +} C_PUBLIC double interpolate(int id, double key, int key_col, int value_col) { @@ -314,7 +314,8 @@ C_PUBLIC int read_int_col(int id, int row, int col, int* items, int offset, int if (row < 0) throw std::underflow_error{std::format("negative row: {}", row)}; if (row + count > static_cast(table.size())) - throw std::overflow_error{std::format("row+count {} is beyond number of rows", row+count)}; + throw std::overflow_error{ + std::format("row+count {} is beyond number of rows", row + count)}; if (col < 0) throw std::underflow_error{std::format("negative column {}", col)}; if (col >= static_cast(table[static_cast(row)].size())) @@ -346,10 +347,12 @@ C_PUBLIC int read_int_row(int id, int row, int col, int* items, int offset, int if (col < 0) throw std::underflow_error{std::format("negative column {}", col)}; if (col + count > static_cast(table[static_cast(row)].size())) - throw std::overflow_error{std::format("col+count {} is beyond table size", col+count)}; + throw std::overflow_error{ + std::format("col+count {} is beyond table size", col + count)}; auto rb = std::next(std::begin(table), row); for (auto i = size_t{0}; i < static_cast(count); ++i) - items[static_cast(offset) + i] = static_cast((*rb)[static_cast(col) + i]); + items[static_cast(offset) + i] = + static_cast((*rb)[static_cast(col) + i]); return 0; } catch (const std::exception& e [[maybe_unused]]) { log_err("%s", e.what()); diff --git a/tests/logging_test.cpp b/tests/logging_test.cpp index 732e180..3f56031 100644 --- a/tests/logging_test.cpp +++ b/tests/logging_test.cpp @@ -15,7 +15,7 @@ TEST_CASE("Error message") REQUIRE(error_path != nullptr); auto is = std::ifstream{error_path}; REQUIRE(static_cast(is)); - using isbit =std::istreambuf_iterator; + using isbit = std::istreambuf_iterator; const auto buffer = std::string{isbit{is}, isbit{}}; const auto content = std::string_view{buffer}; REQUIRE(static_cast(is)); diff --git a/tests/table_test.cpp b/tests/table_test.cpp index 9a5553b..91d6997 100644 --- a/tests/table_test.cpp +++ b/tests/table_test.cpp @@ -14,11 +14,11 @@ TEST_SUITE_BEGIN("libtable"); #if defined(__linux__) -const auto libtable_path = std::filesystem::current_path() /".."/"src"/ "libtable.so"; +const auto libtable_path = std::filesystem::current_path() / ".." / "src" / "libtable.so"; #elif defined(__APPLE__) -const auto libtable_path = std::filesystem::current_path() /".."/"src"/ "libtable.dylib"; +const auto libtable_path = std::filesystem::current_path() / ".." / "src" / "libtable.dylib"; #elif defined(__MINGW32__) -const auto libtable_path = std::filesystem::current_path() /".."/"src"/ "libtable.dll"; +const auto libtable_path = std::filesystem::current_path() / ".." / "src" / "libtable.dll"; #elif defined(_WIN32) const auto table_path = [] { // CMake on Windows puts Release binaries into CMAKE_CURRENT_BINARY_DIR/Release @@ -34,15 +34,16 @@ const auto table_path = [] { return std::filesystem::path{buffer}.parent_path() / "table.dll"; }(); #else -#error("Unknown platform") +#error ("Unknown platform") #endif -const auto csv_path = std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; +const auto csv_path = + std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; TEST_CASE("load libtable") { - REQUIRE_MESSAGE(exists(libtable_path), ("Failed to find "+libtable_path.string())); - REQUIRE_MESSAGE(exists(csv_path), ("Failed to find "+csv_path.string())); + REQUIRE_MESSAGE(exists(libtable_path), ("Failed to find " + libtable_path.string())); + REQUIRE_MESSAGE(exists(csv_path), ("Failed to find " + csv_path.string())); using fn_str_int_to_int = int (*)(const char*, int); using fn_int_str_to_int = int (*)(int, const char*); @@ -81,13 +82,13 @@ TEST_CASE("load libtable") // read from file: const auto id = table_read_csv(csv_path.string().c_str(), 0); - REQUIRE(id >= 0); // success with loading table + REQUIRE(id >= 0); // success with loading table const auto rows = table_rows(id); - REQUIRE(rows >= 0); // table should be non-empty - CHECK(table_rows(id+1) == -1); // non-existing table + REQUIRE(rows >= 0); // table should be non-empty + CHECK(table_rows(id + 1) == -1); // non-existing table const auto cols = table_cols(id); - REQUIRE(cols >= 0); // should be some columns - CHECK(table_cols(id+1) == -1); // non-existing table + REQUIRE(cols >= 0); // should be some columns + CHECK(table_cols(id + 1) == -1); // non-existing table // read access: for (int row = 0; row < rows; ++row) { @@ -97,19 +98,19 @@ TEST_CASE("load libtable") } CHECK(6 == read_double(id, 1, 1)); // bad arguments: - CHECK(std::isnan(read_double(-1, 1, 1))); // negative table id - CHECK(std::isnan(read_double(id+1, 1, 1))); // non-existing table - CHECK(std::isnan(read_double(id, -1, 1))); // negative row - CHECK(std::isnan(read_double(id, rows, 1))); // row overflow - CHECK(std::isnan(read_double(id, 1, -1))); // negative column - CHECK(std::isnan(read_double(id, 1, cols))); // column overflow + CHECK(std::isnan(read_double(-1, 1, 1))); // negative table id + CHECK(std::isnan(read_double(id + 1, 1, 1))); // non-existing table + CHECK(std::isnan(read_double(id, -1, 1))); // negative row + CHECK(std::isnan(read_double(id, rows, 1))); // row overflow + CHECK(std::isnan(read_double(id, 1, -1))); // negative column + CHECK(std::isnan(read_double(id, 1, cols))); // column overflow constexpr auto bad_int = std::numeric_limits::lowest(); - CHECK(read_int(-1, 1, 1) == bad_int); // negative table id - CHECK(read_int(id+1, 1, 1) == bad_int); // non-existing table - CHECK(read_int(id, -1, 1) == bad_int); // negative row - CHECK(read_int(id, rows, 1) == bad_int); // row overflow - CHECK(read_int(id, 1, -1) == bad_int); // negative column - CHECK(read_int(id, 1, cols) == bad_int); // column overflow + CHECK(read_int(-1, 1, 1) == bad_int); // negative table id + CHECK(read_int(id + 1, 1, 1) == bad_int); // non-existing table + CHECK(read_int(id, -1, 1) == bad_int); // negative row + CHECK(read_int(id, rows, 1) == bad_int); // row overflow + CHECK(read_int(id, 1, -1) == bad_int); // negative column + CHECK(read_int(id, 1, cols) == bad_int); // column overflow const auto v1_2 = interpolate(id, 1.2, 0, 1); CHECK(v1_2 == approx(5.2)); From 07a709c10049bb20a0b44819f74a81d6115a742f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Fri, 13 Mar 2026 17:37:33 +0100 Subject: [PATCH 04/12] Renamed csvtable.hpp to table.hpp --- include/library.hpp | 6 +++--- include/{csvtable.hpp => table.hpp} | 6 +++--- src/table.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename include/{csvtable.hpp => table.hpp} (97%) diff --git a/include/library.hpp b/include/library.hpp index 967974e..e39400f 100644 --- a/include/library.hpp +++ b/include/library.hpp @@ -2,8 +2,8 @@ * C++ wrapper for opening dynamically linked libraries * Author: Marius Mikucionis */ -#ifndef _LIBRARY_HPP_ -#define _LIBRARY_HPP_ +#ifndef INCLUDE_LIBRARY_HPP +#define INCLUDE_LIBRARY_HPP #include // to_string #include // runtime_error @@ -87,4 +87,4 @@ struct Library #error "unsupported platform" #endif -#endif /* _LIBRARY_HPP_ */ +#endif /* INCLUDE_LIBRARY_HPP */ diff --git a/include/csvtable.hpp b/include/table.hpp similarity index 97% rename from include/csvtable.hpp rename to include/table.hpp index 24929d8..129b198 100644 --- a/include/csvtable.hpp +++ b/include/table.hpp @@ -2,8 +2,8 @@ * CSV table representation and its input and output. * Author: Marius Mikucionis */ -#ifndef _CSVTABLE_HPP_ -#define _CSVTABLE_HPP_ +#ifndef INCLUDE_TABLE_HPP +#define INCLUDE_TABLE_HPP #include #include @@ -120,4 +120,4 @@ std::ostream& dictionary_write_csv(std::ostream& os, const Dictionary& dictionar return os; } -#endif /* _CSVTABLE_HPP_ */ +#endif /* INCLUDE_TABLE_HPP */ diff --git a/src/table.cpp b/src/table.cpp index 5b98855..8686072 100644 --- a/src/table.cpp +++ b/src/table.cpp @@ -2,7 +2,7 @@ * Implements libtable functions. * Author: Marius Mikucionis */ -#include "csvtable.hpp" +#include "table.hpp" #include "logging.h" #include "dynlib.h" #include From 15be60a17789517b09fc69d697f8c808790a9e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Fri, 13 Mar 2026 18:00:36 +0100 Subject: [PATCH 05/12] Added check for std::nan (On macos std::nan becomes zero when casted to int) --- src/table.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/table.cpp b/src/table.cpp index 8686072..d413a72 100644 --- a/src/table.cpp +++ b/src/table.cpp @@ -244,9 +244,13 @@ C_PUBLIC double read_double(int id, int row, int col) return std::nan(""); } +constexpr auto BAD_INT = std::numeric_limits::lowest(); + C_PUBLIC int read_int(int id, int row, int col) { const auto res = read_double(id, row, col); + if (std::isnan(res)) + return BAD_INT; return static_cast(res); } From 41fcfb099193ee01da3f0e3f8c9bbe4b2772a823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Fri, 13 Mar 2026 18:25:19 +0100 Subject: [PATCH 06/12] Enabled logging through a special preprocessor macro LOGGING --- include/logging.h | 10 +++++----- src/CMakeLists.txt | 6 ++++++ src/logging.cpp | 10 +++++----- tests/logging_test.cpp | 14 ++++++++------ 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/include/logging.h b/include/logging.h index 119db21..1ed8609 100644 --- a/include/logging.h +++ b/include/logging.h @@ -8,21 +8,21 @@ #include "dynlib.h" -#ifndef NDEBUG +#ifdef LOGGING #ifdef _WIN32 #define log_err(format, ...) log_error(__FUNCTION__, __FILE__, __LINE__, format, __VA_ARGS__) #else // not _WIN32 #define log_err(format, ...) \ log_error(__FUNCTION__, __FILE__, __LINE__, format __VA_OPT__(, ) __VA_ARGS__) #endif // _WIN32 -#else // with NDEBUG +#else // no LOGGING #define log_err(format, ...) -#endif // NDEBUG +#endif // LOGGING /// Set the file path for errors, returns 0 always -C_PUBLIC int set_error_path(const char* err_path); +C_PUBLIC int set_log_path(const char* err_path); /// Returns the path to the log file, "error.log" by default -C_PUBLIC const char* get_error_path(); +C_PUBLIC const char* get_log_path(); void log_error(const char* function, const char* path, int line, const char* format, ...); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb0de8e..24a6116 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,10 @@ add_library(logging OBJECT logging.cpp) +if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + target_compile_definitions(logging PUBLIC LOGGING=1) +else() # assuming multi-config generator: + target_compile_definitions(logging PUBLIC $<$:LOGGING=1> ) +endif () + add_library(table SHARED table.cpp) target_link_libraries(table PRIVATE logging) diff --git a/src/logging.cpp b/src/logging.cpp index 4546f16..3996f4b 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -13,18 +13,18 @@ static auto error_own = false; // do we own the error file? static auto error_path = std::string{"error.log"}; -C_PUBLIC int set_error_path(const char* path) +C_PUBLIC int set_log_path(const char* path) { error_path = path; error_own = false; return 0; } -C_PUBLIC const char* get_error_path() { return error_path.c_str(); } +C_PUBLIC const char* get_log_path() { return error_path.c_str(); } static FILE* open_log_file() { - const auto path = get_error_path(); + const auto path = get_log_path(); FILE* file = nullptr; #ifdef __STDC_LIB_EXT1__ auto err = errno_t{}; @@ -40,9 +40,9 @@ static FILE* open_log_file() } #else if (error_own) { - file = std::fopen(get_error_path(), "a"); + file = std::fopen(get_log_path(), "a"); } else { - file = std::fopen(get_error_path(), "w"); + file = std::fopen(get_log_path(), "w"); error_own = true; } if (file == nullptr) { diff --git a/tests/logging_test.cpp b/tests/logging_test.cpp index 3f56031..68ee947 100644 --- a/tests/logging_test.cpp +++ b/tests/logging_test.cpp @@ -8,13 +8,14 @@ TEST_CASE("Error message") { - CHECK(get_error_path() == std::string_view{"error.log"}); - set_error_path("logging_test.log"); + CHECK(get_log_path() == std::string_view{"error.log"}); + set_log_path("logging_test.log"); + const auto error_path = get_log_path(); + REQUIRE_MESSAGE(error_path != nullptr, "Failed to get log path"); +#ifdef LOGGING log_err("Testing: %s %d %f", "errors", 42, 3.141); - const auto error_path = get_error_path(); - REQUIRE(error_path != nullptr); auto is = std::ifstream{error_path}; - REQUIRE(static_cast(is)); + REQUIRE_MESSAGE(static_cast(is), (std::string{"Failed to read "}+error_path)); using isbit = std::istreambuf_iterator; const auto buffer = std::string{isbit{is}, isbit{}}; const auto content = std::string_view{buffer}; @@ -30,5 +31,6 @@ TEST_CASE("Error message") const auto test_errors_pos = content.find("logging_test.cpp", at_pos + 4); REQUIRE(test_errors_pos != std::string_view::npos); const auto location = content.substr(test_errors_pos); - CHECK(location == "logging_test.cpp:13\n"); + CHECK(location == "logging_test.cpp:16\n"); +#endif // LOGGING } \ No newline at end of file From f57a6b9dbd14cc9a84fb42fcd12ad11637d2a6bf Mon Sep 17 00:00:00 2001 From: Marius Mikucionis Date: Fri, 13 Mar 2026 18:01:59 +0100 Subject: [PATCH 07/12] Added toolchain files for g++-14 and g++-15 on macos --- compile.sh | 10 +--------- toolchain/macos64-brew-gcc14.cmake | 28 ++++++++++++++++++++++++++++ toolchain/macos64-brew-gcc15.cmake | 28 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 toolchain/macos64-brew-gcc14.cmake create mode 100644 toolchain/macos64-brew-gcc15.cmake diff --git a/compile.sh b/compile.sh index a52f34e..db48f61 100755 --- a/compile.sh +++ b/compile.sh @@ -121,15 +121,7 @@ for target in $targets ; do extension=so SANITIZE="-DSSP=ON -DUBSAN=ON -DASAN=ON" ;; - macos64-brew-gcc10) - extension=dylib - SANITIZE="-DSSP=ON" - ;; - macos64-brew-gcc11) - extension=dylib - SANITIZE="-DSSP=ON" - ;; - macos64-brew-gcc12) + macos64-brew-gcc*) extension=dylib SANITIZE="-DSSP=ON" ;; diff --git a/toolchain/macos64-brew-gcc14.cmake b/toolchain/macos64-brew-gcc14.cmake new file mode 100644 index 0000000..f55de03 --- /dev/null +++ b/toolchain/macos64-brew-gcc14.cmake @@ -0,0 +1,28 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Darwin) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-14) +set(CMAKE_CXX_COMPILER g++-14) +set(CMAKE_AR gcc-ar-14) +set(CMAKE_NM gcc-nm-14) +set(CMAKE_RANLIB gcc-ranlib-14) # https://stackoverflow.com/questions/53128049/ld-archive-has-no-table-of-contents-file-error-with-homebrew +set(RANLIB gcc-ranlib-14) +# silence superfluous "has no symbols" warnings (does not help): +# set(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") +# set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") +# Do not use RPATH: +#set(CMAKE_MACOSX_RPATH FALSE) +#set(MACOSX_RPATH FALSE) +# set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +# set(CMAKE_SKIP_RPATH TRUE) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both the target and host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/toolchain/macos64-brew-gcc15.cmake b/toolchain/macos64-brew-gcc15.cmake new file mode 100644 index 0000000..1334271 --- /dev/null +++ b/toolchain/macos64-brew-gcc15.cmake @@ -0,0 +1,28 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Darwin) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-15) +set(CMAKE_CXX_COMPILER g++-15) +set(CMAKE_AR gcc-ar-15) +set(CMAKE_NM gcc-nm-15) +set(CMAKE_RANLIB gcc-ranlib-15) # https://stackoverflow.com/questions/53128049/ld-archive-has-no-table-of-contents-file-error-with-homebrew +set(RANLIB gcc-ranlib-15) +# silence superfluous "has no symbols" warnings (does not help): +# set(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") +# set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") +# Do not use RPATH: +#set(CMAKE_MACOSX_RPATH FALSE) +#set(MACOSX_RPATH FALSE) +# set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +# set(CMAKE_SKIP_RPATH TRUE) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both the target and host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) From 5d972abecd3cc093cac1cb0db130eac8bd7b785d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Fri, 13 Mar 2026 18:47:10 +0100 Subject: [PATCH 08/12] Fixed MSVC on windows --- cmake/stdcpp.cmake | 2 +- tests/table_test.cpp | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cmake/stdcpp.cmake b/cmake/stdcpp.cmake index 12d785a..ab6a54d 100644 --- a/cmake/stdcpp.cmake +++ b/cmake/stdcpp.cmake @@ -12,7 +12,7 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") message(STATUS "Enabled extra warnings for ${CMAKE_CXX_COMPILER}") add_compile_options( /permissive- # strict standards - /Wall -wd4191 -wd4668 -wd4710 -wd4711 -wd5045) + /W4) add_compile_definitions(__STDC_LIB_EXT1__ NOMINMAX) # enable fopen_s, kill min/max macros else() message(STATUS "No extra warnings for ${CMAKE_CXX_COMPILER_ID} compiler") diff --git a/tests/table_test.cpp b/tests/table_test.cpp index 91d6997..d18e4b6 100644 --- a/tests/table_test.cpp +++ b/tests/table_test.cpp @@ -15,12 +15,15 @@ TEST_SUITE_BEGIN("libtable"); #if defined(__linux__) const auto libtable_path = std::filesystem::current_path() / ".." / "src" / "libtable.so"; +const auto csv_path = std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; #elif defined(__APPLE__) const auto libtable_path = std::filesystem::current_path() / ".." / "src" / "libtable.dylib"; +const auto csv_path = std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; #elif defined(__MINGW32__) const auto libtable_path = std::filesystem::current_path() / ".." / "src" / "libtable.dll"; +const auto csv_path = std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; #elif defined(_WIN32) -const auto table_path = [] { +const auto libtable_path = [] { // CMake on Windows puts Release binaries into CMAKE_CURRENT_BINARY_DIR/Release // otherwise binaries are in CMAKE_CURRENT_BINARY_DIR auto buffer = std::string(1024, '\0'); @@ -31,17 +34,17 @@ const auto table_path = [] { size = GetModuleFileNameA(NULL, buffer.data(), static_cast(buffer.size())); } buffer.resize(size); // truncate the path - return std::filesystem::path{buffer}.parent_path() / "table.dll"; + return std::filesystem::path{buffer}.parent_path() / ".." / "src" / "table.dll"; }(); +const auto csv_path = + std::filesystem::current_path() / ".." / ".." / ".." / ".." / "examples" / "table_input.csv"; #else #error ("Unknown platform") #endif -const auto csv_path = - std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; - TEST_CASE("load libtable") { + using std::filesystem::exists; // MSVC fails to do ADL REQUIRE_MESSAGE(exists(libtable_path), ("Failed to find " + libtable_path.string())); REQUIRE_MESSAGE(exists(csv_path), ("Failed to find " + csv_path.string())); From 5776a5e2d4c9058517c7eabf709cbdfde3e6ae9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Mon, 16 Mar 2026 08:41:48 +0100 Subject: [PATCH 09/12] Pass the libtable and CSV file through C preprocessor macros, because we cannot guess the folder structure on every platform. --- tests/CMakeLists.txt | 3 +++ tests/table_test.cpp | 51 +++++++++++++++++--------------------------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 12961ed..b2846b8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,7 @@ add_executable(table_test table_test.cpp) +target_compile_definitions(table_test PUBLIC + LIBTABLE_PATH=$ + CSV_PATH=${CMAKE_SOURCE_DIR}/examples/table_input.csv) target_link_libraries(table_test PRIVATE doctest::doctest_with_main) add_dependencies(table_test table) add_test(NAME table_test COMMAND table_test) diff --git a/tests/table_test.cpp b/tests/table_test.cpp index d18e4b6..546b059 100644 --- a/tests/table_test.cpp +++ b/tests/table_test.cpp @@ -13,38 +13,23 @@ TEST_SUITE_BEGIN("libtable"); -#if defined(__linux__) -const auto libtable_path = std::filesystem::current_path() / ".." / "src" / "libtable.so"; -const auto csv_path = std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; -#elif defined(__APPLE__) -const auto libtable_path = std::filesystem::current_path() / ".." / "src" / "libtable.dylib"; -const auto csv_path = std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; -#elif defined(__MINGW32__) -const auto libtable_path = std::filesystem::current_path() / ".." / "src" / "libtable.dll"; -const auto csv_path = std::filesystem::current_path() / ".." / ".." / "examples" / "table_input.csv"; -#elif defined(_WIN32) -const auto libtable_path = [] { - // CMake on Windows puts Release binaries into CMAKE_CURRENT_BINARY_DIR/Release - // otherwise binaries are in CMAKE_CURRENT_BINARY_DIR - auto buffer = std::string(1024, '\0'); - auto size = GetModuleFileNameA( - NULL, buffer.data(), static_cast(buffer.size())); // path to current executable - while (size >= buffer.size()) { - buffer.resize(buffer.size() * 2, '\0'); - size = GetModuleFileNameA(NULL, buffer.data(), static_cast(buffer.size())); - } - buffer.resize(size); // truncate the path - return std::filesystem::path{buffer}.parent_path() / ".." / "src" / "table.dll"; -}(); -const auto csv_path = - std::filesystem::current_path() / ".." / ".." / ".." / ".." / "examples" / "table_input.csv"; -#else -#error ("Unknown platform") +#ifndef LIBTABLE_PATH +#error "Please define LIBTABLE_PATH to path to the dynamic library of table" +#endif +#ifndef CSV_PATH +#error "Please define CSV_PATH to path to the example csv file" #endif +#define STRINGIFY(x) #x +#define TO_STRING(x) STRINGIFY(x) + +using std::filesystem::path; +using std::filesystem::exists; // MSVC fails to do ADL +const path libtable_path = TO_STRING(LIBTABLE_PATH); +const path csv_path = TO_STRING(CSV_PATH); + TEST_CASE("load libtable") { - using std::filesystem::exists; // MSVC fails to do ADL REQUIRE_MESSAGE(exists(libtable_path), ("Failed to find " + libtable_path.string())); REQUIRE_MESSAGE(exists(csv_path), ("Failed to find " + csv_path.string())); @@ -63,7 +48,6 @@ TEST_CASE("load libtable") try { auto lib_path_str = libtable_path.string(); - std::cout << "Loading " << lib_path_str << std::endl; auto lib = Library{lib_path_str.c_str()}; // may throw upon errors auto table_new_int [[maybe_unused]] = lib.lookup("table_new_int"); auto table_new_double = lib.lookup("table_new_double"); @@ -94,11 +78,14 @@ TEST_CASE("load libtable") CHECK(table_cols(id + 1) == -1); // non-existing table // read access: + auto os = std::ostringstream{}; for (int row = 0; row < rows; ++row) { - for (int col = 0; col < cols; ++col) - std::cout << read_double(id, row, col) << " "; - std::cout << '\n'; + os << read_double(id, row, 0); + for (int col = 1; col < cols; ++col) + os << " " << read_double(id, row, col); + os << '\n'; } + CHECK(os.str() == "1 5 9 1\n2 6 10 4\n3 7 11 9\n4 8 12 16\n"); CHECK(6 == read_double(id, 1, 1)); // bad arguments: CHECK(std::isnan(read_double(-1, 1, 1))); // negative table id From 1a9505b0ba6ab9b8bfc06c1671a5fbc742cbd13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Mon, 16 Mar 2026 16:14:30 +0100 Subject: [PATCH 10/12] Added more compilers --- .../toolchain}/i686-w64-mingw32.cmake | 0 .../toolchain}/linux32-gcc10.cmake | 0 .../toolchain}/linux32-gcc11.cmake | 0 .../toolchain}/linux32-gcc12.cmake | 0 .../toolchain}/linux32-gcc13.cmake | 0 cmake/toolchain/linux32-gcc14.cmake | 18 ++++ cmake/toolchain/linux32-gcc15.cmake | 18 ++++ cmake/toolchain/linux32-gcc16.cmake | 18 ++++ {toolchain => cmake/toolchain}/linux32.cmake | 0 .../toolchain}/linux64-gcc10.cmake | 0 .../toolchain}/linux64-gcc11.cmake | 0 .../toolchain}/linux64-gcc12.cmake | 0 .../toolchain}/linux64-gcc13.cmake | 0 cmake/toolchain/linux64-gcc14.cmake | 18 ++++ cmake/toolchain/linux64-gcc15.cmake | 18 ++++ cmake/toolchain/linux64-gcc16.cmake | 18 ++++ {toolchain => cmake/toolchain}/linux64.cmake | 0 .../toolchain}/macos64-brew-gcc10.cmake | 0 .../toolchain}/macos64-brew-gcc11.cmake | 0 .../toolchain}/macos64-brew-gcc12.cmake | 0 .../toolchain}/macos64-brew-gcc13.cmake | 0 .../toolchain}/macos64-brew-gcc14.cmake | 0 .../toolchain}/macos64-brew-gcc15.cmake | 0 cmake/toolchain/macos64-brew-gcc16.cmake | 28 ++++++ .../toolchain}/macos64-ports-gcc10.cmake | 0 .../toolchain}/macos64-ports-gcc11.cmake | 0 .../toolchain}/macos64-ports-gcc12.cmake | 0 cmake/toolchain/macos64-ports-gcc13.cmake | 29 ++++++ cmake/toolchain/macos64-ports-gcc14.cmake | 29 ++++++ cmake/toolchain/macos64-ports-gcc15.cmake | 29 ++++++ cmake/toolchain/macos64-ports-gcc16.cmake | 29 ++++++ {toolchain => cmake/toolchain}/macos64.cmake | 0 .../toolchain}/x86_64-w64-mingw32.cmake | 0 compile.sh | 98 +++++++++---------- 34 files changed, 301 insertions(+), 49 deletions(-) rename {toolchain => cmake/toolchain}/i686-w64-mingw32.cmake (100%) rename {toolchain => cmake/toolchain}/linux32-gcc10.cmake (100%) rename {toolchain => cmake/toolchain}/linux32-gcc11.cmake (100%) rename {toolchain => cmake/toolchain}/linux32-gcc12.cmake (100%) rename {toolchain => cmake/toolchain}/linux32-gcc13.cmake (100%) create mode 100644 cmake/toolchain/linux32-gcc14.cmake create mode 100644 cmake/toolchain/linux32-gcc15.cmake create mode 100644 cmake/toolchain/linux32-gcc16.cmake rename {toolchain => cmake/toolchain}/linux32.cmake (100%) rename {toolchain => cmake/toolchain}/linux64-gcc10.cmake (100%) rename {toolchain => cmake/toolchain}/linux64-gcc11.cmake (100%) rename {toolchain => cmake/toolchain}/linux64-gcc12.cmake (100%) rename {toolchain => cmake/toolchain}/linux64-gcc13.cmake (100%) create mode 100644 cmake/toolchain/linux64-gcc14.cmake create mode 100644 cmake/toolchain/linux64-gcc15.cmake create mode 100644 cmake/toolchain/linux64-gcc16.cmake rename {toolchain => cmake/toolchain}/linux64.cmake (100%) rename {toolchain => cmake/toolchain}/macos64-brew-gcc10.cmake (100%) rename {toolchain => cmake/toolchain}/macos64-brew-gcc11.cmake (100%) rename {toolchain => cmake/toolchain}/macos64-brew-gcc12.cmake (100%) rename {toolchain => cmake/toolchain}/macos64-brew-gcc13.cmake (100%) rename {toolchain => cmake/toolchain}/macos64-brew-gcc14.cmake (100%) rename {toolchain => cmake/toolchain}/macos64-brew-gcc15.cmake (100%) create mode 100644 cmake/toolchain/macos64-brew-gcc16.cmake rename {toolchain => cmake/toolchain}/macos64-ports-gcc10.cmake (100%) rename {toolchain => cmake/toolchain}/macos64-ports-gcc11.cmake (100%) rename {toolchain => cmake/toolchain}/macos64-ports-gcc12.cmake (100%) create mode 100644 cmake/toolchain/macos64-ports-gcc13.cmake create mode 100644 cmake/toolchain/macos64-ports-gcc14.cmake create mode 100644 cmake/toolchain/macos64-ports-gcc15.cmake create mode 100644 cmake/toolchain/macos64-ports-gcc16.cmake rename {toolchain => cmake/toolchain}/macos64.cmake (100%) rename {toolchain => cmake/toolchain}/x86_64-w64-mingw32.cmake (100%) diff --git a/toolchain/i686-w64-mingw32.cmake b/cmake/toolchain/i686-w64-mingw32.cmake similarity index 100% rename from toolchain/i686-w64-mingw32.cmake rename to cmake/toolchain/i686-w64-mingw32.cmake diff --git a/toolchain/linux32-gcc10.cmake b/cmake/toolchain/linux32-gcc10.cmake similarity index 100% rename from toolchain/linux32-gcc10.cmake rename to cmake/toolchain/linux32-gcc10.cmake diff --git a/toolchain/linux32-gcc11.cmake b/cmake/toolchain/linux32-gcc11.cmake similarity index 100% rename from toolchain/linux32-gcc11.cmake rename to cmake/toolchain/linux32-gcc11.cmake diff --git a/toolchain/linux32-gcc12.cmake b/cmake/toolchain/linux32-gcc12.cmake similarity index 100% rename from toolchain/linux32-gcc12.cmake rename to cmake/toolchain/linux32-gcc12.cmake diff --git a/toolchain/linux32-gcc13.cmake b/cmake/toolchain/linux32-gcc13.cmake similarity index 100% rename from toolchain/linux32-gcc13.cmake rename to cmake/toolchain/linux32-gcc13.cmake diff --git a/cmake/toolchain/linux32-gcc14.cmake b/cmake/toolchain/linux32-gcc14.cmake new file mode 100644 index 0000000..4bbeccc --- /dev/null +++ b/cmake/toolchain/linux32-gcc14.cmake @@ -0,0 +1,18 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-14) +set(CMAKE_C_FLAGS -m32) +set(CMAKE_CXX_COMPILER g++-14) +set(CMAKE_CXX_FLAGS -m32) + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both target and host environments +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/toolchain/linux32-gcc15.cmake b/cmake/toolchain/linux32-gcc15.cmake new file mode 100644 index 0000000..328bf9f --- /dev/null +++ b/cmake/toolchain/linux32-gcc15.cmake @@ -0,0 +1,18 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-15) +set(CMAKE_C_FLAGS -m32) +set(CMAKE_CXX_COMPILER g++-15) +set(CMAKE_CXX_FLAGS -m32) + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both target and host environments +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/toolchain/linux32-gcc16.cmake b/cmake/toolchain/linux32-gcc16.cmake new file mode 100644 index 0000000..8dd4a0b --- /dev/null +++ b/cmake/toolchain/linux32-gcc16.cmake @@ -0,0 +1,18 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-16) +set(CMAKE_C_FLAGS -m32) +set(CMAKE_CXX_COMPILER g++-16) +set(CMAKE_CXX_FLAGS -m32) + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both target and host environments +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/toolchain/linux32.cmake b/cmake/toolchain/linux32.cmake similarity index 100% rename from toolchain/linux32.cmake rename to cmake/toolchain/linux32.cmake diff --git a/toolchain/linux64-gcc10.cmake b/cmake/toolchain/linux64-gcc10.cmake similarity index 100% rename from toolchain/linux64-gcc10.cmake rename to cmake/toolchain/linux64-gcc10.cmake diff --git a/toolchain/linux64-gcc11.cmake b/cmake/toolchain/linux64-gcc11.cmake similarity index 100% rename from toolchain/linux64-gcc11.cmake rename to cmake/toolchain/linux64-gcc11.cmake diff --git a/toolchain/linux64-gcc12.cmake b/cmake/toolchain/linux64-gcc12.cmake similarity index 100% rename from toolchain/linux64-gcc12.cmake rename to cmake/toolchain/linux64-gcc12.cmake diff --git a/toolchain/linux64-gcc13.cmake b/cmake/toolchain/linux64-gcc13.cmake similarity index 100% rename from toolchain/linux64-gcc13.cmake rename to cmake/toolchain/linux64-gcc13.cmake diff --git a/cmake/toolchain/linux64-gcc14.cmake b/cmake/toolchain/linux64-gcc14.cmake new file mode 100644 index 0000000..3f36e43 --- /dev/null +++ b/cmake/toolchain/linux64-gcc14.cmake @@ -0,0 +1,18 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-14) +set(CMAKE_C_FLAGS -m64) +set(CMAKE_CXX_COMPILER g++-14) +set(CMAKE_CXX_FLAGS -m64) + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both target and host environments +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/toolchain/linux64-gcc15.cmake b/cmake/toolchain/linux64-gcc15.cmake new file mode 100644 index 0000000..c6f9c7e --- /dev/null +++ b/cmake/toolchain/linux64-gcc15.cmake @@ -0,0 +1,18 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-15) +set(CMAKE_C_FLAGS -m64) +set(CMAKE_CXX_COMPILER g++-15) +set(CMAKE_CXX_FLAGS -m64) + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both target and host environments +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/toolchain/linux64-gcc16.cmake b/cmake/toolchain/linux64-gcc16.cmake new file mode 100644 index 0000000..5120f1a --- /dev/null +++ b/cmake/toolchain/linux64-gcc16.cmake @@ -0,0 +1,18 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-16) +set(CMAKE_C_FLAGS -m64) +set(CMAKE_CXX_COMPILER g++-16) +set(CMAKE_CXX_FLAGS -m64) + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both target and host environments +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/toolchain/linux64.cmake b/cmake/toolchain/linux64.cmake similarity index 100% rename from toolchain/linux64.cmake rename to cmake/toolchain/linux64.cmake diff --git a/toolchain/macos64-brew-gcc10.cmake b/cmake/toolchain/macos64-brew-gcc10.cmake similarity index 100% rename from toolchain/macos64-brew-gcc10.cmake rename to cmake/toolchain/macos64-brew-gcc10.cmake diff --git a/toolchain/macos64-brew-gcc11.cmake b/cmake/toolchain/macos64-brew-gcc11.cmake similarity index 100% rename from toolchain/macos64-brew-gcc11.cmake rename to cmake/toolchain/macos64-brew-gcc11.cmake diff --git a/toolchain/macos64-brew-gcc12.cmake b/cmake/toolchain/macos64-brew-gcc12.cmake similarity index 100% rename from toolchain/macos64-brew-gcc12.cmake rename to cmake/toolchain/macos64-brew-gcc12.cmake diff --git a/toolchain/macos64-brew-gcc13.cmake b/cmake/toolchain/macos64-brew-gcc13.cmake similarity index 100% rename from toolchain/macos64-brew-gcc13.cmake rename to cmake/toolchain/macos64-brew-gcc13.cmake diff --git a/toolchain/macos64-brew-gcc14.cmake b/cmake/toolchain/macos64-brew-gcc14.cmake similarity index 100% rename from toolchain/macos64-brew-gcc14.cmake rename to cmake/toolchain/macos64-brew-gcc14.cmake diff --git a/toolchain/macos64-brew-gcc15.cmake b/cmake/toolchain/macos64-brew-gcc15.cmake similarity index 100% rename from toolchain/macos64-brew-gcc15.cmake rename to cmake/toolchain/macos64-brew-gcc15.cmake diff --git a/cmake/toolchain/macos64-brew-gcc16.cmake b/cmake/toolchain/macos64-brew-gcc16.cmake new file mode 100644 index 0000000..c4f7e7a --- /dev/null +++ b/cmake/toolchain/macos64-brew-gcc16.cmake @@ -0,0 +1,28 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Darwin) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-16) +set(CMAKE_CXX_COMPILER g++-16) +set(CMAKE_AR gcc-ar-16) +set(CMAKE_NM gcc-nm-16) +set(CMAKE_RANLIB gcc-ranlib-16) # https://stackoverflow.com/questions/53128049/ld-archive-has-no-table-of-contents-file-error-with-homebrew +set(RANLIB gcc-ranlib-16) +# silence superfluous "has no symbols" warnings (does not help): +# set(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") +# set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") +# Do not use RPATH: +#set(CMAKE_MACOSX_RPATH FALSE) +#set(MACOSX_RPATH FALSE) +# set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +# set(CMAKE_SKIP_RPATH TRUE) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both the target and host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/toolchain/macos64-ports-gcc10.cmake b/cmake/toolchain/macos64-ports-gcc10.cmake similarity index 100% rename from toolchain/macos64-ports-gcc10.cmake rename to cmake/toolchain/macos64-ports-gcc10.cmake diff --git a/toolchain/macos64-ports-gcc11.cmake b/cmake/toolchain/macos64-ports-gcc11.cmake similarity index 100% rename from toolchain/macos64-ports-gcc11.cmake rename to cmake/toolchain/macos64-ports-gcc11.cmake diff --git a/toolchain/macos64-ports-gcc12.cmake b/cmake/toolchain/macos64-ports-gcc12.cmake similarity index 100% rename from toolchain/macos64-ports-gcc12.cmake rename to cmake/toolchain/macos64-ports-gcc12.cmake diff --git a/cmake/toolchain/macos64-ports-gcc13.cmake b/cmake/toolchain/macos64-ports-gcc13.cmake new file mode 100644 index 0000000..abaf23b --- /dev/null +++ b/cmake/toolchain/macos64-ports-gcc13.cmake @@ -0,0 +1,29 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Darwin) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-mp-13) +set(CMAKE_CXX_COMPILER g++-mp-13) +set(CMAKE_AR gcc-ar-mp-13) +set(CMAKE_NM gcc-nm-mp-13) +set(CMAKE_RANLIB gcc-ranlib-mp-13) +set(RANLIB gcc-ranlib-mp-13) +# silence superfluous "has no symbols" warnings (does not help): +# set(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") +# set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") +# Do not use RPATH: +#set(CMAKE_MACOSX_RPATH FALSE) +#set(MACOSX_RPATH TRUE) +# set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +# set(CMAKE_SKIP_RPATH TRUE) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both the target and host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + diff --git a/cmake/toolchain/macos64-ports-gcc14.cmake b/cmake/toolchain/macos64-ports-gcc14.cmake new file mode 100644 index 0000000..adf22a6 --- /dev/null +++ b/cmake/toolchain/macos64-ports-gcc14.cmake @@ -0,0 +1,29 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Darwin) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-mp-14) +set(CMAKE_CXX_COMPILER g++-mp-14) +set(CMAKE_AR gcc-ar-mp-14) +set(CMAKE_NM gcc-nm-mp-14) +set(CMAKE_RANLIB gcc-ranlib-mp-14) +set(RANLIB gcc-ranlib-mp-14) +# silence superfluous "has no symbols" warnings (does not help): +# set(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") +# set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") +# Do not use RPATH: +#set(CMAKE_MACOSX_RPATH FALSE) +#set(MACOSX_RPATH TRUE) +# set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +# set(CMAKE_SKIP_RPATH TRUE) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both the target and host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + diff --git a/cmake/toolchain/macos64-ports-gcc15.cmake b/cmake/toolchain/macos64-ports-gcc15.cmake new file mode 100644 index 0000000..13dd9e7 --- /dev/null +++ b/cmake/toolchain/macos64-ports-gcc15.cmake @@ -0,0 +1,29 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Darwin) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-mp-15) +set(CMAKE_CXX_COMPILER g++-mp-15) +set(CMAKE_AR gcc-ar-mp-15) +set(CMAKE_NM gcc-nm-mp-15) +set(CMAKE_RANLIB gcc-ranlib-mp-15) +set(RANLIB gcc-ranlib-mp-15) +# silence superfluous "has no symbols" warnings (does not help): +# set(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") +# set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") +# Do not use RPATH: +#set(CMAKE_MACOSX_RPATH FALSE) +#set(MACOSX_RPATH TRUE) +# set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +# set(CMAKE_SKIP_RPATH TRUE) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both the target and host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + diff --git a/cmake/toolchain/macos64-ports-gcc16.cmake b/cmake/toolchain/macos64-ports-gcc16.cmake new file mode 100644 index 0000000..743bb71 --- /dev/null +++ b/cmake/toolchain/macos64-ports-gcc16.cmake @@ -0,0 +1,29 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Darwin) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-mp-16) +set(CMAKE_CXX_COMPILER g++-mp-16) +set(CMAKE_AR gcc-ar-mp-16) +set(CMAKE_NM gcc-nm-mp-16) +set(CMAKE_RANLIB gcc-ranlib-mp-16) +set(RANLIB gcc-ranlib-mp-16) +# silence superfluous "has no symbols" warnings (does not help): +# set(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") +# set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") +# Do not use RPATH: +#set(CMAKE_MACOSX_RPATH FALSE) +#set(MACOSX_RPATH TRUE) +# set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +# set(CMAKE_SKIP_RPATH TRUE) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, +# search programs in both the target and host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + diff --git a/toolchain/macos64.cmake b/cmake/toolchain/macos64.cmake similarity index 100% rename from toolchain/macos64.cmake rename to cmake/toolchain/macos64.cmake diff --git a/toolchain/x86_64-w64-mingw32.cmake b/cmake/toolchain/x86_64-w64-mingw32.cmake similarity index 100% rename from toolchain/x86_64-w64-mingw32.cmake rename to cmake/toolchain/x86_64-w64-mingw32.cmake diff --git a/compile.sh b/compile.sh index db48f61..115b0d5 100755 --- a/compile.sh +++ b/compile.sh @@ -20,7 +20,7 @@ targets="$@" if [ -z "$targets" ]; then echo "Expected a target platform as an argument. The following are supported:" - for target in $(ls toolchain) ; do + for target in $(ls cmake/toolchain) ; do t=${target%%.cmake} echo -e "\t$t" done @@ -30,28 +30,18 @@ if [ -z "$targets" ]; then targets="" if [ -n "$(command -v c++)" ]; then targets="$targets linux64" - if [ -n "$(c++ -m32 --print-file-name=libstdc++.so)" ]; then + if echo 'int main(){}' | c++ -m32 -xc++ - -o /dev/null 2>&1 ; then targets="$targets linux32" fi fi - if [ -n "$(command -v g++-10)" ]; then - targets="$targets linux64-gcc10" - if [ -n "$(g++-10 -m32 --print-file-name=libstdc++.so)" ]; then - targets="$targets linux32-gcc10" + for COMPILER in g++-16 g++-15 g++-14 g++-13 g++-12 g++-11 g++-10 ; do + if [ -n "$(command -v "$COMPILER")" ]; then + targets="$targets linux64-${COMPILER/++-/cc}" + if echo 'int main(){}' | ${COMPILER} -m32 -xc++ - -o /dev/null 2>&1 ; then + targets="$targets linux32-${COMPILER/++-/cc}" + fi fi - fi - if [ -n "$(command -v g++-11)" ]; then - targets="$targets linux64-gcc11" - if [ -n "$(g++-11 -m32 --print-file-name=libstdc++.so)" ]; then - targets="$targets linux32-gcc11" - fi - fi - if [ -n "$(command -v g++-12)" ]; then - targets="$targets linux64-gcc12" - if [ -n "$(g++-12 -m32 --print-file-name=libstdc++.so)" ]; then - targets="$targets linux32-gcc12" - fi - fi + done if [ -n "$(command -v x86_64-w64-mingw32-g++)" ]; then targets="$targets x86_64-w64-mingw32" fi @@ -64,24 +54,16 @@ if [ -z "$targets" ]; then if [ -n "$(command -v c++)" ]; then targets="$targets macos64" fi - if [ -n "$(command -v g++-10)" ]; then - targets="$targets macos64-brew-gcc10" - fi - if [ -n "$(command -v g++-11)" ]; then - targets="$targets macos64-brew-gcc11" - fi - if [ -n "$(command -v g++-12)" ]; then - targets="$targets macos64-brew-gcc12" - fi - if [ -n "$(command -v g++-mp-10)" ]; then - targets="$targets macos64-ports-gcc10" - fi - if [ -n "$(command -v g++-mp-11)" ]; then - targets="$targets macos64-ports-gcc11" - fi - if [ -n "$(command -v g++-mp-12)" ]; then - targets="$targets macos64-ports-gcc12" - fi + for COMPILER in g++-16 g++-15 g++-14 g++-13 g++-12 g++-11 g++-10 ; do + if [ -n "$(command -v "$COMPILER")" ]; then + targets="$targets macos64-brew-${COMPILER}" + fi + done + for COMPILER in g++-mp-16 g++-mp-15 g++-mp-14 g++-mp-13 g++-mp-12 g++-mp-11 g++-mp-10 ; do + if [ -n "$(command -v "$COMPILER")" ]; then + targets="$targets macos64-ports-${COMPILER}" + fi + done ;; *) echo "Unknown hosting platform" @@ -106,15 +88,18 @@ fi if [ -z "$CTEST_TEST_LOAD" ]; then export CTEST_TEST_LOAD=$CORES fi +if [ -z "$CTEST_OUTPUT_ON_FAILURE" ]; then + export CTEST_OUTPUT_ON_FAILURE=1 +fi for target in $targets ; do unset CMAKE_TOOLCHAIN_FILE unset SANITIZE - if [ ! -r $PWD/toolchain/${target}.cmake ]; then - echo "The toolchain file does not exist: $PWD/toolchain/${target}.cmake" + if [ ! -r $PWD/cmake/toolchain/${target}.cmake ]; then + echo "The toolchain file does not exist: $PWD/cmake/toolchain/${target}.cmake" exit 1 else - export CMAKE_TOOLCHAIN_FILE="$PWD/toolchain/${target}.cmake" + export CMAKE_TOOLCHAIN_FILE="$PWD/cmake/toolchain/${target}.cmake" fi case $target in linux*) @@ -132,17 +117,36 @@ for target in $targets ; do i686*mingw32) extension=dll SANITIZE="-DSSP=ON" + export WINEARCH=win32 + export WINEPREFIX="${HOME}/.wine32" + if [ ! -d "$WINEPREFIX" ]; then wine cmd /c echo "Prepared WINEPREFIX"; fi libgcc_path=$($target-g++ --print-file-name=libgcc_s_dw2-1.dll) libgcc_path=$(realpath "$libgcc_path") libgcc_path=$(dirname "$libgcc_path") libwinpthread_path=$($target-g++ --print-file-name=libwinpthread-1.dll) libwinpthread_path=$(realpath "$libwinpthread_path") libwinpthread_path=$(dirname "$libwinpthread_path") - export WINEPATH="$libwinpthread_path;$libgcc_path" + export WINEPATH="${libwinpthread_path};${libgcc_path}" + echo "WINEARCH=$WINEARCH" + echo "WINEPATH=$WINEPATH" + echo "WINEPREFIX=$WINEPREFIX" ;; x86_64*mingw32) extension=dll SANITIZE="-DSSP=ON" + export WINEARCH=win64 + export WINEPREFIX="${HOME}/.wine64" + if [ ! -d "$WINEPREFIX" ]; then wine cmd /c echo "Prepared WINEPREFIX"; fi + libgcc_path=$($target-g++ --print-file-name=libgcc_s_seh-1.dll) + libgcc_path=$(realpath "$libgcc_path") + libgcc_path=$(dirname "$libgcc_path") + libwinpthread_path=$($target-g++ --print-file-name=libwinpthread-1.dll) + libwinpthread_path=$(realpath "$libwinpthread_path") + libwinpthread_path=$(dirname "$libwinpthread_path") + export WINEPATH="${libwinpthread_path};${libgcc_path}" + echo "WINEPREFIX=$WINEPREFIX" + echo "WINEPATH=$WINEPATH" + echo "WINEARCH=$WINEARCH" ;; *) echo "Unknown target platform: $target" @@ -156,11 +160,9 @@ for target in $targets ; do echo "Building debug configuration for $target" cmake --build "$BUILD_DIR" --config $CMAKE_BUILD_TYPE echo "Testing debug configuration for $target" - (cd "$BUILD_DIR" ; ctest -C $CMAKE_BUILD_TYPE --output-on-failure) + ctest --test-dir "$BUILD_DIR" -C $CMAKE_BUILD_TYPE ## Create a link to it: - if [ ! -e libtable-dbg.${extension} ]; then - ln -s "$BUILD_DIR"/src/libtable.${extension} libtable-dbg.${extension} - fi + ln -snf "${BUILD_DIR}/src/libtable.${extension}" "libtable-dbg.${extension}" export CMAKE_BUILD_TYPE=Release BUILD_DIR=build-$target-$CMAKE_BUILD_TYPE @@ -169,9 +171,7 @@ for target in $targets ; do echo "Building optimized release configuration for $target" cmake --build "$BUILD_DIR" --config $CMAKE_BUILD_TYPE echo "Testing optimized release configuration for $target" - (cd "$BUILD_DIR" ; ctest -C $CMAKE_BUILD_TYPE --output-on-failure) + ctest --test-dir "$BUILD_DIR" -C $CMAKE_BUILD_TYPE ## Create a link to it: - if [ ! -e libtable.${extension} ]; then - ln -s "$BUILD_DIR"/src/libtable.${extension} libtable.${extension} - fi + ln -snf "${BUILD_DIR}/src/libtable.${extension}" "libtable.${extension}" done From cd9073d7bfac12fd0402b6447bf67e2265a760f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Mon, 16 Mar 2026 16:32:04 +0100 Subject: [PATCH 11/12] Improved compile.bat for windows --- compile.bat | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/compile.bat b/compile.bat index c7fd9a1..ab1a20f 100644 --- a/compile.bat +++ b/compile.bat @@ -2,36 +2,35 @@ erase /F /S /Q libtable.dll libtable-dbg.dll > NUL -set BUILD_TYPE=Release -set BUILD_DIR=build-release - -echo Compiling %BUILD_TYPE% in %BUILD_DIR% -cmake -B %BUILD_DIR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% . +set CMAKE_BUILD_PARALLEL_LEVEL=%NUMBER_OF_PROCESSORS% +set CTEST_PARALLEL_LEVEL=%NUMBER_OF_PROCESSORS% +set CTEST_TEST_LOAD=%NUMBER_OF_PROCESSORS% +set CTEST_OUTPUT_ON_FAILURE=1 + +set CMAKE_BUILD_TYPE=Debug +set BUILD_DIR=build-%CMAKE_BUILD_TYPE% +echo Compiling %CMAKE_BUILD_TYPE% in %BUILD_DIR% +cmake -B %BUILD_DIR% -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% . if %ERRORLEVEL% NEQ 0 goto Failure -cmake --build %BUILD_DIR% --config %BUILD_TYPE% +cmake --build %BUILD_DIR% --config %CMAKE_BUILD_TYPE% if %ERRORLEVEL% NEQ 0 goto Failure -cd %BUILD_DIR% -ctest --build-config %BUILD_TYPE% --output-on-failure +ctest --test-dir %BUILD_DIR% --build-config %CMAKE_BUILD_TYPE% if %ERRORLEVEL% NEQ 0 goto Failure -cd .. -copy %cd%\%BUILD_DIR%\src\%BUILD_TYPE%\table.dll %cd%\libtable.dll -echo SUCCESS building release into libtable.dll - - -set BUILD_TYPE=Debug -set BUILD_DIR=build-debug +copy %cd%\%BUILD_DIR%\src\%CMAKE_BUILD_TYPE%\table.dll %cd%\libtable-dbg.dll +echo SUCCESS building debug into libtable-dbg.dll -echo Compiling %BUILD_TYPE% in %BUILD_DIR% -cmake -B %BUILD_DIR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% . +set CMAKE_BUILD_TYPE=Release +set BUILD_DIR=build-%CMAKE_BUILD_TYPE% +echo Compiling %CMAKE_BUILD_TYPE% in %BUILD_DIR% +cmake -B %BUILD_DIR% -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% . if %ERRORLEVEL% NEQ 0 goto Failure -cmake --build %BUILD_DIR% --config %BUILD_TYPE% +cmake --build %BUILD_DIR% --config %CMAKE_BUILD_TYPE% if %ERRORLEVEL% NEQ 0 goto Failure -cd %BUILD_DIR% -ctest --build-config %BUILD_TYPE% --output-on-failure +ctest --test-dir %BUILD_DIR% --build-config %CMAKE_BUILD_TYPE% --output-on-failure if %ERRORLEVEL% NEQ 0 goto Failure -cd .. -copy %cd%\%BUILD_DIR%\src\%BUILD_TYPE%\table.dll %cd%\libtable-dbg.dll -echo SUCCESS building debug into libtable-dbg.dll +copy %cd%\%BUILD_DIR%\src\%CMAKE_BUILD_TYPE%\table.dll %cd%\libtable.dll +echo SUCCESS building release into libtable.dll + start "" "%cd%" pause From 48d8b7762ab3803730e37c132d6ed9e0b2c7fda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Mon, 16 Mar 2026 16:39:55 +0100 Subject: [PATCH 12/12] Fixed header inclusion for AppleClang --- tests/table_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/table_test.cpp b/tests/table_test.cpp index 546b059..49501a8 100644 --- a/tests/table_test.cpp +++ b/tests/table_test.cpp @@ -10,6 +10,7 @@ #include #include #include +#include TEST_SUITE_BEGIN("libtable");