From 45ee6e68ef00ef0ce995fa864b0ccb0c9749d9c6 Mon Sep 17 00:00:00 2001 From: Martin Nygren Date: Sat, 29 Aug 2026 12:27:21 +0100 Subject: [PATCH 1/5] docs: mark legacy dll.h functions with dds_c_api.h equivalents as deprecated Adds @deprecated doxygen tags (matching the existing SetMaxThreads / SetThreading / SetResources / FreeMemory pattern) to the legacy C API functions that have a direct replacement in the modern SolverContext-based API and its dds_c_api.h C-ABI shim: - SolveBoard -> SolveBoard(SolverContext&, ...) / dds_c_solve_board - CalcDDtable, CalcDDtableN -> calc_dd_table(SolverContext&, ...) / dds_c_calc_dd_table - CalcDDtablePBN, CalcDDtablePBNN -> calc_dd_table_pbn(SolverContext&, ...) / dds_c_calc_dd_table_pbn - CalcPar -> calc_par(SolverContext&, ...) / dds_c_calc_par CalcPar previously had no doc comment at all; it now gets a full block. Functions with no dds_c_api.h equivalent (SolveBoardPBN, CalcParPBN, the batch CalcAllTables*/SolveAllBoards*/SolveAllChunks* family, sides/dealer par helpers, text-format converters, play analysis) are left untouched. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FNjHJrNuYyeqgMKY4v2urk --- library/src/api/dll.h | 61 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/library/src/api/dll.h b/library/src/api/dll.h index b6050aa3..470c729e 100644 --- a/library/src/api/dll.h +++ b/library/src/api/dll.h @@ -527,7 +527,11 @@ EXTERN_C DLLEXPORT auto STDCALL SetResources( EXTERN_C DLLEXPORT auto STDCALL FreeMemory() -> void; /** - * @brief Solve a single bridge Deal using double dummy analysis. + * @brief Solve a single bridge Deal using double dummy analysis + * + * @deprecated Use SolverContext with the SolveBoard(SolverContext&, ...) + * overload instead. + * See docs/api_migration.md for modern C++ API examples. * * @param dl The Deal to analyze * @param target Target number of tricks @@ -536,6 +540,11 @@ EXTERN_C DLLEXPORT auto STDCALL FreeMemory() -> void; * @param futp Pointer to result structure * @param threadIndex Index of thread to use * @return 1 on success, error code otherwise + * + * This function is part of the legacy C API and is maintained for backward + * compatibility. New code should use the modern C++ API with SolverContext, + * which accumulates transposition-table knowledge across calls instead of + * relying on the internal thread-indexed memory pools. */ EXTERN_C DLLEXPORT auto STDCALL SolveBoard( struct Deal dl, @@ -567,9 +576,18 @@ EXTERN_C DLLEXPORT auto STDCALL SolveBoardPBN( /** * @brief Calculate the double dummy table for a given Deal. * + * @deprecated Use SolverContext with the calc_dd_table(SolverContext&, ...) + * overload instead. + * See docs/api_migration.md for modern C++ API examples. + * * @param tableDeal Deal for which to calculate the table * @param tablep Pointer to result table * @return 1 on success, error code otherwise + * + * This function is part of the legacy C API and is maintained for backward + * compatibility. New code should use the modern C++ API with SolverContext, + * which allows the transposition table and thread resources to be reused + * across calls instead of being reallocated internally on each call. */ EXTERN_C DLLEXPORT auto STDCALL CalcDDtable( struct DdTableDeal tableDeal, @@ -578,6 +596,13 @@ EXTERN_C DLLEXPORT auto STDCALL CalcDDtable( /** * @brief CalcDDtable with an explicit worker-thread cap. * + * @deprecated Use SolverContext with the calc_dd_table(SolverContext&, ...) + * overload instead; the modern API computes the table on the + * calling thread, so per-call thread caps no longer apply - + * the embedding application controls parallelism (typically one + * SolverContext per worker thread). + * See docs/api_migration.md for modern C++ API examples. + * * @param maxThreads Maximum worker threads; <= 0 selects the automatic * (hardware_concurrency) default. */ @@ -589,9 +614,18 @@ EXTERN_C DLLEXPORT auto STDCALL CalcDDtableN( /** * @brief Calculate the double dummy table for a PBN Deal. * + * @deprecated Use SolverContext with the calc_dd_table_pbn(SolverContext&, ...) + * overload instead. + * See docs/api_migration.md for modern C++ API examples. + * * @param tableDealPBN PBN Deal for which to calculate the table * @param tablep Pointer to result table * @return 1 on success, error code otherwise + * + * This function is part of the legacy C API and is maintained for backward + * compatibility. New code should use the modern C++ API with SolverContext, + * which allows the transposition table and thread resources to be reused + * across calls instead of being reallocated internally on each call. */ EXTERN_C DLLEXPORT auto STDCALL CalcDDtablePBN( struct DdTableDealPBN tableDealPBN, @@ -600,6 +634,13 @@ EXTERN_C DLLEXPORT auto STDCALL CalcDDtablePBN( /** * @brief CalcDDtablePBN with an explicit worker-thread cap. * + * @deprecated Use SolverContext with the calc_dd_table_pbn(SolverContext&, ...) + * overload instead; the modern API computes the table on the + * calling thread, so per-call thread caps no longer apply - + * the embedding application controls parallelism (typically one + * SolverContext per worker thread). + * See docs/api_migration.md for modern C++ API examples. + * * @param maxThreads Maximum worker threads; <= 0 selects the automatic * (hardware_concurrency) default. */ @@ -772,6 +813,24 @@ EXTERN_C DLLEXPORT auto STDCALL Par( struct ParResults * presp, int vulnerable) -> int; +/** + * @brief Calculate the double dummy table and par result for a given Deal. + * + * @deprecated Use SolverContext with the calc_par(SolverContext&, ...) + * overload instead. + * See docs/api_migration.md for modern C++ API examples. + * + * @param tableDeal Deal for which to calculate the table + * @param vulnerable Vulnerability (0 = None, 1 = Both, 2 = NS, 3 = EW) + * @param tablep Pointer to result table + * @param presp Pointer to result par information + * @return 1 on success, error code otherwise + * + * This function is part of the legacy C API and is maintained for backward + * compatibility. New code should use the modern C++ API with SolverContext, + * which allows the transposition table and thread resources to be reused + * across calls instead of being reallocated internally on each call. + */ EXTERN_C DLLEXPORT auto STDCALL CalcPar( struct DdTableDeal tableDeal, int vulnerable, From 5f8d4b1123fbcc8febffed259d5a2dd92f71fb80 Mon Sep 17 00:00:00 2001 From: Martin Nygren Date: Sat, 29 Aug 2026 12:52:47 +0100 Subject: [PATCH 2/5] feat: add dds_c_api.h entries for remaining single-board/utility functions Extends the pure-C ABI shim (dds_c_api.h/.cpp) so FFI consumers (Java FFM, .NET, ctypes) have a consistent, complete header to bind against, without needing dll.h for these functions too: Context-free utilities (no SolverContext; forward straight to the existing dll.h PascalCase function since it's already POD-only/extern "C"): - dds_c_par_from_table (mirrors the modern calc_par_from_table name) - dds_c_sides_par, dds_c_dealer_par, dds_c_dealer_par_bin, dds_c_sides_par_bin - dds_c_convert_to_dealer_text_format, dds_c_convert_to_sides_text_format - dds_c_get_dds_info, dds_c_error_message PBN single-board functions (needed new SolverContext-taking C++ overloads first, following the existing calc_dd_table_pbn pattern - convert PBN to binary via the internal convert_from_pbn() and delegate): - solve_board_pbn(SolverContext&, ...) in solve_board.hpp/solver_context_adapter.cpp - calc_par_pbn(SolverContext&, ...) in calc_par.hpp/calc_par.cpp - dds_solve_board_pbn / dds_calc_par_pbn in dds_api.hpp/dds_api.cpp - dds_c_solve_board_pbn / dds_c_calc_par_pbn in dds_c_api.h/.cpp Also marks SolveBoardPBN and CalcParPBN as @deprecated in dll.h now that they have dds_c_api.h equivalents, matching the pattern already applied to SolveBoard/CalcDDtable/CalcPar. CalcParPBN previously had no doc comment; it now gets a full block. Out of scope (per plan): AnalysePlayBin/AnalysePlayPBN (no modern play-analysis C++ API exists yet) and the CalcAllTables*/SolveAllBoards* batch family (would need new heap-based shim entries built on the internal parallel_all_boards_n primitive). SolveAllChunks* are already marked as dying aliases in the codebase and are not touched. Adds null-handle/null-pointer safety tests and functional tests for all eleven new entries in dds_c_api_test.cpp. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FNjHJrNuYyeqgMKY4v2urk --- library/src/api/calc_par.hpp | 21 +++ library/src/api/dds_api.hpp | 16 +- library/src/api/dds_c_api.cpp | 167 ++++++++++++++++++++- library/src/api/dds_c_api.h | 57 ++++++++ library/src/api/dll.h | 27 ++++ library/src/api/solve_board.hpp | 24 +++ library/src/calc_par.cpp | 15 ++ library/src/dds_api.cpp | 15 ++ library/src/solver_context_adapter.cpp | 24 +++ library/tests/dds_c_api_test.cpp | 195 +++++++++++++++++++++++++ 10 files changed, 558 insertions(+), 3 deletions(-) diff --git a/library/src/api/calc_par.hpp b/library/src/api/calc_par.hpp index 5dc3ebb5..cf0e4204 100644 --- a/library/src/api/calc_par.hpp +++ b/library/src/api/calc_par.hpp @@ -66,6 +66,27 @@ auto calc_par( DdTableResults* table_results, ParResults* par_results) -> int; +/** + * @brief Calculate par score and contracts for a PBN-format deal table with + * explicit solver context. + * + * Converts the PBN deal to binary format and delegates to + * calc_par(SolverContext&, ...). + * + * @param ctx Solver context for resource management and TT reuse + * @param table_deal_pbn Deal in PBN format + * @param vulnerable Vulnerability (0=None, 1=Both, 2=NS, 3=EW) + * @param table_results Output: double dummy table results + * @param par_results Output: par score and contract strings + * @return Error code (RETURN_NO_FAULT on success, RETURN_PBN_FAULT on parse error) + */ +auto calc_par_pbn( + SolverContext& ctx, + const DdTableDealPBN& table_deal_pbn, + int vulnerable, + DdTableResults* table_results, + ParResults* par_results) -> int; + /** * @brief Calculate par from pre-computed double dummy table. * diff --git a/library/src/api/dds_api.hpp b/library/src/api/dds_api.hpp index 92c6589f..a57f89fd 100644 --- a/library/src/api/dds_api.hpp +++ b/library/src/api/dds_api.hpp @@ -46,6 +46,13 @@ extern "C" { int mode, FutureTricks* futp) -> int; + EXTERN_C DLLEXPORT auto dds_solve_board_pbn(DDS_SOLVER_CTX ctx, + const DealPBN& dlpbn, + int target, + int solutions, + int mode, + FutureTricks* futp) -> int; + EXTERN_C DLLEXPORT auto dds_calc_dd_table( DDS_SOLVER_CTX ctx, const DdTableDeal& table_deal, @@ -63,6 +70,13 @@ extern "C" { int vulnerable, DdTableResults* table_results, ParResults* par_results) -> int; - + + EXTERN_C DLLEXPORT auto dds_calc_par_pbn( + DDS_SOLVER_CTX ctx, + const DdTableDealPBN& table_deal_pbn, + int vulnerable, + DdTableResults* table_results, + ParResults* par_results) -> int; + } diff --git a/library/src/api/dds_c_api.cpp b/library/src/api/dds_c_api.cpp index ff9892e6..5cb8c94d 100644 --- a/library/src/api/dds_c_api.cpp +++ b/library/src/api/dds_c_api.cpp @@ -1,11 +1,18 @@ /* DDS, a bridge double dummy solver. - Implementation of the pure-C ABI shim. Each function casts the opaque - void* handle back to SolverContext* and forwards to the reference-taking + Implementation of the pure-C ABI shim. Most functions cast the opaque + void* handle back to SolverContext* and forward to the reference-taking dds_* functions declared in dds_api.hpp. SolverConfig / TTKind stay internal to this translation unit and never cross the shim boundary. + A second, smaller group of functions needs no SolverContext at all: they + operate on already-produced data (a DdTableResults/ParResultsMaster) or + static library info. Those take no handle and forward straight to the + corresponding legacy dll.h function, which is already POD-only and + extern "C" — there is no C++ type to keep off the ABI boundary, so no + dds_api.hpp intermediate is needed for them. + See LICENSE and README. */ @@ -61,6 +68,22 @@ DLLEXPORT int dds_c_solve_board(DDS_C_SOLVER_CTX ctx, } } +DLLEXPORT int dds_c_solve_board_pbn(DDS_C_SOLVER_CTX ctx, + const struct DealPBN* dlpbn, + int target, int solutions, int mode, + struct FutureTricks* futp) +{ + if (ctx == nullptr || dlpbn == nullptr || futp == nullptr) + return RETURN_UNKNOWN_FAULT; + + try { + return dds_solve_board_pbn(static_cast(ctx), + *dlpbn, target, solutions, mode, futp); + } catch (...) { + return RETURN_UNKNOWN_FAULT; + } +} + DLLEXPORT int dds_c_calc_dd_table(DDS_C_SOLVER_CTX ctx, const struct DdTableDeal* deal, struct DdTableResults* results) @@ -92,6 +115,23 @@ DLLEXPORT int dds_c_calc_par(DDS_C_SOLVER_CTX ctx, } } +DLLEXPORT int dds_c_calc_par_pbn(DDS_C_SOLVER_CTX ctx, + const struct DdTableDealPBN* deal, + int vulnerable, + struct DdTableResults* results, + struct ParResults* par) +{ + if (ctx == nullptr || deal == nullptr || results == nullptr || par == nullptr) + return RETURN_UNKNOWN_FAULT; + + try { + return dds_calc_par_pbn(static_cast(ctx), + *deal, vulnerable, results, par); + } catch (...) { + return RETURN_UNKNOWN_FAULT; + } +} + DLLEXPORT DDS_C_SOLVER_CTX dds_c_create_solvercontext(int tt_kind, int def_mb, int max_mb) { @@ -207,4 +247,127 @@ DLLEXPORT void dds_c_log_clear(DDS_C_SOLVER_CTX ctx) } } +/* --- Context-free utilities: no SolverContext, forward straight to the + legacy dll.h function. --- */ + +DLLEXPORT int dds_c_par_from_table(const struct DdTableResults* table, + int vulnerable, + struct ParResults* par) +{ + if (table == nullptr || par == nullptr) + return RETURN_UNKNOWN_FAULT; + + try { + return Par(table, par, vulnerable); + } catch (...) { + return RETURN_UNKNOWN_FAULT; + } +} + +DLLEXPORT int dds_c_sides_par(const struct DdTableResults* table, + struct ParResultsDealer sides_res[2], + int vulnerable) +{ + if (table == nullptr || sides_res == nullptr) + return RETURN_UNKNOWN_FAULT; + + try { + return SidesPar(table, sides_res, vulnerable); + } catch (...) { + return RETURN_UNKNOWN_FAULT; + } +} + +DLLEXPORT int dds_c_dealer_par(const struct DdTableResults* table, + struct ParResultsDealer* par, + int dealer, int vulnerable) +{ + if (table == nullptr || par == nullptr) + return RETURN_UNKNOWN_FAULT; + + try { + return DealerPar(table, par, dealer, vulnerable); + } catch (...) { + return RETURN_UNKNOWN_FAULT; + } +} + +DLLEXPORT int dds_c_dealer_par_bin(const struct DdTableResults* table, + struct ParResultsMaster* par, + int dealer, int vulnerable) +{ + if (table == nullptr || par == nullptr) + return RETURN_UNKNOWN_FAULT; + + try { + return DealerParBin(table, par, dealer, vulnerable); + } catch (...) { + return RETURN_UNKNOWN_FAULT; + } +} + +DLLEXPORT int dds_c_sides_par_bin(const struct DdTableResults* table, + struct ParResultsMaster sides_res[2], + int vulnerable) +{ + if (table == nullptr || sides_res == nullptr) + return RETURN_UNKNOWN_FAULT; + + try { + return SidesParBin(table, sides_res, vulnerable); + } catch (...) { + return RETURN_UNKNOWN_FAULT; + } +} + +DLLEXPORT int dds_c_convert_to_dealer_text_format(const struct ParResultsMaster* par, + char* resp) +{ + if (par == nullptr || resp == nullptr) + return RETURN_UNKNOWN_FAULT; + + try { + return ConvertToDealerTextFormat(par, resp); + } catch (...) { + return RETURN_UNKNOWN_FAULT; + } +} + +DLLEXPORT int dds_c_convert_to_sides_text_format(const struct ParResultsMaster* par, + struct ParTextResults* resp) +{ + if (par == nullptr || resp == nullptr) + return RETURN_UNKNOWN_FAULT; + + try { + return ConvertToSidesTextFormat(par, resp); + } catch (...) { + return RETURN_UNKNOWN_FAULT; + } +} + +DLLEXPORT void dds_c_get_dds_info(struct DDSInfo* info) +{ + if (info == nullptr) + return; + + try { + GetDDSInfo(info); + } catch (...) { + // Must not unwind through the C ABI boundary. + } +} + +DLLEXPORT void dds_c_error_message(int code, char line[80]) +{ + if (line == nullptr) + return; + + try { + ErrorMessage(code, line); + } catch (...) { + // Must not unwind through the C ABI boundary. + } +} + } // extern "C" diff --git a/library/src/api/dds_c_api.h b/library/src/api/dds_c_api.h index 1e11053c..c82ded07 100644 --- a/library/src/api/dds_c_api.h +++ b/library/src/api/dds_c_api.h @@ -42,6 +42,12 @@ DLLEXPORT int dds_c_solve_board(DDS_C_SOLVER_CTX ctx, int target, int solutions, int mode, struct FutureTricks* futp); +/* Solve a single board in PBN format. Returns a RETURN_* status code. */ +DLLEXPORT int dds_c_solve_board_pbn(DDS_C_SOLVER_CTX ctx, + const struct DealPBN* dlpbn, + int target, int solutions, int mode, + struct FutureTricks* futp); + /* Compute the double dummy table for a deal. */ DLLEXPORT int dds_c_calc_dd_table(DDS_C_SOLVER_CTX ctx, const struct DdTableDeal* deal, @@ -54,6 +60,14 @@ DLLEXPORT int dds_c_calc_par(DDS_C_SOLVER_CTX ctx, struct DdTableResults* results, struct ParResults* par); +/* Compute the par result for a PBN-format deal (computes the DD table + internally). */ +DLLEXPORT int dds_c_calc_par_pbn(DDS_C_SOLVER_CTX ctx, + const struct DdTableDealPBN* deal, + int vulnerable, + struct DdTableResults* results, + struct ParResults* par); + /* Creation with explicit transposition-table configuration. The C++ SolverConfig is decomposed into scalars rather than mirrored as a struct: passing a struct by value is exactly the ABI question this shim exists to avoid, and a mirror @@ -81,6 +95,49 @@ DLLEXPORT void dds_c_reset_best_moves_lite(DDS_C_SOLVER_CTX ctx); DLLEXPORT void dds_c_log_append(DDS_C_SOLVER_CTX ctx, const char* msg); DLLEXPORT void dds_c_log_clear(DDS_C_SOLVER_CTX ctx); +/* Context-free utilities. These operate on already-produced data (a + DdTableResults/ParResultsMaster) or static library info; they need no + SolverContext and so take no handle. */ + +/* Compute par from an already-computed double dummy table. */ +DLLEXPORT int dds_c_par_from_table(const struct DdTableResults* table, + int vulnerable, + struct ParResults* par); + +/* Compute par from both the NS and EW dealing sides' viewpoints. */ +DLLEXPORT int dds_c_sides_par(const struct DdTableResults* table, + struct ParResultsDealer sides_res[2], + int vulnerable); + +/* Compute par for a specific dealer. */ +DLLEXPORT int dds_c_dealer_par(const struct DdTableResults* table, + struct ParResultsDealer* par, + int dealer, int vulnerable); + +/* Binary (ContractType) variant of dds_c_dealer_par. */ +DLLEXPORT int dds_c_dealer_par_bin(const struct DdTableResults* table, + struct ParResultsMaster* par, + int dealer, int vulnerable); + +/* Binary (ContractType) variant of dds_c_sides_par. */ +DLLEXPORT int dds_c_sides_par_bin(const struct DdTableResults* table, + struct ParResultsMaster sides_res[2], + int vulnerable); + +/* Format a ParResultsMaster as dealer-oriented text. */ +DLLEXPORT int dds_c_convert_to_dealer_text_format(const struct ParResultsMaster* par, + char* resp); + +/* Format a ParResultsMaster as sides-oriented text. */ +DLLEXPORT int dds_c_convert_to_sides_text_format(const struct ParResultsMaster* par, + struct ParTextResults* resp); + +/* Query library version/build information. */ +DLLEXPORT void dds_c_get_dds_info(struct DDSInfo* info); + +/* Map a RETURN_* status code to its human-readable text. */ +DLLEXPORT void dds_c_error_message(int code, char line[80]); + #ifdef __cplusplus } #endif diff --git a/library/src/api/dll.h b/library/src/api/dll.h index 470c729e..0b0100ac 100644 --- a/library/src/api/dll.h +++ b/library/src/api/dll.h @@ -557,6 +557,10 @@ EXTERN_C DLLEXPORT auto STDCALL SolveBoard( /** * @brief Solve a single bridge Deal in PBN format using double dummy analysis. * + * @deprecated Use SolverContext with the solve_board_pbn(SolverContext&, ...) + * overload instead. + * See docs/api_migration.md for modern C++ API examples. + * * @param dlpbn The PBN Deal to analyze * @param target Target number of tricks * @param solutions Solution mode @@ -564,6 +568,11 @@ EXTERN_C DLLEXPORT auto STDCALL SolveBoard( * @param futp Pointer to result structure * @param thrId Index of thread to use * @return 1 on success, error code otherwise + * + * This function is part of the legacy C API and is maintained for backward + * compatibility. New code should use the modern C++ API with SolverContext, + * which allows the transposition table and thread resources to be reused + * across calls instead of being reallocated internally on each call. */ EXTERN_C DLLEXPORT auto STDCALL SolveBoardPBN( struct DealPBN dlpbn, @@ -837,6 +846,24 @@ EXTERN_C DLLEXPORT auto STDCALL CalcPar( struct DdTableResults * tablep, struct ParResults * presp) -> int; +/** + * @brief Calculate the double dummy table and par result for a PBN Deal. + * + * @deprecated Use SolverContext with the calc_par_pbn(SolverContext&, ...) + * overload instead. + * See docs/api_migration.md for modern C++ API examples. + * + * @param tableDealPBN PBN Deal for which to calculate the table + * @param tablep Pointer to result table + * @param vulnerable Vulnerability (0 = None, 1 = Both, 2 = NS, 3 = EW) + * @param presp Pointer to result par information + * @return 1 on success, error code otherwise + * + * This function is part of the legacy C API and is maintained for backward + * compatibility. New code should use the modern C++ API with SolverContext, + * which allows the transposition table and thread resources to be reused + * across calls instead of being reallocated internally on each call. + */ EXTERN_C DLLEXPORT auto STDCALL CalcParPBN( struct DdTableDealPBN tableDealPBN, struct DdTableResults * tablep, diff --git a/library/src/api/solve_board.hpp b/library/src/api/solve_board.hpp index 50f056f1..c64d9d36 100644 --- a/library/src/api/solve_board.hpp +++ b/library/src/api/solve_board.hpp @@ -53,3 +53,27 @@ auto SolveBoard( int solutions, int mode, FutureTricks* futp) -> int; + +/** + * @brief Solve a single deal in PBN format with explicit solver context. + * + * C++-only overload that accepts an explicit SolverContext and a PBN-format + * deal, allowing clients to manage solver state and resources across + * multiple solve operations. Converts to binary format and delegates to + * solve_board(SolverContext&, ...). + * + * @param ctx Solver context containing state and resources + * @param dlpbn Deal in PBN format to solve + * @param target Target number of tricks (-1 for maximum) + * @param solutions Solution mode (1=one, 2=all, 3=all with ranks) + * @param mode Solve mode (0=auto, 1-3=specific modes) + * @param futp Output structure for future tricks + * @return Error code (RETURN_NO_FAULT on success, RETURN_PBN_FAULT on parse error) + */ +auto solve_board_pbn( + SolverContext& ctx, + const DealPBN& dlpbn, + int target, + int solutions, + int mode, + FutureTricks* futp) -> int; diff --git a/library/src/calc_par.cpp b/library/src/calc_par.cpp index 0fa90abb..95da5a6b 100644 --- a/library/src/calc_par.cpp +++ b/library/src/calc_par.cpp @@ -10,6 +10,7 @@ #include #include #include +#include auto calc_par( const DdTableDeal& table_deal, @@ -45,6 +46,20 @@ auto calc_par( return Par(table_results, par_results, vulnerable); } +auto calc_par_pbn( + SolverContext& ctx, + const DdTableDealPBN& table_deal_pbn, + int vulnerable, + DdTableResults* table_results, + ParResults* par_results) -> int +{ + DdTableDeal table_deal; + if (convert_from_pbn(table_deal_pbn.cards, table_deal.cards) != RETURN_NO_FAULT) + return RETURN_PBN_FAULT; + + return calc_par(ctx, table_deal, vulnerable, table_results, par_results); +} + auto calc_par_from_table( const DdTableResults* table_results, int vulnerable, diff --git a/library/src/dds_api.cpp b/library/src/dds_api.cpp index 841aac9a..cd570556 100644 --- a/library/src/dds_api.cpp +++ b/library/src/dds_api.cpp @@ -75,6 +75,11 @@ DLLEXPORT auto dds_solve_board(DDS_SOLVER_CTX ctx, const Deal& dl, int target, i futp); } +DLLEXPORT auto dds_solve_board_pbn(DDS_SOLVER_CTX ctx, const DealPBN& dlpbn, int target, int solutions, int mode, FutureTricks* futp) -> int +{ + return solve_board_pbn(*ctx, dlpbn, target, solutions, mode, futp); +} + DLLEXPORT auto dds_calc_dd_table(DDS_SOLVER_CTX ctx, const DdTableDeal& table_deal, DdTableResults* table_results) -> int { return calc_dd_table(*ctx, table_deal, table_results); @@ -96,3 +101,13 @@ DLLEXPORT auto dds_calc_par( return calc_par(*ctx, table_deal, vulnerable, table_results, par_results); } +DLLEXPORT auto dds_calc_par_pbn( + DDS_SOLVER_CTX ctx, + const DdTableDealPBN& table_deal_pbn, + int vulnerable, + DdTableResults* table_results, + ParResults* par_results) -> int +{ + return calc_par_pbn(*ctx, table_deal_pbn, vulnerable, table_results, par_results); +} + diff --git a/library/src/solver_context_adapter.cpp b/library/src/solver_context_adapter.cpp index 826ca498..7440599f 100644 --- a/library/src/solver_context_adapter.cpp +++ b/library/src/solver_context_adapter.cpp @@ -1,5 +1,6 @@ #include #include +#include auto solve_board( SolverContext& ctx, @@ -24,3 +25,26 @@ auto SolveBoard( { return solve_board(ctx, dl, target, solutions, mode, futp); } + +auto solve_board_pbn( + SolverContext& ctx, + const DealPBN& dlpbn, + int target, + int solutions, + int mode, + FutureTricks* futp) -> int +{ + Deal dl; + if (convert_from_pbn(dlpbn.remainCards, dl.remainCards) != RETURN_NO_FAULT) + return RETURN_PBN_FAULT; + + for (int k = 0; k <= 2; k++) + { + dl.currentTrickRank[k] = dlpbn.currentTrickRank[k]; + dl.currentTrickSuit[k] = dlpbn.currentTrickSuit[k]; + } + dl.first = dlpbn.first; + dl.trump = dlpbn.trump; + + return solve_board(ctx, dl, target, solutions, mode, futp); +} diff --git a/library/tests/dds_c_api_test.cpp b/library/tests/dds_c_api_test.cpp index 507d3476..95b04899 100644 --- a/library/tests/dds_c_api_test.cpp +++ b/library/tests/dds_c_api_test.cpp @@ -66,6 +66,15 @@ struct DdTableDeal MakeReferenceTableDeal() return deal; } +struct DealPBN MakeReferenceDealPbn() +{ + struct DealPBN dlpbn = {}; + dlpbn.trump = 0; // spades + dlpbn.first = 0; // North leads + std::snprintf(dlpbn.remainCards, sizeof dlpbn.remainCards, "%s", kReferencePbn); + return dlpbn; +} + // Solve the reference board on ctx and return the trick count. int SolveReference(DDS_C_SOLVER_CTX ctx) { @@ -85,6 +94,7 @@ int SolveReference(DDS_C_SOLVER_CTX ctx) TEST(DdsCApiNullHandle, IntReturningEntryPointsFailFast) { const struct Deal dl = MakeReferenceDeal(); + const struct DealPBN dlpbn = MakeReferenceDealPbn(); const struct DdTableDeal table_deal = MakeReferenceTableDeal(); struct DdTableDealPBN pbn_deal = {}; struct FutureTricks fut = {}; @@ -92,9 +102,45 @@ TEST(DdsCApiNullHandle, IntReturningEntryPointsFailFast) struct ParResults par = {}; EXPECT_EQ(dds_c_solve_board(nullptr, &dl, -1, 1, 1, &fut), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_solve_board_pbn(nullptr, &dlpbn, -1, 1, 1, &fut), RETURN_UNKNOWN_FAULT); EXPECT_EQ(dds_c_calc_dd_table(nullptr, &table_deal, &results), RETURN_UNKNOWN_FAULT); EXPECT_EQ(dds_c_calc_dd_table_pbn(nullptr, &pbn_deal, &results), RETURN_UNKNOWN_FAULT); EXPECT_EQ(dds_c_calc_par(nullptr, &table_deal, 0, &results, &par), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_calc_par_pbn(nullptr, &pbn_deal, 0, &results, &par), RETURN_UNKNOWN_FAULT); +} + +// The context-free utilities take no handle at all; they must instead reject +// null data pointers (the only thing they could otherwise dereference). +TEST(DdsCApiNullHandle, ContextFreeUtilitiesRejectNullPointers) +{ + struct DdTableResults results = {}; + struct ParResults par = {}; + struct ParResultsDealer dealer_res = {}; + struct ParResultsDealer sides_res[2] = {}; + struct ParResultsMaster dealer_bin = {}; + struct ParResultsMaster sides_bin[2] = {}; + struct ParTextResults text = {}; + char line[80] = {}; + + EXPECT_EQ(dds_c_par_from_table(nullptr, 0, &par), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_par_from_table(&results, 0, nullptr), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_sides_par(nullptr, sides_res, 0), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_sides_par(&results, nullptr, 0), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_dealer_par(nullptr, &dealer_res, 0, 0), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_dealer_par(&results, nullptr, 0, 0), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_dealer_par_bin(nullptr, &dealer_bin, 0, 0), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_dealer_par_bin(&results, nullptr, 0, 0), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_sides_par_bin(nullptr, sides_bin, 0), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_sides_par_bin(&results, nullptr, 0), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_convert_to_dealer_text_format(nullptr, line), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_convert_to_dealer_text_format(&dealer_bin, nullptr), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_convert_to_sides_text_format(nullptr, &text), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_convert_to_sides_text_format(&dealer_bin, nullptr), RETURN_UNKNOWN_FAULT); + + // Void-returning: must return without dereferencing. + dds_c_get_dds_info(nullptr); + dds_c_error_message(RETURN_NO_FAULT, nullptr); + SUCCEED(); } TEST(DdsCApiNullHandle, VoidReturningEntryPointsAreNoOps) @@ -120,11 +166,15 @@ TEST(DdsCApiNullArgument, PointerArgumentsAreValidated) struct DdTableDealPBN pbn_deal = {}; const struct DdTableDeal table_deal = MakeReferenceTableDeal(); struct FutureTricks fut = {}; + struct ParResults par = {}; EXPECT_EQ(dds_c_solve_board(ctx, nullptr, -1, 1, 1, &fut), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_solve_board_pbn(ctx, nullptr, -1, 1, 1, &fut), RETURN_UNKNOWN_FAULT); EXPECT_EQ(dds_c_calc_dd_table_pbn(ctx, nullptr, &results), RETURN_UNKNOWN_FAULT); EXPECT_EQ(dds_c_calc_dd_table_pbn(ctx, &pbn_deal, nullptr), RETURN_UNKNOWN_FAULT); EXPECT_EQ(dds_c_calc_par(ctx, &table_deal, 0, &results, nullptr), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_calc_par_pbn(ctx, nullptr, 0, &results, &par), RETURN_UNKNOWN_FAULT); + EXPECT_EQ(dds_c_calc_par_pbn(ctx, &pbn_deal, 0, &results, nullptr), RETURN_UNKNOWN_FAULT); // A null message must be ignored rather than passed through to strlen. dds_c_log_append(ctx, nullptr); @@ -292,4 +342,149 @@ TEST(DdsCApiPar, ProducesNonEmptyScore) dds_c_destroy_solvercontext(ctx); } +TEST(DdsCApiSolveBoard, PbnMatchesBinary) +{ + DDS_C_SOLVER_CTX ctx = dds_c_create_solvercontext_default(); + ASSERT_NE(ctx, nullptr); + + const struct DealPBN dlpbn = MakeReferenceDealPbn(); + struct FutureTricks fut = {}; + ASSERT_EQ(dds_c_solve_board_pbn(ctx, &dlpbn, -1, 1, 1, &fut), RETURN_NO_FAULT); + EXPECT_EQ(fut.score[0], kExpectedTricks); + + dds_c_destroy_solvercontext(ctx); +} + +TEST(DdsCApiPar, PbnMatchesBinary) +{ + DDS_C_SOLVER_CTX ctx = dds_c_create_solvercontext_default(); + ASSERT_NE(ctx, nullptr); + + const struct DdTableDeal binary_deal = MakeReferenceTableDeal(); + struct DdTableResults binary_results = {}; + struct ParResults binary_par = {}; + ASSERT_EQ(dds_c_calc_par(ctx, &binary_deal, 0, &binary_results, &binary_par), + RETURN_NO_FAULT); + + struct DdTableDealPBN pbn_deal = {}; + std::snprintf(pbn_deal.cards, sizeof pbn_deal.cards, "%s", kReferencePbn); + struct DdTableResults pbn_results = {}; + struct ParResults pbn_par = {}; + ASSERT_EQ(dds_c_calc_par_pbn(ctx, &pbn_deal, 0, &pbn_results, &pbn_par), + RETURN_NO_FAULT); + + EXPECT_STREQ(pbn_par.par_score[0], binary_par.par_score[0]); + EXPECT_STREQ(pbn_par.par_score[1], binary_par.par_score[1]); + + dds_c_destroy_solvercontext(ctx); +} + +// --------------------------------------------------------------------------- +// Context-free utilities: no SolverContext involved, so these are exercised +// directly against a table produced once via dds_c_calc_dd_table. +// --------------------------------------------------------------------------- + +class DdsCApiParUtilities : public testing::Test { +protected: + void SetUp() override + { + ctx_ = dds_c_create_solvercontext_default(); + ASSERT_NE(ctx_, nullptr); + + const struct DdTableDeal deal = MakeReferenceTableDeal(); + ASSERT_EQ(dds_c_calc_dd_table(ctx_, &deal, &table_), RETURN_NO_FAULT); + } + + void TearDown() override + { + dds_c_destroy_solvercontext(ctx_); + } + + DDS_C_SOLVER_CTX ctx_ = nullptr; + struct DdTableResults table_ = {}; +}; + +TEST_F(DdsCApiParUtilities, ParFromTableMatchesCalcPar) +{ + const struct DdTableDeal deal = MakeReferenceTableDeal(); + struct DdTableResults results = {}; + struct ParResults expected = {}; + ASSERT_EQ(dds_c_calc_par(ctx_, &deal, 0, &results, &expected), RETURN_NO_FAULT); + + struct ParResults par = {}; + ASSERT_EQ(dds_c_par_from_table(&table_, 0, &par), RETURN_NO_FAULT); + EXPECT_STREQ(par.par_score[0], expected.par_score[0]); + EXPECT_STREQ(par.par_score[1], expected.par_score[1]); +} + +TEST_F(DdsCApiParUtilities, SidesParProducesContracts) +{ + struct ParResultsDealer sides[2] = {}; + ASSERT_EQ(dds_c_sides_par(&table_, sides, 0), RETURN_NO_FAULT); + EXPECT_GT(sides[0].number, 0); + EXPECT_GT(sides[1].number, 0); +} + +TEST_F(DdsCApiParUtilities, SidesParBinProducesContracts) +{ + struct ParResultsMaster sides[2] = {}; + ASSERT_EQ(dds_c_sides_par_bin(&table_, sides, 0), RETURN_NO_FAULT); + EXPECT_GT(sides[0].number, 0); + EXPECT_GT(sides[1].number, 0); +} + +TEST_F(DdsCApiParUtilities, DealerParProducesContractsForEveryDealer) +{ + for (int dealer = 0; dealer <= 3; ++dealer) { + struct ParResultsDealer res = {}; + ASSERT_EQ(dds_c_dealer_par(&table_, &res, dealer, 0), RETURN_NO_FAULT) + << "dealer = " << dealer; + EXPECT_GT(res.number, 0) << "dealer = " << dealer; + } +} + +TEST_F(DdsCApiParUtilities, DealerParBinProducesContractsForEveryDealer) +{ + for (int dealer = 0; dealer <= 3; ++dealer) { + struct ParResultsMaster res = {}; + ASSERT_EQ(dds_c_dealer_par_bin(&table_, &res, dealer, 0), RETURN_NO_FAULT) + << "dealer = " << dealer; + EXPECT_GT(res.number, 0) << "dealer = " << dealer; + } +} + +TEST_F(DdsCApiParUtilities, ConvertToDealerTextFormatProducesText) +{ + struct ParResultsMaster res = {}; + ASSERT_EQ(dds_c_dealer_par_bin(&table_, &res, 0, 0), RETURN_NO_FAULT); + + char line[128] = {}; + ASSERT_EQ(dds_c_convert_to_dealer_text_format(&res, line), RETURN_NO_FAULT); + EXPECT_GT(std::strlen(line), 0U); +} + +TEST_F(DdsCApiParUtilities, ConvertToSidesTextFormatProducesText) +{ + struct ParResultsMaster res = {}; + ASSERT_EQ(dds_c_dealer_par_bin(&table_, &res, 0, 0), RETURN_NO_FAULT); + + struct ParTextResults text = {}; + ASSERT_EQ(dds_c_convert_to_sides_text_format(&res, &text), RETURN_NO_FAULT); + EXPECT_GT(std::strlen(text.par_text[0]), 0U); +} + +TEST(DdsCApiInfo, GetDDSInfoPopulatesVersion) +{ + struct DDSInfo info = {}; + dds_c_get_dds_info(&info); + EXPECT_STREQ(info.version_string, "3.1.0"); +} + +TEST(DdsCApiInfo, ErrorMessageMapsKnownCode) +{ + char line[80] = {}; + dds_c_error_message(RETURN_NO_FAULT, line); + EXPECT_STREQ(line, TEXT_NO_FAULT); +} + } // namespace From 8e6321a5edb83d1a6acb572baf83d22a4fe6c2e4 Mon Sep 17 00:00:00 2001 From: Martin Nygren Date: Sat, 29 Aug 2026 13:17:21 +0100 Subject: [PATCH 3/5] fix: CI failures from the new dds_c_api.h entries Two independent breakages surfaced by CI on the previous commit: 1. jni/tests:export_set_test failed on every build_and_test job: the two .lds export lists (jni/version_script.lds, jni/exported_symbols.lds) are checked-in, manually-regenerated files, and I forgot to regenerate them after adding the eleven new dds_c_* symbols to dds_c_api.h. Regenerated both via `python3 jni/gen_export_lists.py --headers ... --linux ... --macos ...` per the instructions in jni/BUILD.bazel. 2. DdsCApiParUtilities.ConvertToSidesTextFormatProducesText failed under ASan/MSan with a stack-buffer-overflow/uninitialized-read: the test fed ConvertToSidesTextFormat a single dds_c_dealer_par_bin() result, but the function indexes its input as a 2-element array (pres[0]/pres[1], one entry per side) and expects dds_c_sides_par_bin()'s output instead - a single ParResultsMaster is one element short, so reading the second one ran past the end of the stack variable. Fixed the test to use dds_c_sides_par_bin(), and tightened dds_c_convert_to_sides_text_format's signature/doc comments in dds_c_api.h/.cpp to say `par[2]` and spell out which of the two dealer/sides functions feeds which converter, so the same mistake isn't easy to repeat at a call site. Verified locally: bazel test //library/... //jni/... (62/62 pass) and bazel test --config=asan //library/tests:dds_c_api_test. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FNjHJrNuYyeqgMKY4v2urk --- jni/exported_symbols.lds | 11 +++++++++++ jni/version_script.lds | 11 +++++++++++ library/src/api/dds_c_api.cpp | 2 +- library/src/api/dds_c_api.h | 8 +++++--- library/tests/dds_c_api_test.cpp | 10 +++++++--- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/jni/exported_symbols.lds b/jni/exported_symbols.lds index 5802587b..085f763f 100644 --- a/jni/exported_symbols.lds +++ b/jni/exported_symbols.lds @@ -42,14 +42,25 @@ _SolveBoardPBN _dds_c_calc_dd_table _dds_c_calc_dd_table_pbn _dds_c_calc_par +_dds_c_calc_par_pbn _dds_c_clear_tt _dds_c_configure_tt +_dds_c_convert_to_dealer_text_format +_dds_c_convert_to_sides_text_format _dds_c_create_solvercontext _dds_c_create_solvercontext_default +_dds_c_dealer_par +_dds_c_dealer_par_bin _dds_c_destroy_solvercontext +_dds_c_error_message +_dds_c_get_dds_info _dds_c_log_append _dds_c_log_clear +_dds_c_par_from_table _dds_c_reset_best_moves_lite _dds_c_reset_for_solve _dds_c_resize_tt +_dds_c_sides_par +_dds_c_sides_par_bin _dds_c_solve_board +_dds_c_solve_board_pbn diff --git a/jni/version_script.lds b/jni/version_script.lds index 91bd80c3..7c747c87 100644 --- a/jni/version_script.lds +++ b/jni/version_script.lds @@ -44,17 +44,28 @@ dds_c_calc_dd_table; dds_c_calc_dd_table_pbn; dds_c_calc_par; + dds_c_calc_par_pbn; dds_c_clear_tt; dds_c_configure_tt; + dds_c_convert_to_dealer_text_format; + dds_c_convert_to_sides_text_format; dds_c_create_solvercontext; dds_c_create_solvercontext_default; + dds_c_dealer_par; + dds_c_dealer_par_bin; dds_c_destroy_solvercontext; + dds_c_error_message; + dds_c_get_dds_info; dds_c_log_append; dds_c_log_clear; + dds_c_par_from_table; dds_c_reset_best_moves_lite; dds_c_reset_for_solve; dds_c_resize_tt; + dds_c_sides_par; + dds_c_sides_par_bin; dds_c_solve_board; + dds_c_solve_board_pbn; local: *; }; diff --git a/library/src/api/dds_c_api.cpp b/library/src/api/dds_c_api.cpp index 5cb8c94d..0e75a148 100644 --- a/library/src/api/dds_c_api.cpp +++ b/library/src/api/dds_c_api.cpp @@ -333,7 +333,7 @@ DLLEXPORT int dds_c_convert_to_dealer_text_format(const struct ParResultsMaster* } } -DLLEXPORT int dds_c_convert_to_sides_text_format(const struct ParResultsMaster* par, +DLLEXPORT int dds_c_convert_to_sides_text_format(const struct ParResultsMaster par[2], struct ParTextResults* resp) { if (par == nullptr || resp == nullptr) diff --git a/library/src/api/dds_c_api.h b/library/src/api/dds_c_api.h index c82ded07..0074369b 100644 --- a/library/src/api/dds_c_api.h +++ b/library/src/api/dds_c_api.h @@ -124,12 +124,14 @@ DLLEXPORT int dds_c_sides_par_bin(const struct DdTableResults* table, struct ParResultsMaster sides_res[2], int vulnerable); -/* Format a ParResultsMaster as dealer-oriented text. */ +/* Format a dds_c_dealer_par_bin() result as dealer-oriented text. */ DLLEXPORT int dds_c_convert_to_dealer_text_format(const struct ParResultsMaster* par, char* resp); -/* Format a ParResultsMaster as sides-oriented text. */ -DLLEXPORT int dds_c_convert_to_sides_text_format(const struct ParResultsMaster* par, +/* Format a dds_c_sides_par_bin() result (both sides) as sides-oriented text. + par must point to a 2-element array, one entry per side, matching + dds_c_sides_par_bin's output -- not a single dds_c_dealer_par_bin() result. */ +DLLEXPORT int dds_c_convert_to_sides_text_format(const struct ParResultsMaster par[2], struct ParTextResults* resp); /* Query library version/build information. */ diff --git a/library/tests/dds_c_api_test.cpp b/library/tests/dds_c_api_test.cpp index 95b04899..9d1cc363 100644 --- a/library/tests/dds_c_api_test.cpp +++ b/library/tests/dds_c_api_test.cpp @@ -465,11 +465,15 @@ TEST_F(DdsCApiParUtilities, ConvertToDealerTextFormatProducesText) TEST_F(DdsCApiParUtilities, ConvertToSidesTextFormatProducesText) { - struct ParResultsMaster res = {}; - ASSERT_EQ(dds_c_dealer_par_bin(&table_, &res, 0, 0), RETURN_NO_FAULT); + // ConvertToSidesTextFormat indexes its input as a 2-element array (one + // entry per side), so it must be fed SidesParBin's output, not a single + // DealerParBin result -- a single ParResultsMaster is one element short + // and reading the second one overruns it. + struct ParResultsMaster sides[2] = {}; + ASSERT_EQ(dds_c_sides_par_bin(&table_, sides, 0), RETURN_NO_FAULT); struct ParTextResults text = {}; - ASSERT_EQ(dds_c_convert_to_sides_text_format(&res, &text), RETURN_NO_FAULT); + ASSERT_EQ(dds_c_convert_to_sides_text_format(sides, &text), RETURN_NO_FAULT); EXPECT_GT(std::strlen(text.par_text[0]), 0U); } From 645d3a865e3ff078342f6e19782c6bd898b20b96 Mon Sep 17 00:00:00 2001 From: Martin Nygren Date: Sun, 30 Aug 2026 11:07:47 +0100 Subject: [PATCH 4/5] address PR review feedback - library/tests/dds_c_api_test.cpp: derive the expected GetDDSInfo version string from DDS_VERSION (matching the major/minor/patch decomposition GetDDSInfo itself uses) instead of hard-coding "3.1.0", so the test keeps tracking version bumps automatically (Copilot review comment). Not actioned: the review thread questioning whether closing #294 is consistent with only deprecating the SolveBoard/CalcDDtable/CalcPar family (not CalcAllTables*) was already answered directly by the PR owner in the thread -- the CalcAllTables* deprecation was split out to follow-up issue #356, so no code change is needed here. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FNjHJrNuYyeqgMKY4v2urk --- library/tests/dds_c_api_test.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/library/tests/dds_c_api_test.cpp b/library/tests/dds_c_api_test.cpp index 9d1cc363..09bd3a19 100644 --- a/library/tests/dds_c_api_test.cpp +++ b/library/tests/dds_c_api_test.cpp @@ -17,6 +17,7 @@ #include #include +#include #include @@ -481,7 +482,20 @@ TEST(DdsCApiInfo, GetDDSInfoPopulatesVersion) { struct DDSInfo info = {}; dds_c_get_dds_info(&info); - EXPECT_STREQ(info.version_string, "3.1.0"); + + // Derive the expected version from DDS_VERSION (matching the + // major/minor/patch decomposition GetDDSInfo itself uses) rather than a + // hard-coded literal, so this test keeps tracking version bumps. + const int major = DDS_VERSION / 10000; + const int minor = (DDS_VERSION - major * 10000) / 100; + const int patch = DDS_VERSION % 100; + const std::string expected_version = std::to_string(major) + "." + + std::to_string(minor) + "." + std::to_string(patch); + + EXPECT_EQ(info.major, major); + EXPECT_EQ(info.minor, minor); + EXPECT_EQ(info.patch, patch); + EXPECT_STREQ(info.version_string, expected_version.c_str()); } TEST(DdsCApiInfo, ErrorMessageMapsKnownCode) From 33752e199f6242f09a3041c200eb6a83fd7285c6 Mon Sep 17 00:00:00 2001 From: Martin Nygren Date: Mon, 31 Aug 2026 13:18:40 +0100 Subject: [PATCH 5/5] fix: improves the test description for context free utilities rejecting null data pointers. --- library/tests/dds_c_api_test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/tests/dds_c_api_test.cpp b/library/tests/dds_c_api_test.cpp index 09bd3a19..e8ed6546 100644 --- a/library/tests/dds_c_api_test.cpp +++ b/library/tests/dds_c_api_test.cpp @@ -110,8 +110,7 @@ TEST(DdsCApiNullHandle, IntReturningEntryPointsFailFast) EXPECT_EQ(dds_c_calc_par_pbn(nullptr, &pbn_deal, 0, &results, &par), RETURN_UNKNOWN_FAULT); } -// The context-free utilities take no handle at all; they must instead reject -// null data pointers (the only thing they could otherwise dereference). +// Context-free utilities: must reject null data pointers. TEST(DdsCApiNullHandle, ContextFreeUtilitiesRejectNullPointers) { struct DdTableResults results = {};