diff --git a/CHANGELOG.md b/CHANGELOG.md index ba39e6bbb..6fde871f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **C++ SDK:** `asset::AssetContext` (`statements` / `statement_download_url`) is now actually built and usable. `longbridge.hpp` has always included `asset_context.hpp`, but `cpp/src/asset_context.cpp` was never listed in `cpp/CMakeLists.txt`, so the class was declared to users and then failed to link. It had also never compiled: it included neither `longbridge.h` nor the C declarations, and `statement_download_url` read `res->data` as a `lb_statement_download_url_response_t*` — a type that does not exist anywhere in the C layer, which delivers the URL as a bare `const char*` (the same convention as `QuoteContext::quote_level`). Fixed the include and the callback, and added the file to the build +- **C SDK:** export `lb_statement_item_t` from `longbridge.h`. `CStatementItem` is only reachable through the `void*` async-result pointer, so cbindgen did not emit it and no C or C++ caller could read what `lb_asset_context_statements` returns. Also added the missing `CAssetContext` → `lb_asset_context_t` entry to the cbindgen rename map: every other context type was mapped, so the header exposed the raw Rust name (`const struct CAssetContext *lb_asset_context_new(...)`) while the C++ side forward-declared `lb_asset_context_t` - **C/C++ SDKs:** every list argument that crosses the FFI boundary now tolerates a null pointer with a zero length. `std::vector::data()` is allowed to return `nullptr` for an empty vector, which is exactly what the C++ binding passes for an omitted list argument, but the C layer fed it straight to `std::slice::from_raw_parts` — undefined behaviour that **aborts the process** under the debug UB checks. Hit live by `QuoteContext::warrant_list` with no filters (`c/src/quote_context/context.rs`); all 17 call sites across `quote_context`, `trade_context`, `agent_context`, `alert_context`, and `types` now go through a null-tolerant `slice_from_raw_parts` helper ### Added diff --git a/c/cbindgen.toml b/c/cbindgen.toml index 5f0f304a5..0e20bf6f8 100644 --- a/c/cbindgen.toml +++ b/c/cbindgen.toml @@ -167,6 +167,8 @@ cpp_compat = true "CCalendarContext" = "lb_calendar_context_t" "CPortfolioContext" = "lb_portfolio_context_t" "CMarketContext" = "lb_market_context_t" +"CAssetContext" = "lb_asset_context_t" +"CStatementItem" = "lb_statement_item_t" # MarketContext types "CMarketTimeItem" = "lb_market_time_item_t" "CMarketStatusResponse" = "lb_market_status_response_t" @@ -428,6 +430,8 @@ include = [ "CWatchlistSecurity", "CMarginRatio", "COrderDetail", + # AssetContext: statements (reachable only via void* async data pointer) + "CStatementItem", # GridContext: grid trading (reachable only via void* async data pointer) "CSubmitGridOrderResponse", "CGridOrder", "CGridOrderSubOrder", "CGridOrderHistory", "CGridOrderDetail", diff --git a/c/csrc/include/longbridge.h b/c/csrc/include/longbridge.h index 3bb25d2f5..b67fb3ade 100644 --- a/c/csrc/include/longbridge.h +++ b/c/csrc/include/longbridge.h @@ -1853,7 +1853,7 @@ typedef struct lb_alert_context_t lb_alert_context_t; /** * Asset context */ -typedef struct CAssetContext CAssetContext; +typedef struct lb_asset_context_t lb_asset_context_t; typedef struct lb_calendar_context_t lb_calendar_context_t; @@ -6065,6 +6065,20 @@ typedef struct lb_order_detail_t { struct CMultiLegInfo multi_leg; } lb_order_detail_t; +/** + * Statement item + */ +typedef struct lb_statement_item_t { + /** + * Statement date (integer, e.g. 20250301) + */ + int32_t dt; + /** + * File key + */ + const char *file_key; +} lb_statement_item_t; + /** * Response for submit grid trading order request */ @@ -11862,17 +11876,17 @@ void lb_alert_context_delete(const struct lb_alert_context_t *ctx, * @param config Config object * @return A new asset context */ -const struct CAssetContext *lb_asset_context_new(const struct lb_config_t *config); +const struct lb_asset_context_t *lb_asset_context_new(const struct lb_config_t *config); /** * Retain the asset context (increment reference count) */ -void lb_asset_context_retain(const struct CAssetContext *ctx); +void lb_asset_context_retain(const struct lb_asset_context_t *ctx); /** * Release the asset context (decrement reference count) */ -void lb_asset_context_release(const struct CAssetContext *ctx); +void lb_asset_context_release(const struct lb_asset_context_t *ctx); /** * Get statement data list @@ -11884,7 +11898,7 @@ void lb_asset_context_release(const struct CAssetContext *ctx); * @param callback Async callback * @param userdata User data passed to the callback */ -void lb_asset_context_statements(const struct CAssetContext *ctx, +void lb_asset_context_statements(const struct lb_asset_context_t *ctx, int32_t statement_type, int32_t start_date, int32_t limit, @@ -11899,7 +11913,7 @@ void lb_asset_context_statements(const struct CAssetContext *ctx, * @param callback Async callback * @param userdata User data passed to the callback */ -void lb_asset_context_download_url(const struct CAssetContext *ctx, +void lb_asset_context_download_url(const struct lb_asset_context_t *ctx, const char *file_key, lb_async_callback_t callback, void *userdata); diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 89c86bf44..89cfa535d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES src/decimal.cpp src/agent_context.cpp src/alert_context.cpp + src/asset_context.cpp src/dca_context.cpp src/sharelist_context.cpp src/calendar_context.cpp diff --git a/cpp/src/asset_context.cpp b/cpp/src/asset_context.cpp index bb7a15440..16b29b6e9 100644 --- a/cpp/src/asset_context.cpp +++ b/cpp/src/asset_context.cpp @@ -1,4 +1,5 @@ #include "asset_context.hpp" +#include "longbridge.h" #include #include @@ -122,9 +123,8 @@ AssetContext::statement_download_url( Status status(res->error); if (status) { - auto resp = (const lb_statement_download_url_response_t*)res->data; StatementDownloadUrlResponse result; - result.url = resp->url; + result.url = (const char*)res->data; (*callback_ptr)( AsyncResult(