diff --git a/vnpy/api/xtp/vnxtp.pyi b/vnpy/api/xtp/vnxtp.pyi index faca3608..40e0641d 100644 --- a/vnpy/api/xtp/vnxtp.pyi +++ b/vnpy/api/xtp/vnxtp.pyi @@ -7,19 +7,34 @@ if typing.TYPE_CHECKING: from .vnxtp import * +def set_async_callback_exception_handler(handler: Callable[[AsyncDispatchException], None]): + """ + set a customize exception handler for async callback in this module(pyd) + \a handler should return True if it handles that exception, + If the return value of \a handler is not True, exception will be re-thrown. + """ + ... + + +class AsyncDispatchException: + what: str + instance: object + function_name: str + + from . import vnxtp_XTP as XTP class XTPRspInfoStruct(): error_id: int - error_msg: Sequence[int] + error_msg: str class XTPSpecificTickerStruct(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str class XTPMarketDataStockExData(): @@ -71,7 +86,7 @@ class XTPMarketDataStruct(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str last_price: float pre_close_price: float open_price: float @@ -95,7 +110,7 @@ class XTPMarketDataStruct(): bid_qty: Sequence[int] ask_qty: Sequence[int] trades_count: int - ticker_status: Sequence[int] + ticker_status: str stk: XTPMarketDataStockExData opt: XTPMarketDataOptionExData data_type: XTP_MARKETDATA_TYPE @@ -106,8 +121,8 @@ class XTPQuoteStaticInfo(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] - ticker_name: Sequence[int] + ticker: str + ticker_name: str ticker_type: XTP_TICKER_TYPE pre_close_price: float upper_limit_price: float @@ -121,7 +136,7 @@ class OrderBookStruct(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str last_price: float qty: int turnover: float @@ -161,7 +176,7 @@ class XTPTickByTickStruct(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str seq: int data_time: int type: XTP_TBT_TYPE @@ -173,7 +188,7 @@ class XTPTickerPriceInfo(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str last_price: float @@ -182,7 +197,7 @@ class XTPOrderInsertInfo(): order_xtp_id: int order_client_id: int - ticker: Sequence[int] + ticker: str market: XTP_MARKET_TYPE price: float stop_price: float @@ -210,7 +225,7 @@ class XTPOrderInfo(): order_client_id: int order_cancel_client_id: int order_cancel_xtp_id: int - ticker: Sequence[int] + ticker: str market: XTP_MARKET_TYPE price: float quantity: int @@ -227,7 +242,7 @@ class XTPOrderInfo(): update_time: int cancel_time: int trade_amount: float - order_local_id: Sequence[int] + order_local_id: str order_status: XTP_ORDER_STATUS_TYPE order_submit_status: XTP_ORDER_SUBMIT_STATUS_TYPE order_type: int @@ -238,16 +253,16 @@ class XTPTradeReport(): order_xtp_id: int order_client_id: int - ticker: Sequence[int] + ticker: str market: XTP_MARKET_TYPE local_order_id: int - exec_id: Sequence[int] + exec_id: str price: float quantity: int trade_time: int trade_amount: float report_index: int - order_exch_id: Sequence[int] + order_exch_id: str trade_type: int u32: int side: int @@ -255,13 +270,13 @@ class XTPTradeReport(): reserved1: int reserved2: int business_type: XTP_BUSINESS_TYPE - branch_pbu: Sequence[int] + branch_pbu: str class XTPQueryOrderReq(): - ticker: Sequence[int] + ticker: str begin_time: int end_time: int @@ -270,13 +285,13 @@ class XTPQueryReportByExecIdReq(): order_xtp_id: int - exec_id: Sequence[int] + exec_id: str class XTPQueryTraderReq(): - ticker: Sequence[int] + ticker: str begin_time: int end_time: int @@ -311,8 +326,8 @@ class XTPQueryAssetRsp(): class XTPQueryStkPositionRsp(): - ticker: Sequence[int] - ticker_name: Sequence[int] + ticker: str + ticker_name: str market: XTP_MARKET_TYPE total_qty: int sellable_qty: int @@ -350,17 +365,17 @@ class XTPQueryStructuredFundInfoReq(): exchange_id: XTP_EXCHANGE_TYPE - sf_ticker: Sequence[int] + sf_ticker: str class XTPStructuredFundInfo(): exchange_id: XTP_EXCHANGE_TYPE - sf_ticker: Sequence[int] - sf_ticker_name: Sequence[int] - ticker: Sequence[int] - ticker_name: Sequence[int] + sf_ticker: str + sf_ticker_name: str + ticker: str + ticker_name: str split_merge_status: XTP_SPLIT_MERGE_STATUS ratio: int min_split_qty: int @@ -372,15 +387,15 @@ class XTPQueryETFBaseReq(): market: XTP_MARKET_TYPE - ticker: Sequence[int] + ticker: str class XTPQueryETFBaseRsp(): market: XTP_MARKET_TYPE - etf: Sequence[int] - subscribe_redemption_ticker: Sequence[int] + etf: str + subscribe_redemption_ticker: str unit: int subscribe_status: int redemption_status: int @@ -395,16 +410,16 @@ class XTPQueryETFComponentReq(): market: XTP_MARKET_TYPE - ticker: Sequence[int] + ticker: str class XTPQueryETFComponentRsp(): market: XTP_MARKET_TYPE - ticker: Sequence[int] - component_ticker: Sequence[int] - component_name: Sequence[int] + ticker: str + component_ticker: str + component_name: str quantity: int component_market: XTP_MARKET_TYPE replace_type: ETF_REPLACE_TYPE @@ -416,8 +431,8 @@ class XTPQueryIPOTickerRsp(): market: XTP_MARKET_TYPE - ticker: Sequence[int] - ticker_name: Sequence[int] + ticker: str + ticker_name: str price: float unit: int qty_upper_limit: int @@ -434,17 +449,17 @@ class XTPQueryOptionAuctionInfoReq(): market: XTP_MARKET_TYPE - ticker: Sequence[int] + ticker: str class XTPQueryOptionAuctionInfoRsp(): - ticker: Sequence[int] + ticker: str security_id_source: XTP_MARKET_TYPE - symbol: Sequence[int] - contract_id: Sequence[int] - underlying_security_id: Sequence[int] + symbol: str + contract_id: str + underlying_security_id: str underlying_security_id_source: XTP_MARKET_TYPE list_date: int last_trade_date: int @@ -483,8 +498,8 @@ class XTPFundTransferReq(): serial_id: int - fund_account: Sequence[int] - password: Sequence[int] + fund_account: str + password: str amount: float transfer_type: XTP_FUND_TRANSFER_TYPE @@ -610,7 +625,7 @@ class XTP_POSITION_DIRECTION_TYPE(Enum): class XTP_MARKETDATA_TYPE(Enum): XTP_MARKETDATA_ACTUAL: XTP_MARKETDATA_TYPE XTP_MARKETDATA_OPTION: XTP_MARKETDATA_TYPE -XTPVersionType = Sequence[int] +XTPVersionType = str XTP_LOG_LEVEL = XTP_LOG_LEVEL XTP_PROTOCOL_TYPE = XTP_PROTOCOL_TYPE XTP_EXCHANGE_TYPE = XTP_EXCHANGE_TYPE diff --git a/vnpy/api/xtp/vnxtp/generated_files/generated_functions_1.cpp b/vnpy/api/xtp/vnxtp/generated_files/generated_functions_1.cpp index 14130770..6933bd46 100644 --- a/vnpy/api/xtp/vnxtp/generated_files/generated_functions_1.cpp +++ b/vnpy/api/xtp/vnxtp/generated_files/generated_functions_1.cpp @@ -277,9 +277,14 @@ void generate_class_XTP_API_QuoteApi(pybind11::object & parent) PyQuoteApi > c(parent, "QuoteApi"); c.def_static("CreateQuoteApi", + autocxxpy::apply_function_transform< + autocxxpy::function_constant< &XTP::API::QuoteApi::CreateQuoteApi - //, - //pybind11::call_guard() + >, + brigand::list< + > + >::value, + pybind11::call_guard() ); c.def("Release", autocxxpy::apply_function_transform< diff --git a/vnpy/api/xtp/vnxtp/generated_files/module.cpp b/vnpy/api/xtp/vnxtp/generated_files/module.cpp index 617f88e8..7c62af9f 100644 --- a/vnpy/api/xtp/vnxtp/generated_files/module.cpp +++ b/vnpy/api/xtp/vnxtp/generated_files/module.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "module.hpp" @@ -20,6 +21,12 @@ void additional_init(pybind11::module &m) void init_dispatcher(pybind11::module &m) { + m.def("set_async_callback_exception_handler", &autocxxpy::async_callback_exception_handler::set_handler); + pybind11::class_ c(m, "AsyncDispatchException"); + c.def_property("what", &autocxxpy::async_dispatch_exception::what, nullptr); + c.def_readonly("instance", &autocxxpy::async_dispatch_exception::instance); + c.def_readonly("function_name", &autocxxpy::async_dispatch_exception::function_name); + autocxxpy::dispatcher::instance().start(); } diff --git a/vnpy/api/xtp/vnxtp/include/autocxxpy/callback_wrapper.hpp b/vnpy/api/xtp/vnxtp/include/autocxxpy/callback_wrapper.hpp index 6100625a..2b9e523c 100644 --- a/vnpy/api/xtp/vnxtp/include/autocxxpy/callback_wrapper.hpp +++ b/vnpy/api/xtp/vnxtp/include/autocxxpy/callback_wrapper.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "brigand.hpp" @@ -66,7 +67,7 @@ namespace autocxxpy template constexpr callback_type callback_type_of_v = callback_type_of::value; -#ifdef PYBIND11_OVERLOAD_NAME +#ifdef AUTOCXXPY_INCLUDED_PYBIND11 template struct pybind11_static_caster { static pybind11::detail::overload_caster_t caster; @@ -75,18 +76,59 @@ namespace autocxxpy template AUTOCXXPY_SELECT_ANY pybind11::detail::overload_caster_t pybind11_static_caster::caster; + struct async_dispatch_exception : public std::exception + { + async_dispatch_exception(const char *what, const pybind11::object &instance, std::string function_name) + : std::exception(what), instance(instance), function_name(function_name) + {} + pybind11::object instance; + std::string function_name; + inline const char* what() noexcept + { + return std::exception::what(); + } + }; + + struct async_callback_exception_handler + { + using handler_type = std::function; + static handler_type custom_handler; + + inline static void handle_excepiton(const async_dispatch_exception&e) + { + if (custom_handler) + { + custom_handler(e); + } + } + + inline static void set_handler(const handler_type& handler) + { + custom_handler = handler; + } + }; + + AUTOCXXPY_SELECT_ANY async_callback_exception_handler::handler_type async_callback_exception_handler::custom_handler; #endif namespace arg_helper { + ////////////////////////////////////////////////////////////////////////// + // stores + ////////////////////////////////////////////////////////////////////////// + // # todo: char8, char16, char32, wchar_t, etc... // # todo: shall i copy only const type, treating non-const type as output pointer? - inline auto save(const char *val) + inline std::optional save(const char* val) { // match const char * + if (nullptr == val) AUTOCXXPY_UNLIKELY + return std::nullopt; // maybe empty string is also a choice? return std::string(val); } - inline auto save(char *val) + inline std::optional save(char* val) { // match char * + if (nullptr == val) AUTOCXXPY_UNLIKELY + return std::nullopt; // maybe empty string is also a choice? return std::string(val); } template @@ -101,29 +143,41 @@ namespace autocxxpy } template - inline T &save(T *val) + inline std::optional save(T * val) { // match pointer + if (nullptr == val) AUTOCXXPY_UNLIKELY + { + return std::nullopt; + } return *val; } template - inline T &save(const T *val) + inline std::optional& save(const T * val) { // match const pointer + if (nullptr == val) AUTOCXXPY_UNLIKELY + { + return std::nullopt; + } return const_cast(*val); } template - inline T &save(const T &val) + inline T& save(const T & val) { // match everything else : just use original type return const_cast(val); } + ////////////////////////////////////////////////////////////////////////// + // loads + ////////////////////////////////////////////////////////////////////////// + template struct loader { // match default(everyting besides pointer) template - inline to_type operator ()(src_type &val) + inline to_type operator ()(src_type& val) { return val; } @@ -132,57 +186,57 @@ namespace autocxxpy template struct loader> { // match const char [] - using to_type = const char *; - inline to_type operator ()(const std::string &val) + using to_type = const char*; + inline to_type operator ()(const std::string& val) { - return const_cast(val.data()); + return const_cast(val.data()); } }; template struct loader> { // match char [] - using to_type = char *; - inline to_type operator ()(const std::string &val) + using to_type = char*; + inline to_type operator ()(const std::string& val) { - return const_cast(val.data()); + return const_cast(val.data()); } }; template <> - struct loader + struct loader { // match const char * - using to_type = const char *; - inline to_type operator ()(const std::string &val) + using to_type = const char*; + inline to_type operator ()(const std::optional& val) { - return const_cast(val.data()); + if (val) AUTOCXXPY_LIKELY + return const_cast(val->data()); + return nullptr; } }; template <> - struct loader + struct loader { // match char * - using to_type = char *; - inline to_type operator ()(const std::string &val) + using to_type = char*; + inline to_type operator ()(const std::optional& val) { - return const_cast(val.data()); + if (val) AUTOCXXPY_LIKELY + return const_cast(val->data()); + return nullptr; } }; template - struct loader + struct loader { // match pointer template - inline to_type *operator ()(src_type &val) + inline to_type* operator ()(const std::optional& val) { // val to poiner - return const_cast(&val); + if (val) AUTOCXXPY_LIKELY + return const_cast(&(*val)); + return nullptr; } - - //template - //inline to_type *operator ()(src_type *val) - //{ // pointer to pointer - // return val; - //} }; }; @@ -195,7 +249,7 @@ namespace autocxxpy using class_type = class_of_member_method_t; public: template - inline static ret_type call(class_type *instance, const char *py_func_name, arg_types ... args) + inline static ret_type call(class_type* instance, const char* py_func_name, arg_types ... args) { if constexpr (callback_type_of_v == callback_type::Direct) { @@ -211,47 +265,70 @@ namespace autocxxpy } template - inline static void async(class_type *instance, const char *py_func_name, arg_types ... args) + inline static void async(class_type* instance, const char* py_func_name, arg_types ... args) { return async_impl(instance, py_func_name, std::index_sequence_for{}, args ...); } template - inline static ret_type sync(class_type *instance, const char * py_func_name, arg_types ... args) + inline static ret_type sync(class_type * instance, const char* py_func_name, arg_types ... args) { // if this code is under test environment, we don't need pybind11 // since header of pybind11 use #pragma once, no macros is defined, we use a public macro to check if pybind11 is included or not #ifdef PYBIND11_OVERLOAD_NAME pybind11::gil_scoped_acquire gil; - pybind11::function overload = pybind11::get_overload(static_cast(instance), py_func_name); - if (overload) { - auto o = overload(args ...); - if (pybind11::detail::cast_is_temporary_value_reference::value) { - auto & caster = pybind11_static_caster::caster; - return pybind11::detail::cast_ref(std::move(o), caster); + pybind11::function overload = pybind11::get_overload(static_cast(instance), py_func_name); + if (overload) AUTOCXXPY_LIKELY{ + try + { + auto result = overload(args ...); + if (pybind11::detail::cast_is_temporary_value_reference::value) + { + auto& caster = pybind11_static_caster::caster; + return pybind11::detail::cast_ref(std::move(result), caster); + } + else + { + return pybind11::detail::cast_safe(std::move(result)); + } + } + catch (const pybind11::error_already_set & e) + { + // todo: option to not to throw when sync is called directly + throw async_dispatch_exception(e.what(), pybind11::cast(instance), py_func_name); } - else return pybind11::detail::cast_safe(std::move(o)); } #endif return (instance->*method)(args ...); } private: template - inline static void async_impl(class_type *instance, const char *py_func_name, std::index_sequence, arg_types ... args) + inline static void async_impl(class_type * instance, const char* py_func_name, std::index_sequence, arg_types ... args) { // wrap for ctp like function calls: // all the pointer might be unavailable after this call, so copy its value into a tuple auto arg_tuple = std::make_tuple(arg_helper::save(args) ...); auto task = [instance, py_func_name, arg_tuple = std::move(arg_tuple)]() { - // resolve all value: - // if it was originally a pointer, then use pointer type. - // if it was originally a value, just keep a reference to that value. - sync( - instance, py_func_name, - arg_helper::loader, brigand::integral_constant > >{} - (std::get(arg_tuple)) ... - ); +#ifdef AUTOCXXPY_INCLUDED_PYBIND11 + try + { +#endif + // resolve all value: + // if it was originally a pointer, then use pointer type. + // if it was originally a value, just keep a reference to that value. + sync( + instance, py_func_name, + arg_helper::loader, brigand::integral_constant > >{} + (std::get(arg_tuple)) ... + ); +#ifdef AUTOCXXPY_INCLUDED_PYBIND11 + } + catch (const async_dispatch_exception &e) + { + async_callback_exception_handler::handle_excepiton(e); + } +#endif }; dispatcher::instance().add(std::move(task)); } diff --git a/vnpy/api/xtp/vnxtp/include/autocxxpy/config/config.hpp b/vnpy/api/xtp/vnxtp/include/autocxxpy/config/config.hpp index 79301824..f0e28a91 100644 --- a/vnpy/api/xtp/vnxtp/include/autocxxpy/config/config.hpp +++ b/vnpy/api/xtp/vnxtp/include/autocxxpy/config/config.hpp @@ -8,12 +8,30 @@ #ifndef AUTOCXXPY_UNUSED -#define AUTOCXXPY_UNUSED(x) (void)(x) +# define AUTOCXXPY_UNUSED(x) (void)(x) #endif #ifdef _MSC_VER -#define AUTOCXXPY_SELECT_ANY __declspec(selectany) +# define AUTOCXXPY_SELECT_ANY __declspec(selectany) #else -#define AUTOCXXPY_SELECT_ANY __attribute__ ((selectany)) -#endif \ No newline at end of file +# define AUTOCXXPY_SELECT_ANY __attribute__ ((selectany)) +#endif + + +#ifdef __has_cpp_attribute +# if __has_cpp_attribute(likely) +# define AUTOCXXPY_LIKELY [[likely]] +# endif +# if __has_cpp_attribute(unlikely) +# define AUTOCXXPY_UNLIKELY [[unlikely]] +# endif +#endif + +#ifndef AUTOCXXPY_LIKELY +#define AUTOCXXPY_LIKELY +#endif + +#ifndef AUTOCXXPY_UNLIKELY +#define AUTOCXXPY_UNLIKELY +#endif diff --git a/vnpy/api/xtp/vnxtp/vnxtp.vcxproj b/vnpy/api/xtp/vnxtp/vnxtp.vcxproj index 9811582a..e711c1af 100644 --- a/vnpy/api/xtp/vnxtp/vnxtp.vcxproj +++ b/vnpy/api/xtp/vnxtp/vnxtp.vcxproj @@ -119,6 +119,7 @@ stdcpp17 4819 /bigobj %(AdditionalOptions) + true Console @@ -138,6 +139,7 @@ stdcpp17 4819 /bigobj %(AdditionalOptions) + true Console @@ -160,6 +162,7 @@ stdcpp17 4819 /bigobj %(AdditionalOptions) + true Console @@ -184,6 +187,7 @@ stdcpp17 4819 /bigobj %(AdditionalOptions) + true Console diff --git a/vnpy/api/xtp/vnxtp_XTP.pyi b/vnpy/api/xtp/vnxtp_XTP.pyi index 63aa7d94..f5ac8cb9 100644 --- a/vnpy/api/xtp/vnxtp_XTP.pyi +++ b/vnpy/api/xtp/vnxtp_XTP.pyi @@ -7,5 +7,20 @@ if typing.TYPE_CHECKING: from .vnxtp import * +def set_async_callback_exception_handler(handler: Callable[[Exception, object, str], bool]): + """ + set a customize exception handler for async callback in this module(pyd) + \a handler should return True if it handles that exception, + If the return value of \a handler is not True, exception will be re-thrown. + """ + ... + + +class AsyncDispatchException: + what: str + instance: object + function_name: str + + from . import vnxtp_XTP_API as API diff --git a/vnpy/api/xtp/vnxtp_XTP_API.pyi b/vnpy/api/xtp/vnxtp_XTP_API.pyi index 11706ffc..e3f98ddd 100644 --- a/vnpy/api/xtp/vnxtp_XTP_API.pyi +++ b/vnpy/api/xtp/vnxtp_XTP_API.pyi @@ -7,6 +7,21 @@ if typing.TYPE_CHECKING: from .vnxtp import * +def set_async_callback_exception_handler(handler: Callable[[Exception, object, str], bool]): + """ + set a customize exception handler for async callback in this module(pyd) + \a handler should return True if it handles that exception, + If the return value of \a handler is not True, exception will be re-thrown. + """ + ... + + +class AsyncDispatchException: + what: str + instance: object + function_name: str + + class TraderSpi():