Skip to content

Expose PBN-capable C exports #284

Description

@tameware

This could help us refactor the WASM shim and might make things easier for other clients. What do you thing, @zzcgumn? Here's what Cursor has to say:

The gap is explicit in specs/dds-public-api.md: the pure-C shim (dds_c_*) has binary-only entry points, while legacy dll.h has full *PBN twins and the modern C++ layer only has dds_calc_dd_table_pbn.

Current layers

Layer PBN today
Legacy dll.h Full pairing (SolveBoardPBN, CalcDDtablePBN, CalcParPBN, …)
Modern dds_api.hpp Only dds_calc_dd_table_pbn
C shim dds_c_api.h None — binary Deal / DdTableDeal only

PBN structs already live in dll.h (DealPBN, DdTableDealPBN, …) and conversion is convert_from_pbn in PBN.h. The DD-table path already converts then delegates:

auto calc_dd_table_pbn(
    SolverContext& ctx,
    const DdTableDealPBN& table_deal_pbn,
    DdTableResults* table_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_dd_table(ctx, table_deal, table_results);
}

What a shim twin would look like

Same pattern as today’s binary exports: pointer-only, null-check, catch-all, forward to layer 2.

Header (dds_c_api.h):

DLLEXPORT int dds_c_calc_dd_table_pbn(DDS_C_SOLVER_CTX ctx,
                                      const struct DdTableDealPBN* deal,
                                      struct DdTableResults* results);

Impl (dds_c_api.cpp) — mirrors dds_c_calc_dd_table:

DLLEXPORT int dds_c_calc_dd_table_pbn(DDS_C_SOLVER_CTX ctx,
                                      const struct DdTableDealPBN* deal,
                                      struct DdTableResults* results)
{
    if (ctx == nullptr || deal == nullptr || results == nullptr)
        return RETURN_UNKNOWN_FAULT;

    try {
        return dds_calc_dd_table_pbn(static_cast<SolverContext*>(ctx),
            *deal, results);
    } catch (...) {
        return RETURN_UNKNOWN_FAULT;
    }
}

That’s the smallest useful addition: one export, already backed by dds_calc_dd_table_pbn.

Fuller pairing (if you want solve/par too)

Those need modern API first (shim only forwards):

// dds_api.hpp
int dds_solve_board_pbn(DDS_SOLVER_CTX ctx, const DealPBN& dl, ...);
int dds_calc_par_pbn(DDS_SOLVER_CTX ctx, const DdTableDealPBN& deal, ...);

// dds_c_api.h
int dds_c_solve_board_pbn(DDS_C_SOLVER_CTX ctx, const struct DealPBN* dl, ...);
int dds_c_calc_par_pbn(DDS_C_SOLVER_CTX ctx, const struct DdTableDealPBN* deal, ...);

Implement like legacy SolveBoardPBN: convert_from_pbn → copy trump/first/trick → call binary dds_*. Bad PBN → RETURN_PBN_FAULT.

Plumbing after the symbols

  1. Regenerate export lists (jni/gen_export_lists.pyversion_script.lds / exported_symbols.lds); //jni/tests:export_set_test must stay green.
  2. Java FFM: add DEAL_PBN / DD_TABLE_DEAL_PBN layouts and downcalls in jni/java/org/dds/ffm/Dds.java.
  3. Update the shim docs/spec lines that say “no *PBN twins”.

Design choice

  • Minimal: only dds_c_calc_dd_table_pbn — matches what’s already on the modern layer; enough for the web/Java “hand me a PBN string” case.
  • Symmetric: add solve/par PBN on both dds_* and dds_c_* so the shim mirrors the binary trio.
  • Don’t grow dll.h — new work goes on the context API + shim.

I wouldn’t expose a raw convert_from_pbn on the shim unless bindings need it; keep conversion inside the *_pbn entry points.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions