From 130eff8d1124e71f02062701082c6476b53e3bb3 Mon Sep 17 00:00:00 2001 From: Anatoly Kalin Date: Mon, 14 Oct 2024 17:30:26 +0300 Subject: [PATCH 1/7] [MDAPI-79] [C++] Retrieve promise-based events from feed DXFeed::getLastEventPromise --- include/dxfeed_graal_cpp_api/api/DXFeed.hpp | 68 ++++++++++++++++++- .../isolated/api/IsolatedDXFeed.hpp | 10 +-- .../dxfeed_graal_cpp_api/promise/Promise.hpp | 4 +- samples/cpp/FetchDailyCandles/src/main.cpp | 2 +- src/api/DXFeed.cpp | 4 ++ src/isolated/api/IsolatedDXFeed.cpp | 18 +++++ 6 files changed, 98 insertions(+), 8 deletions(-) diff --git a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp index 60ebc51c..f2202872 100644 --- a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp @@ -125,6 +125,8 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { JavaObjectHandle handle_; static std::shared_ptr create(void *feedHandle); + void *getLastEventPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol) const; + void *getTimeSeriesPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, std::int64_t fromTime, std::int64_t toTime) const; @@ -395,15 +397,79 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { return sub; } + /** + * Requests the last event for the specified event type and symbol. + * This method works only for event types that implement LastingEvent marker "interface". + * This method requests the data from the uplink data provider, creates new event of the specified `eventType`, + * and completes the resulting promise with this event. + * + *

The promise is cancelled when the underlying DXEndpoint is @ref DXEndpoint::close() "closed". + * If the event is not available for any transient reason (no subscription, no connection to uplink, etc), + * then the resulting promise completes when the issue is resolved, which may involve an arbitrarily long wait. + * Use Promise::await() method to specify timeout while waiting for promise to complete. + * If the event is permanently not available (not supported), then the promise completes exceptionally with + * JavaException "IllegalArgumentException". + * + *

There is a bulk version of this method that works much faster for a single event type and multiple symbols. + * See getLastEventsPromises() . + * + *

Note, that this method does not work when DXEndpoint was created with @ref DXEndpoint::Role::STREAM_FEED + * "STREAM_FEED" role (promise completes exceptionally). + * + * @tparam E The type of event. + * @param symbol The symbol. + * @return The promise for the result of the request. + */ + template E> + Promise> getLastEventPromise(const SymbolWrapper &symbol) const { + return Promise>(getLastEventPromiseImpl(E::TYPE, symbol)); + } + /** * Requests time series of events for the specified event type, symbol, and a range of time. * + * This method works only for event types that implement TimeSeriesEvent "interface". + * This method requests the data from the uplink data provider, creates a list of events of the specified + * `eventType`, and completes the resulting promise with this container. The events are ordered by @ref + * TimeSeriesEvent::getTime() "time" in the container. + * + *

This method is designed for retrieval of a snapshot only. + * Use TimeSeriesEventModel if you need a list of time-series events that updates in real time. + * + *

The range and depth of events that are available with this service is typically constrained by + * upstream data provider. + * + *

The promise is cancelled when the underlying DXEndpoint is @ref DXEndpoint::close() "closed". + * + * If events are not available for any transient reason (no subscription, no connection to uplink, etc.), + * then the resulting promise completes when the issue is resolved, which may involve an arbitrarily long wait. + * Use EventsPromiseMixin::await() method to specify timeout while waiting for promise to complete. + * If events are permanently not available (not supported), then the promise + * completes exceptionally with JavaException "IllegalArgumentException". + * + *

Note, that this method does not work when DXEndpoint was created with + * @ref DXEndpoint::Role::STREAM_FEED "STREAM_FEED" role (promise completes exceptionally). + * + *

This method does not accept an instance of TimeSeriesSubscriptionSymbol as a `symbol`. + * The later class is designed for use with DXFeedSubscription and to observe time-series subscription + * in DXPublisher. + * + *

Event flags

+ * + * This method completes promise only when a consistent snapshot of time series has been received from + * the data feed. The @ref IndexedEvent::getEventFlags() "eventFlags" property of the events in the resulting + * container is always zero. + * + *

Note, that the resulting container should not be used with DXPublisher::publishEvents() method, + * because the latter expects events in a different order and with an appropriate flags set. See documentation on a + * specific event class for details on how they should be published. + * * @tparam E The type of event. * @param symbol The symbol. * @param fromTime The time, inclusive, to request events from (see TimeSeriesEvent::getTime()). * @param toTime The time, inclusive, to request events to (see TimeSeriesEvent::getTime()). * Use `std::numeric_limits::max()` or `LLONG_MAX` macro to retrieve events without an - * upper limit on time. + * upper limit on time. * @return The promise for the result of the request. */ template E> diff --git a/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp b/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp index fdc576f1..fd2939dd 100644 --- a/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp @@ -46,12 +46,14 @@ dxfg_event_type_list* dxfg_DXFeed_getTimeSeriesIfSubscribed(graal_is /* int32_t dxfg_DXFeed_getLastEvents(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_type_list *events); -dxfg_promise_event_t* dxfg_DXFeed_getLastEventPromise(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_t *symbol); -dxfg_promise_list* dxfg_DXFeed_getLastEventsPromises(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_list *symbols); -dxfg_promise_events_t* dxfg_DXFeed_getIndexedEventsPromise(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_t *symbol, dxfg_indexed_event_source_t* source); - */ +//dxfg_promise_event_t* dxfg_DXFeed_getLastEventPromise(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_t *symbol); +void* getLastEventPromise(const JavaObjectHandle& feed, const EventTypeEnum &eventType, const SymbolWrapper &symbol); + +//dxfg_promise_list* dxfg_DXFeed_getLastEventsPromises(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_list *symbols); +//dxfg_promise_events_t* dxfg_DXFeed_getIndexedEventsPromise(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_t *symbol, dxfg_indexed_event_source_t* source); + // dxfg_DXFeed_getTimeSeriesPromise /* dxfg_promise_events_t* */ void* getTimeSeriesPromise(/* dxfg_feed_t * */ const JavaObjectHandle& feed, /* dxfg_event_clazz_t */ const EventTypeEnum &eventType, /* dxfg_symbol_t * */ const SymbolWrapper &symbol, std::int64_t fromTime, std::int64_t toTime); diff --git a/include/dxfeed_graal_cpp_api/promise/Promise.hpp b/include/dxfeed_graal_cpp_api/promise/Promise.hpp index cb96f416..e703fb6c 100644 --- a/include/dxfeed_graal_cpp_api/promise/Promise.hpp +++ b/include/dxfeed_graal_cpp_api/promise/Promise.hpp @@ -251,7 +251,7 @@ template struct EventPromiseMixin { template struct EventsPromiseMixin { /** * Returns result of computation. If computation has no @ref CommonPromiseMixin::hasResult() "result", then - * this method returns an empty ollection. + * this method returns an empty collection. * * @return The result of computation. * @see CommonPromiseMixin::hasResult() @@ -325,7 +325,7 @@ struct Promise> : CommonPromiseMixin; friend struct EventPromiseMixin; - EventsPromiseImpl impl; + EventPromiseImpl impl; explicit Promise(void *handle) : impl(handle) { } diff --git a/samples/cpp/FetchDailyCandles/src/main.cpp b/samples/cpp/FetchDailyCandles/src/main.cpp index 8fb934ee..ad0b7acd 100644 --- a/samples/cpp/FetchDailyCandles/src/main.cpp +++ b/samples/cpp/FetchDailyCandles/src/main.cpp @@ -41,7 +41,7 @@ FetchDailyCandles auto candleSymbol = CandleSymbol::valueOf(baseSymbol, CandlePeriod::DAY); auto toTime = dxfcpp::now(); - auto fromTime = toTime - std::chrono::duration_cast(std::chrono::days(DAYS)).count(); + auto fromTime = (std::chrono::milliseconds(toTime) - std::chrono::days(DAYS)).count(); std::cout << "Fetching last " << DAYS << " days of candles for " << baseSymbol << "\n"; diff --git a/src/api/DXFeed.cpp b/src/api/DXFeed.cpp index a8a71d2b..9d449750 100644 --- a/src/api/DXFeed.cpp +++ b/src/api/DXFeed.cpp @@ -121,6 +121,10 @@ std::shared_ptr DXFeed::create(void *feedHandle) { return feed; } +void *DXFeed::getLastEventPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol) const { + return isolated::api::IsolatedDXFeed::getLastEventPromise(handle_, eventType, symbol); +} + void *DXFeed::getTimeSeriesPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, std::int64_t fromTime, std::int64_t toTime) const { return isolated::api::IsolatedDXFeed::getTimeSeriesPromise(handle_, eventType, symbol, fromTime, toTime); diff --git a/src/isolated/api/IsolatedDXFeed.cpp b/src/isolated/api/IsolatedDXFeed.cpp index 0fcec998..74c8a940 100644 --- a/src/isolated/api/IsolatedDXFeed.cpp +++ b/src/isolated/api/IsolatedDXFeed.cpp @@ -63,6 +63,24 @@ dxfg_symbol_list *symbols); dxfg_promise_events_t* dxfg_DXFeed_getInd */ +// dxfg_promise_event_t* dxfg_DXFeed_getLastEventPromise(graal_isolatethread_t *thread, dxfg_feed_t *feed, +// dxfg_event_clazz_t eventClazz, dxfg_symbol_t *symbol); +void *getLastEventPromise(const JavaObjectHandle &feed, const EventTypeEnum &eventType, + const SymbolWrapper &symbol) { + if (!feed) { + throw InvalidArgumentException( + "Unable to execute function `dxfg_DXFeed_getLastEventPromise`. The `feed` handle is invalid"); + } + + auto graalSymbol = symbol.toGraalUnique(); + + auto result = dxfcpp::bit_cast(runGraalFunctionAndThrowIfNullptr( + dxfg_DXFeed_getLastEventPromise, static_cast(feed.get()), + static_cast(eventType.getId()), static_cast(graalSymbol.get()))); + + return result; +} + /* dxfg_promise_events_t* */ void *getTimeSeriesPromise(/* dxfg_feed_t * */ const JavaObjectHandle &feed, /* dxfg_event_clazz_t */ const EventTypeEnum &eventType, /* dxfg_symbol_t * */ const SymbolWrapper &symbol, From 5884f456ffa31517b981b88038c2faac5ab0455d Mon Sep 17 00:00:00 2001 From: Anatoly Kalin Date: Mon, 14 Oct 2024 18:59:23 +0300 Subject: [PATCH 2/7] [MDAPI-79] [C++] Retrieve promise-based events from feed DXFeed::getIndexedEventsPromise --- include/dxfeed_graal_cpp_api/api/DXFeed.hpp | 54 +++++++++++++++++++ .../osub/IndexedEventSubscriptionSymbol.hpp | 9 ++-- .../event/IndexedEventSource.hpp | 4 ++ .../event/market/OrderSource.hpp | 4 ++ .../isolated/api/IsolatedDXFeed.hpp | 2 + src/api/DXFeed.cpp | 4 ++ .../osub/IndexedEventSubscriptionSymbol.cpp | 18 +++---- src/isolated/api/IsolatedDXFeed.cpp | 20 +++++++ 8 files changed, 101 insertions(+), 14 deletions(-) diff --git a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp index f2202872..850148ae 100644 --- a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp @@ -127,6 +127,8 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { void *getLastEventPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol) const; + void *getIndexedEventsPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, const IndexedEventSource& source) const; + void *getTimeSeriesPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, std::int64_t fromTime, std::int64_t toTime) const; @@ -425,6 +427,58 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { return Promise>(getLastEventPromiseImpl(E::TYPE, symbol)); } + /** + * Requests a container of indexed events for the specified event type, symbol, and source. + * This method works only for event types that implement IndexedEvent "interface". + * This method requests the data from the uplink data provider, creates a container of events of the specified `eventType`, + * and completes the resulting promise with this container. + * The events are ordered by @ref IndexedEvent::getIndex() "index" in the container. + * + *

This method is designed for retrieval of a snapshot only. + * Use IndexedEventModel if you need a container of indexed events that updates in real time. + * + *

The promise is cancelled when the underlying DXEndpoint is @ref DXEndpoint::close() "closed". + * If the events are not available for any transient reason (no subscription, no connection to uplink, etc.), + * then the resulting promise completes when the issue is resolved, which may involve an arbitrarily long wait. + * Use Promise::await() method to specify timeout while waiting for promise to complete. + * If the events are permanently not available (not supported), then the promise + * completes exceptionally with JavaException "IllegalArgumentException". + * + *

Note, that this method does not work when DXEndpoint was created with + * @ref DXEndpoint::Role::STREAM_FEED "STREAM_FEED" role (promise completes exceptionally). + * + *

Event source

+ * + * Use the @ref IndexedEventSource::DEFAULT "DEFAULT" value for `source` with events that do not + * have multiple sources (like Series). For events with multiple sources (like Order, + * AnalyticOrder, OtcMarketsOrder and SpreadOrder), use an event-specific source class (for example, OrderSource). + * This method does not support synthetic sources of orders (orders that are automatically + * generated from Quote events). + * + *

This method does not accept an instance of IndexedEventSubscriptionSymbol as a `symbol`. + * The later class is designed for use with DXFeedSubscription and to observe source-specific subscription + * in DXPublisher. + * + *

Event flags and consistent snapshot

+ * + * This method completes promise only when a consistent snapshot of indexed events has been received from + * the data feed. The @ref IndexedEvent::getEventFlags() "eventFlags" property of the events in the resulting list + * is always zero. + * + *

Note, that the resulting list should not be used with DXPublisher::publishEvents() method, because the latter expects + * events in a different order and with an appropriate flags set. See documentation on a specific event class + * for details on how they should be published. + * + * @tparam E The type of event. + * @param symbol The symbol. + * @param source The source. + * @return The promise for the result of the request. + */ + template E> + Promise>> getIndexedEventsPromise(const SymbolWrapper &symbol, const IndexedEventSource& source) const { + return Promise>>(getIndexedEventsPromiseImpl(E::TYPE, symbol, source)); + } + /** * Requests time series of events for the specified event type, symbol, and a range of time. * diff --git a/include/dxfeed_graal_cpp_api/api/osub/IndexedEventSubscriptionSymbol.hpp b/include/dxfeed_graal_cpp_api/api/osub/IndexedEventSubscriptionSymbol.hpp index 04a088c6..016d301a 100644 --- a/include/dxfeed_graal_cpp_api/api/osub/IndexedEventSubscriptionSymbol.hpp +++ b/include/dxfeed_graal_cpp_api/api/osub/IndexedEventSubscriptionSymbol.hpp @@ -40,7 +40,7 @@ class DXFCPP_EXPORT IndexedEventSubscriptionSymbol { friend SymbolWrapper; std::unique_ptr eventSymbol_; - IndexedEventSource source_; + std::unique_ptr source_; protected: /** @@ -83,8 +83,7 @@ class DXFCPP_EXPORT IndexedEventSubscriptionSymbol { IndexedEventSubscriptionSymbol(IndexedEventSubscriptionSymbol &&indexedEventSubscriptionSymbol) noexcept; - IndexedEventSubscriptionSymbol & - operator=(const IndexedEventSubscriptionSymbol &indexedEventSubscriptionSymbol); + IndexedEventSubscriptionSymbol &operator=(const IndexedEventSubscriptionSymbol &indexedEventSubscriptionSymbol); IndexedEventSubscriptionSymbol &operator=(IndexedEventSubscriptionSymbol &&indexedEventSubscriptionSymbol) noexcept; IndexedEventSubscriptionSymbol() noexcept = default; @@ -102,7 +101,7 @@ class DXFCPP_EXPORT IndexedEventSubscriptionSymbol { * * @return indexed event source. */ - virtual const IndexedEventSource &getSource() const; + virtual const std::unique_ptr &getSource() const; /** * Returns string representation of this indexed event subscription symbol. @@ -122,7 +121,7 @@ template <> struct DXFCPP_EXPORT std::hash>{}(indexedEventSubscriptionSymbol.getEventSymbol()) + - std::hash{}(indexedEventSubscriptionSymbol.getSource()) * 31; + std::hash{}(*indexedEventSubscriptionSymbol.getSource()) * 31; } }; diff --git a/include/dxfeed_graal_cpp_api/event/IndexedEventSource.hpp b/include/dxfeed_graal_cpp_api/event/IndexedEventSource.hpp index 1070fcde..9894c73a 100644 --- a/include/dxfeed_graal_cpp_api/event/IndexedEventSource.hpp +++ b/include/dxfeed_graal_cpp_api/event/IndexedEventSource.hpp @@ -45,6 +45,10 @@ class DXFCPP_EXPORT IndexedEventSource { */ static void freeGraal(void *graalNative); + virtual std::unique_ptr toGraalUnique() const noexcept { + return {toGraal(), IndexedEventSource::freeGraal}; + } + /** * Creates an object of the current type and fills it with data from the the dxFeed Graal SDK structure. * diff --git a/include/dxfeed_graal_cpp_api/event/market/OrderSource.hpp b/include/dxfeed_graal_cpp_api/event/market/OrderSource.hpp index 9efed430..7e91408e 100644 --- a/include/dxfeed_graal_cpp_api/event/market/OrderSource.hpp +++ b/include/dxfeed_graal_cpp_api/event/market/OrderSource.hpp @@ -142,6 +142,10 @@ class DXFCPP_EXPORT OrderSource final : public IndexedEventSource { */ void *toGraal() const override; + std::unique_ptr toGraalUnique() const noexcept override { + return {toGraal(), IndexedEventSource::freeGraal}; + } + public: /** * Bid side of a composite Quote. diff --git a/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp b/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp index fd2939dd..0508fce2 100644 --- a/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp @@ -52,7 +52,9 @@ int32_t dxfg_DXFeed_getLastEvents(graal_isolatethread_ void* getLastEventPromise(const JavaObjectHandle& feed, const EventTypeEnum &eventType, const SymbolWrapper &symbol); //dxfg_promise_list* dxfg_DXFeed_getLastEventsPromises(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_list *symbols); + //dxfg_promise_events_t* dxfg_DXFeed_getIndexedEventsPromise(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_t *symbol, dxfg_indexed_event_source_t* source); +void* getIndexedEventsPromise(const JavaObjectHandle& feed, const EventTypeEnum &eventType, const SymbolWrapper &symbol, const IndexedEventSource& source); // dxfg_DXFeed_getTimeSeriesPromise /* dxfg_promise_events_t* */ void* getTimeSeriesPromise(/* dxfg_feed_t * */ const JavaObjectHandle& feed, /* dxfg_event_clazz_t */ const EventTypeEnum &eventType, /* dxfg_symbol_t * */ const SymbolWrapper &symbol, std::int64_t fromTime, diff --git a/src/api/DXFeed.cpp b/src/api/DXFeed.cpp index 9d449750..6ad34d67 100644 --- a/src/api/DXFeed.cpp +++ b/src/api/DXFeed.cpp @@ -125,6 +125,10 @@ void *DXFeed::getLastEventPromiseImpl(const EventTypeEnum &eventType, const Symb return isolated::api::IsolatedDXFeed::getLastEventPromise(handle_, eventType, symbol); } +void *DXFeed::getIndexedEventsPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, const IndexedEventSource& source) const { + return isolated::api::IsolatedDXFeed::getIndexedEventsPromise(handle_, eventType, symbol, source); +} + void *DXFeed::getTimeSeriesPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, std::int64_t fromTime, std::int64_t toTime) const { return isolated::api::IsolatedDXFeed::getTimeSeriesPromise(handle_, eventType, symbol, fromTime, toTime); diff --git a/src/api/osub/IndexedEventSubscriptionSymbol.cpp b/src/api/osub/IndexedEventSubscriptionSymbol.cpp index ffb97364..50e28f09 100644 --- a/src/api/osub/IndexedEventSubscriptionSymbol.cpp +++ b/src/api/osub/IndexedEventSubscriptionSymbol.cpp @@ -9,14 +9,14 @@ DXFCPP_BEGIN_NAMESPACE IndexedEventSubscriptionSymbol::IndexedEventSubscriptionSymbol(const SymbolWrapper &eventSymbol, const IndexedEventSource &source) - : eventSymbol_(std::make_unique(eventSymbol)), source_(source) { + : eventSymbol_(std::make_unique(eventSymbol)), source_(std::make_unique(source)) { } const std::unique_ptr &IndexedEventSubscriptionSymbol::getEventSymbol() const { return eventSymbol_; } -const IndexedEventSource &IndexedEventSubscriptionSymbol::getSource() const { +const std::unique_ptr &IndexedEventSubscriptionSymbol::getSource() const { return source_; } @@ -24,7 +24,7 @@ void *IndexedEventSubscriptionSymbol::toGraal() const { auto *graalSymbol = new dxfg_indexed_event_subscription_symbol_t{ .supper = {.type = dxfg_symbol_type_t::INDEXED_EVENT_SUBSCRIPTION}, .symbol = static_cast(eventSymbol_->toGraal()), - .source = static_cast(source_.toGraal())}; + .source = static_cast(source_->toGraal())}; return static_cast(graalSymbol); } @@ -59,9 +59,9 @@ IndexedEventSubscriptionSymbol IndexedEventSubscriptionSymbol::fromGraal(void *g std::string IndexedEventSubscriptionSymbol::toString() const { if constexpr (Debugger::isDebug) { - return "IndexedEventSubscriptionSymbol{" + eventSymbol_->toString() + ", source = " + source_.toString() + "}"; + return "IndexedEventSubscriptionSymbol{" + eventSymbol_->toString() + ", source = " + source_->toString() + "}"; } else { - return eventSymbol_->toString() + "{source=" + source_.toString() + "}"; + return eventSymbol_->toString() + "{source=" + source_->toString() + "}"; } } @@ -80,13 +80,13 @@ bool IndexedEventSubscriptionSymbol::operator<( IndexedEventSubscriptionSymbol::IndexedEventSubscriptionSymbol( const IndexedEventSubscriptionSymbol &indexedEventSubscriptionSymbol) { eventSymbol_ = std::make_unique(*indexedEventSubscriptionSymbol.eventSymbol_); - source_ = indexedEventSubscriptionSymbol.source_; + source_ = std::make_unique(*indexedEventSubscriptionSymbol.source_); } IndexedEventSubscriptionSymbol::IndexedEventSubscriptionSymbol( IndexedEventSubscriptionSymbol &&indexedEventSubscriptionSymbol) noexcept { eventSymbol_ = std::move(indexedEventSubscriptionSymbol.eventSymbol_); - source_ = indexedEventSubscriptionSymbol.source_; + source_ = std::move(indexedEventSubscriptionSymbol.source_); } IndexedEventSubscriptionSymbol & @@ -96,7 +96,7 @@ IndexedEventSubscriptionSymbol::operator=(const IndexedEventSubscriptionSymbol & } eventSymbol_ = std::make_unique(*indexedEventSubscriptionSymbol.eventSymbol_); - source_ = indexedEventSubscriptionSymbol.source_; + source_ = std::make_unique(*indexedEventSubscriptionSymbol.source_); return *this; } @@ -108,7 +108,7 @@ IndexedEventSubscriptionSymbol::operator=(IndexedEventSubscriptionSymbol &&index } eventSymbol_ = std::move(indexedEventSubscriptionSymbol.eventSymbol_); - source_ = indexedEventSubscriptionSymbol.source_; + source_ = std::move(indexedEventSubscriptionSymbol.source_); return *this; } diff --git a/src/isolated/api/IsolatedDXFeed.cpp b/src/isolated/api/IsolatedDXFeed.cpp index 74c8a940..40fb91e9 100644 --- a/src/isolated/api/IsolatedDXFeed.cpp +++ b/src/isolated/api/IsolatedDXFeed.cpp @@ -81,6 +81,26 @@ void *getLastEventPromise(const JavaObjectHandle &feed, const EventTypeE return result; } +// dxfg_promise_events_t* dxfg_DXFeed_getIndexedEventsPromise(graal_isolatethread_t *thread, dxfg_feed_t +// *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_t *symbol, dxfg_indexed_event_source_t* source); +void *getIndexedEventsPromise(const JavaObjectHandle &feed, const EventTypeEnum &eventType, + const SymbolWrapper &symbol, const IndexedEventSource &source) { + if (!feed) { + throw InvalidArgumentException( + "Unable to execute function `dxfg_DXFeed_getIndexedEventsPromise`. The `feed` handle is invalid"); + } + + auto graalSymbol = symbol.toGraalUnique(); + auto graalSource = source.toGraalUnique(); + + auto result = dxfcpp::bit_cast(runGraalFunctionAndThrowIfNullptr( + dxfg_DXFeed_getIndexedEventsPromise, static_cast(feed.get()), + static_cast(eventType.getId()), static_cast(graalSymbol.get()), + static_cast(graalSource.get()))); + + return result; +} + /* dxfg_promise_events_t* */ void *getTimeSeriesPromise(/* dxfg_feed_t * */ const JavaObjectHandle &feed, /* dxfg_event_clazz_t */ const EventTypeEnum &eventType, /* dxfg_symbol_t * */ const SymbolWrapper &symbol, From 394e33555c3614ee1e46e7a8f455d7fe667d8ab3 Mon Sep 17 00:00:00 2001 From: ttldtor Date: Thu, 17 Oct 2024 23:41:46 +0300 Subject: [PATCH 3/7] [MDAPI-79] [C++] Retrieve promise-based events from feed Promises::allOf Promise PromiseList GraalListWrapper isolated::promise::IsolatedPromise::allOf DXFeed::getLastEventPromise -> std::shared_ptr>> DXFeed::getIndexedEventsPromise -> std::shared_ptr>>> DXFeed::getTimeSeriesPromise -> std::shared_ptr>>> --- CMakeLists.txt | 1 + include/dxfeed_graal_cpp_api/api.hpp | 1 + include/dxfeed_graal_cpp_api/api/DXFeed.hpp | 32 ++- .../isolated/promise/IsolatedPromise.hpp | 4 +- .../dxfeed_graal_cpp_api/promise/Promise.hpp | 264 +++++++++++++++++- .../dxfeed_graal_cpp_api/promise/Promises.hpp | 63 +++++ samples/cpp/FetchDailyCandles/src/main.cpp | 2 +- src/internal/JavaObjectHandle.cpp | 1 + src/isolated/promise/IsolatedPromise.cpp | 90 +++++- src/promise/Promise.cpp | 58 +++- src/promise/Promises.cpp | 23 ++ 11 files changed, 513 insertions(+), 26 deletions(-) create mode 100644 include/dxfeed_graal_cpp_api/promise/Promises.hpp create mode 100644 src/promise/Promises.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 54eb6ff9..0d6a02f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -265,6 +265,7 @@ set(dxFeedGraalCxxApi_OnDemand_Sources set(dxFeedGraalCxxApi_Promise_Sources src/promise/Promise.cpp + src/promise/Promises.cpp ) set(dxFeedGraalCxxApi_Symbols_Sources diff --git a/include/dxfeed_graal_cpp_api/api.hpp b/include/dxfeed_graal_cpp_api/api.hpp index 28436f61..2b38d6fa 100644 --- a/include/dxfeed_graal_cpp_api/api.hpp +++ b/include/dxfeed_graal_cpp_api/api.hpp @@ -81,5 +81,6 @@ DXFCXX_DISABLE_MSC_WARNINGS_PUSH(4251 4996) #include "ondemand/OnDemandService.hpp" #include "promise/Promise.hpp" +#include "promise/Promises.hpp" DXFCXX_DISABLE_MSC_WARNINGS_POP() \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp index 850148ae..79ac36af 100644 --- a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp @@ -127,7 +127,8 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { void *getLastEventPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol) const; - void *getIndexedEventsPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, const IndexedEventSource& source) const; + void *getIndexedEventsPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, + const IndexedEventSource &source) const; void *getTimeSeriesPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, std::int64_t fromTime, std::int64_t toTime) const; @@ -423,16 +424,16 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { * @return The promise for the result of the request. */ template E> - Promise> getLastEventPromise(const SymbolWrapper &symbol) const { - return Promise>(getLastEventPromiseImpl(E::TYPE, symbol)); + std::shared_ptr>> getLastEventPromise(const SymbolWrapper &symbol) const { + return std::make_shared>>(getLastEventPromiseImpl(E::TYPE, symbol)); } /** * Requests a container of indexed events for the specified event type, symbol, and source. * This method works only for event types that implement IndexedEvent "interface". - * This method requests the data from the uplink data provider, creates a container of events of the specified `eventType`, - * and completes the resulting promise with this container. - * The events are ordered by @ref IndexedEvent::getIndex() "index" in the container. + * This method requests the data from the uplink data provider, creates a container of events of the specified + * `eventType`, and completes the resulting promise with this container. The events are ordered by @ref + * IndexedEvent::getIndex() "index" in the container. * *

This method is designed for retrieval of a snapshot only. * Use IndexedEventModel if you need a container of indexed events that updates in real time. @@ -465,9 +466,9 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { * the data feed. The @ref IndexedEvent::getEventFlags() "eventFlags" property of the events in the resulting list * is always zero. * - *

Note, that the resulting list should not be used with DXPublisher::publishEvents() method, because the latter expects - * events in a different order and with an appropriate flags set. See documentation on a specific event class - * for details on how they should be published. + *

Note, that the resulting list should not be used with DXPublisher::publishEvents() method, because + * the latter expects events in a different order and with an appropriate flags set. See documentation on a specific + * event class for details on how they should be published. * * @tparam E The type of event. * @param symbol The symbol. @@ -475,8 +476,10 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { * @return The promise for the result of the request. */ template E> - Promise>> getIndexedEventsPromise(const SymbolWrapper &symbol, const IndexedEventSource& source) const { - return Promise>>(getIndexedEventsPromiseImpl(E::TYPE, symbol, source)); + std::shared_ptr>>> + getIndexedEventsPromise(const SymbolWrapper &symbol, const IndexedEventSource &source) const { + return std::make_shared>>>( + getIndexedEventsPromiseImpl(E::TYPE, symbol, source)); } /** @@ -527,9 +530,10 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { * @return The promise for the result of the request. */ template E> - Promise>> getTimeSeriesPromise(const SymbolWrapper &symbol, std::int64_t fromTime, - std::int64_t toTime) const { - return Promise>>(getTimeSeriesPromiseImpl(E::TYPE, symbol, fromTime, toTime)); + std::shared_ptr>>> + getTimeSeriesPromise(const SymbolWrapper &symbol, std::int64_t fromTime, std::int64_t toTime) const { + return std::make_shared>>>( + getTimeSeriesPromiseImpl(E::TYPE, symbol, fromTime, toTime)); } std::string toString() const override; diff --git a/include/dxfeed_graal_cpp_api/isolated/promise/IsolatedPromise.hpp b/include/dxfeed_graal_cpp_api/isolated/promise/IsolatedPromise.hpp index 23e8e635..4837af5a 100644 --- a/include/dxfeed_graal_cpp_api/isolated/promise/IsolatedPromise.hpp +++ b/include/dxfeed_graal_cpp_api/isolated/promise/IsolatedPromise.hpp @@ -147,10 +147,10 @@ int32_t dxfg_Promise_whenDoneAsync(graal_isolatethread_t *thread, dxfg_promise_t* dxfg_Promise_completed(graal_isolatethread_t *thread, dxfg_promise_t *promise, dxfg_java_object_handler *handler); dxfg_promise_t* dxfg_Promise_failed(graal_isolatethread_t *thread, dxfg_promise_t *promise, dxfg_exception_t* exception); -dxfg_promise_t* dxfg_Promises_allOf(graal_isolatethread_t *thread, dxfg_promise_list *promises); - */ +//dxfg_promise_t* dxfg_Promises_allOf(graal_isolatethread_t *thread, dxfg_promise_list *promises); +void* allOf(const std::vector& promises); } diff --git a/include/dxfeed_graal_cpp_api/promise/Promise.hpp b/include/dxfeed_graal_cpp_api/promise/Promise.hpp index e703fb6c..f89c33fb 100644 --- a/include/dxfeed_graal_cpp_api/promise/Promise.hpp +++ b/include/dxfeed_graal_cpp_api/promise/Promise.hpp @@ -56,18 +56,29 @@ struct PromiseImpl { void cancel() const; }; +struct VoidPromiseImpl : PromiseImpl { + void *handle = nullptr; + bool own = true; + + explicit VoidPromiseImpl(void *handle, bool own = true); + ~VoidPromiseImpl(); + void getResult() const; +}; + struct EventPromiseImpl : PromiseImpl { void *handle = nullptr; + bool own = true; - explicit EventPromiseImpl(void *handle); + explicit EventPromiseImpl(void *handle, bool own = true); ~EventPromiseImpl(); std::shared_ptr getResult() const; }; struct EventsPromiseImpl : PromiseImpl { void *handle = nullptr; + bool own = true; - explicit EventsPromiseImpl(void *handle); + explicit EventsPromiseImpl(void *handle, bool own = true); ~EventsPromiseImpl(); std::vector> getResult() const; }; @@ -182,6 +193,71 @@ template struct CommonPromiseMixin { } }; +/** + * Mixin for wrapping Promise method calls for a void. + * + * @tparam P The promise type. + */ +template struct VoidPromiseMixin { + /** + * Returns result of computation. + * + * @return The result of computation. + * @see CommonPromiseMixin::hasResult() + */ + void getResult() const { + return static_cast(this)->impl.getResult(); + } + + /** + * Wait for computation to complete and return its result or throw an exception in case of exceptional completion. + * @return result of computation. + * @throws CancellationException if computation was cancelled. + * @throws PromiseException if computation has completed exceptionally. + */ + void await() const { + static_cast(this)->impl.await(); + + return getResult(); + } + + /** + * Wait for computation to complete or timeout and return its result or throw an exception in case of exceptional + * completion or timeout. + * + * @param timeoutInMilliseconds The timeout. + * @return The result of computation. + * @throws CancellationException if computation was cancelled or timed out. + * @throws PromiseException if computation has completed exceptionally. + */ + void await(std::int32_t timeoutInMilliseconds) const & { + static_cast(this)->impl.await(timeoutInMilliseconds); + + return getResult(); + } + + /** + * Wait for computation to complete or timeout and return its result or throw an exception in case of exceptional + * completion or timeout. + * + * @param timeoutInMilliseconds The timeout. + * @return The result of computation. + * @throws CancellationException if computation was cancelled or timed out. + * @throws PromiseException if computation has completed exceptionally. + */ + void await(const std::chrono::milliseconds &timeoutInMilliseconds) const & { + auto timeout = timeoutInMilliseconds.count(); + + if (timeout > std::numeric_limits::max()) { + timeout = std::numeric_limits::max(); + } + + static_cast(this)->impl.await(timeout); + + return getResult(); + } +}; + /** * Mixin for wrapping Promise method calls for a single event. * @tparam E The event type. @@ -315,6 +391,24 @@ template struct EventsPromiseMixin { */ template struct Promise {}; +/** + * Result of an void receiving that will be completed normally or exceptionally in the future. + */ +template <> struct Promise : CommonPromiseMixin>, VoidPromiseMixin> { + friend struct CommonPromiseMixin; + friend struct VoidPromiseMixin; + + VoidPromiseImpl impl; + + explicit Promise(void *handle) : impl(handle) { + } + + Promise(const Promise &) = delete; + Promise &operator=(const Promise &) = delete; + Promise(Promise &&) noexcept = delete; + Promise &operator=(Promise &&) noexcept = delete; +}; + /** * Result of an event receiving that will be completed normally or exceptionally in the future. * @tparam E The event type. @@ -329,6 +423,167 @@ struct Promise> : CommonPromiseMixin>> wrapper with Graal semantics. + * @tparam E The event type. + */ +template struct PromiseList { + using data_type = std::vector>>; + + using iterator_category = std::random_access_iterator_tag; + + using value_type = typename data_type::value_type; + using allocator_type = typename data_type::allocator_type; + using pointer = typename data_type::pointer; + using const_pointer = typename data_type::const_pointer; + using reference = typename data_type::reference; + using const_reference = typename data_type::const_reference; + using size_type = typename data_type::size_type; + using difference_type = typename data_type::difference_type; + + using iterator = typename data_type::iterator; + using const_iterator = typename data_type::const_iterator; + using reverse_iterator = typename data_type::reverse_iterator; + using const_reverse_iterator = typename data_type::const_reverse_iterator; + + PromiseListImpl impl; + + data_type data_; + + explicit PromiseList(void *handle = nullptr) : impl(handle) { + } + + std::shared_ptr create(void *handle) { + if (!handle) { + return {}; + } + + std::vector>> result{}; + auto size = PromiseListImpl::getSize(handle); + auto promiseList = std::make_shared>(handle); + + for (std::size_t elementIndex = 0; elementIndex < size; elementIndex++) { + if (auto element = PromiseListImpl::getElement(handle, elementIndex)) { + promiseList->data_.emplace_back(element, false); + } + } + + return promiseList; + } + + pointer data() noexcept { + return data_.data(); + } + + const_pointer data() const noexcept { + return data_.data(); + } + + iterator begin() noexcept { + return data_.begin(); + } + + const_iterator begin() const noexcept { + return data_.begin(); + } + + iterator end() noexcept { + return data_.end(); + } + + const_iterator end() const noexcept { + return data_.end(); + } + + const_iterator cbegin() const noexcept { + return data_.cbegin(); + } + + const_iterator cend() const noexcept { + return data_.cend(); + } + + reverse_iterator rbegin() noexcept { + return data_.rbegin(); + } + + reverse_iterator rend() noexcept { + return data_.rend(); + } + + const_reverse_iterator crbegin() const noexcept { + return data_.crbegin(); + } + + const_reverse_iterator crend() const noexcept { + return data_.crend(); + } + + bool empty() const noexcept { + return data_.empty(); + } + + size_type size() const noexcept { + return data_.size(); + } + + size_type max_size() const noexcept { + return data_.max_size(); + } + + size_type capacity() const noexcept { + return data_.capacity(); + } + + reference operator[](const size_type pos) noexcept { + return data_[pos]; + } + + const_reference operator[](const size_type pos) const noexcept { + return data_[pos]; + } + + reference at(const size_type pos) { + return data_.at(pos); + } + + const_reference at(const size_type pos) const { + return data_.at(pos); + } + + reference front() noexcept { + return data_.front(); + } + + const_reference front() const noexcept { + return data_.front(); + } + + reference back() noexcept { + return data_.back(); + } + + const_reference back() const noexcept { + return data_.back(); + } }; /** @@ -345,6 +600,11 @@ struct Promise>> : CommonPromiseMixin +#include +#include + +DXFCPP_BEGIN_NAMESPACE + +/** + * Utility methods to manipulate @ref Promise "promises". + */ +struct DXFCPP_EXPORT Promises { + private: + static std::shared_ptr> allOfImpl(const std::vector &handles); + + public: + /** + * Returns a new promise that completes when all promises from the given collection complete normally or exceptionally. + * The results of the given promises are not reflected in the returned promise, but may be + * obtained by inspecting them individually. If no promises are provided, returns a promise completed + * with the value `void`. + * When the resulting promise completes for any reason (is canceled, for example) + * then all of the promises from the given array are canceled. + * + * @tparam Collection The collection type. For example: PromiseList (i.e. std::vector>>) + * or std::vector>>>. + * @tparam Element The collection's element type. + * @tparam PromiseType The promise type. + * @param collection The collection of promises. + * @return A new promise that completes when all promises from the given array complete. + */ + template , + typename PromiseType = std::decay_t> + static std::shared_ptr> allOf(Collection &&collection) { + std::vector handles{}; + + handles.reserve(std::size(collection)); + + for (const auto &e : collection) { + if constexpr (requires { e->impl.handle; }) { // std::shared_ptr> + handles.emplace_back(e->impl.handle); + } else if constexpr (requires { e.impl.handle; }) { // Promise (PromiseList etc) + handles.emplace_back(e.impl.handle); + } + } + + handles.shrink_to_fit(); + + return allOfImpl(handles); + } +}; + +DXFCPP_END_NAMESPACE + +DXFCXX_DISABLE_MSC_WARNINGS_POP() \ No newline at end of file diff --git a/samples/cpp/FetchDailyCandles/src/main.cpp b/samples/cpp/FetchDailyCandles/src/main.cpp index ad0b7acd..56e08ead 100644 --- a/samples/cpp/FetchDailyCandles/src/main.cpp +++ b/samples/cpp/FetchDailyCandles/src/main.cpp @@ -10,7 +10,7 @@ void fetchAndPrint(const dxfcpp::CandleSymbol &candleSymbol, std::int64_t toTime // Use default DXFeed instance for that data feed address is defined by dxfeed.properties file auto result = dxfcpp::DXFeed::getInstance() ->getTimeSeriesPromise(candleSymbol, fromTime, toTime) - .await(std::chrono::seconds(5)); + ->await(std::chrono::seconds(5)); for (const auto &candle : result) { std::cout << candle->toString() << "\n"; diff --git a/src/internal/JavaObjectHandle.cpp b/src/internal/JavaObjectHandle.cpp index 413c51c0..489932f0 100644 --- a/src/internal/JavaObjectHandle.cpp +++ b/src/internal/JavaObjectHandle.cpp @@ -85,6 +85,7 @@ template struct JavaObjectHandle; template struct JavaObjectHandle; template struct JavaObjectHandle; +template struct JavaObjectHandleList; template struct JavaObjectHandle; template struct JavaObjectHandle; diff --git a/src/isolated/promise/IsolatedPromise.cpp b/src/isolated/promise/IsolatedPromise.cpp index 92836e75..d7e7a7cd 100644 --- a/src/isolated/promise/IsolatedPromise.cpp +++ b/src/isolated/promise/IsolatedPromise.cpp @@ -29,7 +29,8 @@ bool /* int32_t */ hasResult(/* dxfg_promise_t * */ void *promise) { bool /* int32_t */ hasException(/* dxfg_promise_t * */ void *promise) { if (!promise) { - throw InvalidArgumentException("Unable to execute function `dxfg_Promise_hasException`. The `promise` is nullptr"); + throw InvalidArgumentException( + "Unable to execute function `dxfg_Promise_hasException`. The `promise` is nullptr"); } return runGraalFunctionAndThrowIfLessThanZero(dxfg_Promise_hasException, static_cast(promise)) == @@ -38,7 +39,8 @@ bool /* int32_t */ hasException(/* dxfg_promise_t * */ void *promise) { bool /* int32_t */ isCancelled(/* dxfg_promise_t * */ void *promise) { if (!promise) { - throw InvalidArgumentException("Unable to execute function `dxfg_Promise_isCancelled`. The `promise` is nullptr"); + throw InvalidArgumentException( + "Unable to execute function `dxfg_Promise_isCancelled`. The `promise` is nullptr"); } return runGraalFunctionAndThrowIfLessThanZero(dxfg_Promise_isCancelled, static_cast(promise)) == @@ -80,7 +82,8 @@ getResults(/* dxfg_promise_events_t * */ void *promise) { JavaException /* dxfg_exception_t* */ getException(/* dxfg_promise_t * */ void *promise) { if (!promise) { - throw InvalidArgumentException("Unable to execute function `dxfg_Promise_getException`. The `promise` is nullptr"); + throw InvalidArgumentException( + "Unable to execute function `dxfg_Promise_getException`. The `promise` is nullptr"); } auto *graalException = @@ -128,6 +131,87 @@ void /* int32_t */ cancel(/* dxfg_promise_t * */ void *promise) { runGraalFunctionAndThrowIfLessThanZero(dxfg_Promise_cancel, static_cast(promise)); } +template +struct GraalListWrapper { + void *handle = nullptr; + bool own = false; + + static std::ptrdiff_t calculateSize(std::ptrdiff_t initSize) noexcept { + if (initSize < 0) { + return 0; + } + + if (initSize > std::numeric_limits::max()) { + return std::numeric_limits::max(); + } + + return initSize; + } + + static void *create(std::ptrdiff_t size) { + auto *list = new ListType{static_cast(size), nullptr}; + + if (size == 0) { + return static_cast(list); + } + + list->elements = new ElementType *[size] { + nullptr + }; + + return list; + } + + bool setElement(std::ptrdiff_t elementIdx, void *element) noexcept { + if (handle == nullptr || elementIdx < 0 || elementIdx >= std::numeric_limits::max() || + element == nullptr) { + return false; + } + + static_cast(handle)->elements[elementIdx] = static_cast(element); + + return true; + } + + static GraalListWrapper create(const std::vector &handles) { + auto list = GraalListWrapper{create(calculateSize(handles.size()))}; + + for (std::size_t i = 0; i < handles.size(); i++) { + list.setElement(i, handles[i]); + } + + return GraalListWrapper{list.handle, true}; + } + + void free() { + if (handle == nullptr) { + return; + } + + auto list = static_cast(handle); + + if (list->size > 0 && list->elements != nullptr) { + delete[] list->elements; + } + + delete list; + } + + ~GraalListWrapper() { + if (own) { + free(); + } + } +}; + +// dxfg_promise_t* dxfg_Promises_allOf(graal_isolatethread_t *thread, dxfg_promise_list *promises); +void *allOf(const std::vector &promises) { + auto list = GraalListWrapper::create(promises); + + return dxfcpp::bit_cast( + runGraalFunctionAndThrowIfNullptr(dxfg_Promises_allOf, dxfcpp::bit_cast(list.handle))); +} + } // namespace isolated::promise::IsolatedPromise DXFCPP_END_NAMESPACE diff --git a/src/promise/Promise.cpp b/src/promise/Promise.cpp index f36d0ef7..59c6bcc6 100644 --- a/src/promise/Promise.cpp +++ b/src/promise/Promise.cpp @@ -55,26 +55,76 @@ void PromiseImpl::cancel() const { isolated::promise::IsolatedPromise::cancel(handle); } -EventPromiseImpl::EventPromiseImpl(void *handle) : PromiseImpl(handle), handle(handle) { +VoidPromiseImpl::VoidPromiseImpl(void *handle, bool own) : PromiseImpl(handle), handle(handle), own(own) { +} + +VoidPromiseImpl::~VoidPromiseImpl() { + if (own) { + JavaObjectHandle::deleter(handle); + } +} + +void VoidPromiseImpl::getResult() const { +} + +EventPromiseImpl::EventPromiseImpl(void *handle, bool own) : PromiseImpl(handle), handle(handle), own(own) { } EventPromiseImpl::~EventPromiseImpl() { - JavaObjectHandle::deleter(handle); + if (own) { + JavaObjectHandle::deleter(handle); + } } std::shared_ptr EventPromiseImpl::getResult() const { return isolated::promise::IsolatedPromise::getResult(handle); } -EventsPromiseImpl::EventsPromiseImpl(void *handle) : PromiseImpl(handle), handle(handle) { +EventsPromiseImpl::EventsPromiseImpl(void *handle, bool own) : PromiseImpl(handle), handle(handle), own(own) { } EventsPromiseImpl::~EventsPromiseImpl() { - JavaObjectHandle::deleter(handle); + if (own) { + JavaObjectHandle::deleter(handle); + } } std::vector> EventsPromiseImpl::getResult() const { return isolated::promise::IsolatedPromise::getResults(handle); } +PromiseListImpl::PromiseListImpl(void *handle) : handle(handle) { +} + +PromiseListImpl::~PromiseListImpl() { + JavaObjectHandleList::deleter(handle); +} + +std::size_t PromiseListImpl::getSize(void *handle) { + if (!handle) { + return {}; + } + + using ListType = dxfg_java_object_handler_list; + + auto list = static_cast(handle); + + if (list->elements == nullptr) { + return 0; + } + + return list->size; +} + +void* PromiseListImpl::getElement(void *handle, std::size_t index) { + if (!handle) { + return {}; + } + + using ListType = dxfg_java_object_handler_list; + auto list = static_cast(handle); + + return static_cast(list->elements[index]); +} + DXFCPP_END_NAMESPACE \ No newline at end of file diff --git a/src/promise/Promises.cpp b/src/promise/Promises.cpp new file mode 100644 index 00000000..a29374d0 --- /dev/null +++ b/src/promise/Promises.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2024 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +DXFCPP_BEGIN_NAMESPACE + +std::shared_ptr> Promises::allOfImpl(const std::vector& handles) { + return std::make_shared>(isolated::promise::IsolatedPromise::allOf(handles)); +} + +DXFCPP_END_NAMESPACE \ No newline at end of file From 1fa9bd071efa86e1d4f1a7517fac6ee8787c8f38 Mon Sep 17 00:00:00 2001 From: ttldtor Date: Fri, 18 Oct 2024 11:37:23 +0300 Subject: [PATCH 4/7] [MDAPI-79] [C++] Retrieve promise-based events from feed Promises::allOf: simplify --- .../dxfeed_graal_cpp_api/promise/Promises.hpp | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/include/dxfeed_graal_cpp_api/promise/Promises.hpp b/include/dxfeed_graal_cpp_api/promise/Promises.hpp index d88c95ee..560f8342 100644 --- a/include/dxfeed_graal_cpp_api/promise/Promises.hpp +++ b/include/dxfeed_graal_cpp_api/promise/Promises.hpp @@ -23,23 +23,19 @@ struct DXFCPP_EXPORT Promises { public: /** - * Returns a new promise that completes when all promises from the given collection complete normally or exceptionally. - * The results of the given promises are not reflected in the returned promise, but may be - * obtained by inspecting them individually. If no promises are provided, returns a promise completed - * with the value `void`. + * Returns a new promise that completes when all promises from the given collection complete normally or + * exceptionally. The results of the given promises are not reflected in the returned promise, but may be obtained + * by inspecting them individually. If no promises are provided, returns a promise completed with the value `void`. * When the resulting promise completes for any reason (is canceled, for example) * then all of the promises from the given array are canceled. * - * @tparam Collection The collection type. For example: PromiseList (i.e. std::vector>>) - * or std::vector>>>. - * @tparam Element The collection's element type. - * @tparam PromiseType The promise type. - * @param collection The collection of promises. + * @tparam Collection The collection type. For example: PromiseList (i.e. + * std::vector>>) or + * std::vector>>>. + * @param collection The collection of promises or collection of pointer-like of promises * @return A new promise that completes when all promises from the given array complete. */ - template , - typename PromiseType = std::decay_t> - static std::shared_ptr> allOf(Collection &&collection) { + template static std::shared_ptr> allOf(Collection &&collection) { std::vector handles{}; handles.reserve(std::size(collection)); @@ -47,7 +43,9 @@ struct DXFCPP_EXPORT Promises { for (const auto &e : collection) { if constexpr (requires { e->impl.handle; }) { // std::shared_ptr> handles.emplace_back(e->impl.handle); - } else if constexpr (requires { e.impl.handle; }) { // Promise (PromiseList etc) + } else if constexpr (requires { + e.impl.handle; + }) { // Promise (PromiseList etc) handles.emplace_back(e.impl.handle); } } From 22e9a88e2591e624424a30e9fa11b1d23bfbfc1a Mon Sep 17 00:00:00 2001 From: ttldtor Date: Fri, 18 Oct 2024 12:11:43 +0300 Subject: [PATCH 5/7] [MDAPI-79] [C++] Retrieve promise-based events from feed DXFeed::getLastEventsPromises --- include/dxfeed_graal_cpp_api/api/DXFeed.hpp | 19 +++++++++++++ .../api/DXFeedSubscription.hpp | 15 ++++------ .../dxfeed_graal_cpp_api/api/DXPublisher.hpp | 5 ++-- .../event/EventMapper.hpp | 4 +++ .../isolated/api/IsolatedDXFeed.hpp | 1 + .../symbols/SymbolWrapper.hpp | 27 ++++++++++++++---- src/api/DXFeed.cpp | 7 ++++- src/isolated/api/IsolatedDXFeed.cpp | 28 +++++++++++++------ 8 files changed, 79 insertions(+), 27 deletions(-) diff --git a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp index 79ac36af..583b62ec 100644 --- a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp @@ -127,6 +127,8 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { void *getLastEventPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol) const; + void *getLastEventsPromisesImpl(const EventTypeEnum &eventType, void *graalSymbolList) const; + void *getIndexedEventsPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, const IndexedEventSource &source) const; @@ -428,6 +430,23 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { return std::make_shared>>(getLastEventPromiseImpl(E::TYPE, symbol)); } + template E, typename SymbolIt> + std::shared_ptr> getLastEventsPromises(SymbolIt begin, SymbolIt end) const { + auto list = SymbolWrapper::SymbolListUtils::toGraalListUnique(begin, end); + + return std::make_shared>>(getLastEventsPromisesImpl(E::TYPE, list.get())); + } + + template E, ConvertibleToSymbolWrapperCollection SymbolsCollection> + std::shared_ptr> getLastEventsPromises(SymbolsCollection &&collection) const { + return getLastEventsPromises(std::begin(collection), std::end(collection)); + } + + template E> + std::shared_ptr> getLastEventsPromises(std::initializer_list collection) const { + return getLastEventsPromises(collection.begin(), collection.end()); + } + /** * Requests a container of indexed events for the specified event type, symbol, and source. * This method works only for event types that implement IndexedEvent "interface". diff --git a/include/dxfeed_graal_cpp_api/api/DXFeedSubscription.hpp b/include/dxfeed_graal_cpp_api/api/DXFeedSubscription.hpp index 7440b761..51a9672d 100644 --- a/include/dxfeed_graal_cpp_api/api/DXFeedSubscription.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXFeedSubscription.hpp @@ -326,10 +326,9 @@ class DXFCPP_EXPORT DXFeedSubscription : public RequireMakeShared static std::unique_ptr toGraalListUnique(EventIt begin, EventIt end) { + return {toGraalList(begin, end), freeGraalList}; + } + private: static std::ptrdiff_t calculateGraalListSize(std::ptrdiff_t initSize) noexcept; static void *newGraalList(std::ptrdiff_t size); diff --git a/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp b/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp index 0508fce2..da113a80 100644 --- a/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp @@ -52,6 +52,7 @@ int32_t dxfg_DXFeed_getLastEvents(graal_isolatethread_ void* getLastEventPromise(const JavaObjectHandle& feed, const EventTypeEnum &eventType, const SymbolWrapper &symbol); //dxfg_promise_list* dxfg_DXFeed_getLastEventsPromises(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_list *symbols); +void* getLastEventsPromises(const JavaObjectHandle& feed, const EventTypeEnum &eventType, void *symbols); //dxfg_promise_events_t* dxfg_DXFeed_getIndexedEventsPromise(graal_isolatethread_t *thread, dxfg_feed_t *feed, dxfg_event_clazz_t eventClazz, dxfg_symbol_t *symbol, dxfg_indexed_event_source_t* source); void* getIndexedEventsPromise(const JavaObjectHandle& feed, const EventTypeEnum &eventType, const SymbolWrapper &symbol, const IndexedEventSource& source); diff --git a/include/dxfeed_graal_cpp_api/symbols/SymbolWrapper.hpp b/include/dxfeed_graal_cpp_api/symbols/SymbolWrapper.hpp index f9040355..3a91861f 100644 --- a/include/dxfeed_graal_cpp_api/symbols/SymbolWrapper.hpp +++ b/include/dxfeed_graal_cpp_api/symbols/SymbolWrapper.hpp @@ -108,16 +108,31 @@ struct DXFCPP_EXPORT SymbolWrapper final { } template - static void *toGraalList(const SymbolsCollection &collection) noexcept { + static void *toGraalList(const SymbolsCollection &collection) { return SymbolListUtils::toGraalList(std::begin(collection), std::end(collection)); } - static void *toGraalList(std::initializer_list collection) noexcept { + static void *toGraalList(std::initializer_list collection) { return SymbolListUtils::toGraalList(collection.begin(), collection.end()); } static void freeGraalList(void *graalList); + template + static std::unique_ptr toGraalListUnique(SymbolIt begin, SymbolIt end) { + return {toGraalList(begin, end), freeGraalList}; + } + + template + static std::unique_ptr toGraalListUnique(const SymbolsCollection &collection) { + return {toGraalList(collection), freeGraalList}; + } + + static std::unique_ptr + toGraalListUnique(std::initializer_list collection) { + return {toGraalList(collection), freeGraalList}; + } + static std::vector fromGraalList(void *graalList); }; @@ -278,10 +293,10 @@ struct DXFCPP_EXPORT SymbolWrapper final { */ std::string toStringUnderlying() const { return std::visit( - [](const auto &symbol) { - return toStringAny(symbol); - }, - data_); + [](const auto &symbol) { + return toStringAny(symbol); + }, + data_); } /** diff --git a/src/api/DXFeed.cpp b/src/api/DXFeed.cpp index 6ad34d67..957132a4 100644 --- a/src/api/DXFeed.cpp +++ b/src/api/DXFeed.cpp @@ -125,7 +125,12 @@ void *DXFeed::getLastEventPromiseImpl(const EventTypeEnum &eventType, const Symb return isolated::api::IsolatedDXFeed::getLastEventPromise(handle_, eventType, symbol); } -void *DXFeed::getIndexedEventsPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, const IndexedEventSource& source) const { +void *DXFeed::getLastEventsPromisesImpl(const EventTypeEnum &eventType, void *graalSymbolList) const { + return isolated::api::IsolatedDXFeed::getLastEventsPromises(handle_, eventType, graalSymbolList); +} + +void *DXFeed::getIndexedEventsPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, + const IndexedEventSource &source) const { return isolated::api::IsolatedDXFeed::getIndexedEventsPromise(handle_, eventType, symbol, source); } diff --git a/src/isolated/api/IsolatedDXFeed.cpp b/src/isolated/api/IsolatedDXFeed.cpp index 40fb91e9..048876e5 100644 --- a/src/isolated/api/IsolatedDXFeed.cpp +++ b/src/isolated/api/IsolatedDXFeed.cpp @@ -74,11 +74,27 @@ void *getLastEventPromise(const JavaObjectHandle &feed, const EventTypeE auto graalSymbol = symbol.toGraalUnique(); - auto result = dxfcpp::bit_cast(runGraalFunctionAndThrowIfNullptr( + return dxfcpp::bit_cast(runGraalFunctionAndThrowIfNullptr( dxfg_DXFeed_getLastEventPromise, static_cast(feed.get()), static_cast(eventType.getId()), static_cast(graalSymbol.get()))); +} + +// dxfg_promise_list* dxfg_DXFeed_getLastEventsPromises(graal_isolatethread_t *thread, dxfg_feed_t *feed, +// dxfg_event_clazz_t eventClazz, dxfg_symbol_list *symbols); +void *getLastEventsPromises(const JavaObjectHandle &feed, const EventTypeEnum &eventType, void *symbols) { + if (!feed) { + throw InvalidArgumentException( + "Unable to execute function `dxfg_DXFeed_getLastEventsPromises`. The `feed` handle is invalid"); + } - return result; + if (!symbols) { + throw InvalidArgumentException( + "Unable to execute function `dxfg_DXFeed_getLastEventsPromises`. The `symbols` is nullptr"); + } + + return runGraalFunctionAndThrowIfNullptr(dxfg_DXFeed_getLastEventsPromises, static_cast(feed.get()), + static_cast(eventType.getId()), + static_cast(symbols)); } // dxfg_promise_events_t* dxfg_DXFeed_getIndexedEventsPromise(graal_isolatethread_t *thread, dxfg_feed_t @@ -93,12 +109,10 @@ void *getIndexedEventsPromise(const JavaObjectHandle &feed, const EventT auto graalSymbol = symbol.toGraalUnique(); auto graalSource = source.toGraalUnique(); - auto result = dxfcpp::bit_cast(runGraalFunctionAndThrowIfNullptr( + return dxfcpp::bit_cast(runGraalFunctionAndThrowIfNullptr( dxfg_DXFeed_getIndexedEventsPromise, static_cast(feed.get()), static_cast(eventType.getId()), static_cast(graalSymbol.get()), static_cast(graalSource.get()))); - - return result; } /* dxfg_promise_events_t* */ void *getTimeSeriesPromise(/* dxfg_feed_t * */ const JavaObjectHandle &feed, @@ -112,12 +126,10 @@ void *getIndexedEventsPromise(const JavaObjectHandle &feed, const EventT auto graalSymbol = symbol.toGraalUnique(); - auto result = dxfcpp::bit_cast( + return dxfcpp::bit_cast( runGraalFunctionAndThrowIfNullptr(dxfg_DXFeed_getTimeSeriesPromise, static_cast(feed.get()), static_cast(eventType.getId()), static_cast(graalSymbol.get()), fromTime, toTime)); - - return result; } } // namespace isolated::api::IsolatedDXFeed From c67e75c12589a708b5cac69070969e30636ca29c Mon Sep 17 00:00:00 2001 From: ttldtor Date: Fri, 18 Oct 2024 17:53:29 +0300 Subject: [PATCH 6/7] [MDAPI-79] [C++] Retrieve promise-based events from feed DXFeed::getLastEventsPromises --- .github/workflows/build.yml | 3 + .github/workflows/release.yml | 9 ++ include/dxfeed_graal_cpp_api/api/DXFeed.hpp | 120 +++++++++++++++++- .../dxfeed_graal_cpp_api/promise/Promise.hpp | 57 ++++----- src/event/EventMapper.cpp | 2 +- 5 files changed, 155 insertions(+), 36 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 67793e0c..297afc07 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,6 +87,7 @@ jobs: -DCMAKE_C_COMPILER=${{matrix.config.cc}} -DCMAKE_CXX_COMPILER=${{matrix.config.cxx}} -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON - name: Configure CMake if: ${{ contains(matrix.config.os, 'linux') }} @@ -96,6 +97,7 @@ jobs: -DCMAKE_C_COMPILER=${{matrix.config.cc}} -DCMAKE_CXX_COMPILER=${{matrix.config.cxx}} -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON - name: Configure CMake if: ${{ contains(matrix.config.os, 'win') }} @@ -103,6 +105,7 @@ jobs: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.buildType}} -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON - name: Build if: ${{ !contains(matrix.config.os, 'win') }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 401ea3d8..b0a6a98e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -116,6 +116,7 @@ jobs: -DDXFCXX_INSTALL_SAMPLES=OFF -DDXFCXX_INSTALL_TOOLS=OFF -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON - name: Configure CMake [Samples][Tools] if: ${{ contains(matrix.config.os, 'windows') && !contains(matrix.buildType, 'Deb') }} @@ -129,6 +130,7 @@ jobs: -DDXFCXX_INSTALL_LIB=OFF -DDXFCXX_INSTALL_TOOLS=OFF -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON cmake -B ${{github.workspace}}/build-Tools -DCMAKE_BUILD_TYPE=${{matrix.buildType}} @@ -139,6 +141,7 @@ jobs: -DDXFCXX_INSTALL_LIB=OFF -DDXFCXX_INSTALL_SAMPLES=OFF -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON - name: Configure CMake [Lib] if: ${{ contains(matrix.config.os, 'macos') }} @@ -155,6 +158,7 @@ jobs: -DDXFCXX_INSTALL_SAMPLES=OFF -DDXFCXX_INSTALL_TOOLS=OFF -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON - name: Configure CMake [Samples][Tools] if: ${{ contains(matrix.config.os, 'macos') && !contains(matrix.buildType, 'Deb') }} @@ -170,6 +174,7 @@ jobs: -DDXFCXX_INSTALL_LIB=OFF -DDXFCXX_INSTALL_TOOLS=OFF -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON cmake -B ${{github.workspace}}/build-Tools -DCMAKE_BUILD_TYPE=${{matrix.buildType}} @@ -182,6 +187,7 @@ jobs: -DDXFCXX_INSTALL_LIB=OFF -DDXFCXX_INSTALL_SAMPLES=OFF -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON - name: Configure CMake [Lib] if: ${{ contains(matrix.config.os, 'linux') }} @@ -198,6 +204,7 @@ jobs: -DDXFCXX_INSTALL_SAMPLES=OFF -DDXFCXX_INSTALL_TOOLS=OFF -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON - name: Configure CMake [Samples][Tools] if: ${{ contains(matrix.config.os, 'linux') && !contains(matrix.buildType, 'Deb') }} @@ -213,6 +220,7 @@ jobs: -DDXFCXX_INSTALL_LIB=OFF -DDXFCXX_INSTALL_TOOLS=OFF -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON cmake -B ${{github.workspace}}/build-Tools -DCMAKE_BUILD_TYPE=${{matrix.buildType}} @@ -225,6 +233,7 @@ jobs: -DDXFCXX_INSTALL_LIB=OFF -DDXFCXX_INSTALL_SAMPLES=OFF -DDXFCXX_BUILD_DOC=OFF + -DDXFCXX_FEATURE_STACKTRACE=ON - name: Build [Lib] if: ${{ !contains(matrix.config.os, 'win') }} diff --git a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp index 583b62ec..c46db5e7 100644 --- a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp @@ -405,7 +405,7 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { /** * Requests the last event for the specified event type and symbol. * This method works only for event types that implement LastingEvent marker "interface". - * This method requests the data from the uplink data provider, creates new event of the specified `eventType`, + * This method requests the data from the uplink data provider, creates new event of the specified event type, * and completes the resulting promise with this event. * *

The promise is cancelled when the underlying DXEndpoint is @ref DXEndpoint::close() "closed". @@ -430,18 +430,134 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { return std::make_shared>>(getLastEventPromiseImpl(E::TYPE, symbol)); } + /** + * Requests the last events for the specified event type and a collection of symbols. + * This method works only for event types that implement LastingEvent marker "interface". + * This method requests the data from the the uplink data provider, + * creates new events of the specified evet type, and completes the resulting promises with these events. + * + *

This is a bulk version of DXFeed::getLastEventPromise() method. + * + *

The promise is cancelled when the the underlying DXEndpoint is @ref DXEndpoint::close() "closed". + * If the event is not available for any transient reason (no subscription, no connection to uplink, etc), + * then the resulting promise completes when the issue is resolved, which may involve an arbitrarily long wait. + * Use Promise::await() method to specify timeout while waiting for promise to complete. + * If the event is permanently not available (not supported), then the promise + * completes exceptionally with JavaException "IllegalArgumentException". + * + *

Use the following pattern of code to acquire multiple events (either for multiple symbols and/or multiple + * events) and wait with a single timeout for all of them: + * + * ```cpp + * std::vector symbols{"AAPL&Q", "IBM&Q"}; + * auto promises = DXFeed::getInstance()->getLastEventsPromises(symbols.begin(), symbols.end()); + * + * // combine the list of promises into one with Promises utility method and wait + * Promises::allOf(*promises)->awaitWithoutException(std::chrono::seconds(timeout)); + * + * // now iterate the promises to retrieve results + * for (const auto& promise : *promises) { + * doSomethingWith(promise->getResult()); // InvalidArgumentException if result is nullptr + * } + * ``` + * + *

Note, that this method does not work when DXEndpoint was created with @ref DXEndpoint::Role::STREAM_FEED "STREAM_FEED" + * role (promise completes exceptionally). + * + * @tparam E The event type. + * @tparam SymbolIt The symbols collection's iterator type. + * @param begin The beginning of the collection of symbols (SymbolWrapper). + * @param end The end of the collection of symbols (SymbolWrapper). + * @return The list of promises for the result of the requests, one item in list per symbol. + */ template E, typename SymbolIt> std::shared_ptr> getLastEventsPromises(SymbolIt begin, SymbolIt end) const { auto list = SymbolWrapper::SymbolListUtils::toGraalListUnique(begin, end); - return std::make_shared>>(getLastEventsPromisesImpl(E::TYPE, list.get())); + return PromiseList::create(getLastEventsPromisesImpl(E::TYPE, list.get())); } + /** + * Requests the last events for the specified event type and a collection of symbols. + * This method works only for event types that implement LastingEvent marker "interface". + * This method requests the data from the the uplink data provider, + * creates new events of the specified evet type, and completes the resulting promises with these events. + * + *

This is a bulk version of DXFeed::getLastEventPromise() method. + * + *

The promise is cancelled when the the underlying DXEndpoint is @ref DXEndpoint::close() "closed". + * If the event is not available for any transient reason (no subscription, no connection to uplink, etc), + * then the resulting promise completes when the issue is resolved, which may involve an arbitrarily long wait. + * Use Promise::await() method to specify timeout while waiting for promise to complete. + * If the event is permanently not available (not supported), then the promise + * completes exceptionally with JavaException "IllegalArgumentException". + * + *

Use the following pattern of code to acquire multiple events (either for multiple symbols and/or multiple + * events) and wait with a single timeout for all of them: + * + * ```cpp + * std::vector symbols{"AAPL&Q", "IBM&Q"}; + * auto promises = DXFeed::getInstance()->getLastEventsPromises(symbols); + * + * // combine the list of promises into one with Promises utility method and wait + * Promises::allOf(*promises)->awaitWithoutException(std::chrono::seconds(timeout)); + * + * // now iterate the promises to retrieve results + * for (const auto& promise : *promises) { + * doSomethingWith(promise->getResult()); // InvalidArgumentException if result is nullptr + * } + * ``` + * + *

Note, that this method does not work when DXEndpoint was created with @ref DXEndpoint::Role::STREAM_FEED "STREAM_FEED" + * role (promise completes exceptionally). + * + * @tparam E The event type. + * @tparam SymbolsCollection The symbols collection's type. + * @param collection The symbols collection. + * @return The list of promises for the result of the requests, one item in list per symbol. + */ template E, ConvertibleToSymbolWrapperCollection SymbolsCollection> std::shared_ptr> getLastEventsPromises(SymbolsCollection &&collection) const { return getLastEventsPromises(std::begin(collection), std::end(collection)); } + /** + * Requests the last events for the specified event type and a collection of symbols. + * This method works only for event types that implement LastingEvent marker "interface". + * This method requests the data from the the uplink data provider, + * creates new events of the specified evet type, and completes the resulting promises with these events. + * + *

This is a bulk version of DXFeed::getLastEventPromise() method. + * + *

The promise is cancelled when the the underlying DXEndpoint is @ref DXEndpoint::close() "closed". + * If the event is not available for any transient reason (no subscription, no connection to uplink, etc), + * then the resulting promise completes when the issue is resolved, which may involve an arbitrarily long wait. + * Use Promise::await() method to specify timeout while waiting for promise to complete. + * If the event is permanently not available (not supported), then the promise + * completes exceptionally with JavaException "IllegalArgumentException". + * + *

Use the following pattern of code to acquire multiple events (either for multiple symbols and/or multiple + * events) and wait with a single timeout for all of them: + * + * ```cpp + * auto promises = DXFeed::getInstance()->getLastEventsPromises({"AAPL&Q", "IBM&Q"}); + * + * // combine the list of promises into one with Promises utility method and wait + * Promises::allOf(*promises)->awaitWithoutException(std::chrono::seconds(timeout)); + * + * // now iterate the promises to retrieve results + * for (const auto& promise : *promises) { + * doSomethingWith(promise->getResult()); // InvalidArgumentException if result is nullptr + * } + * ``` + * + *

Note, that this method does not work when DXEndpoint was created with @ref DXEndpoint::Role::STREAM_FEED "STREAM_FEED" + * role (promise completes exceptionally). + * + * @tparam E The event type. + * @param collection The symbols collection. + * @return The list of promises for the result of the requests, one item in list per symbol. + */ template E> std::shared_ptr> getLastEventsPromises(std::initializer_list collection) const { return getLastEventsPromises(collection.begin(), collection.end()); diff --git a/include/dxfeed_graal_cpp_api/promise/Promise.hpp b/include/dxfeed_graal_cpp_api/promise/Promise.hpp index f89c33fb..2d1396f4 100644 --- a/include/dxfeed_graal_cpp_api/promise/Promise.hpp +++ b/include/dxfeed_graal_cpp_api/promise/Promise.hpp @@ -16,6 +16,7 @@ DXFCPP_BEGIN_NAMESPACE struct EventType; struct JavaException; +struct Promises; template concept Derived = std::is_base_of_v; @@ -171,13 +172,8 @@ template struct CommonPromiseMixin { * @throws PromiseException if computation has completed exceptionally. */ bool awaitWithoutException(const std::chrono::milliseconds &timeoutInMilliseconds) const { - auto timeout = timeoutInMilliseconds.count(); - - if (timeout > std::numeric_limits::max()) { - timeout = std::numeric_limits::max(); - } - - return static_cast(this)->impl.awaitWithoutException(timeout); + return static_cast(this)->impl.awaitWithoutException( + dxfcpp::fitToType(timeoutInMilliseconds.count())); } /** @@ -246,13 +242,7 @@ template struct VoidPromiseMixin { * @throws PromiseException if computation has completed exceptionally. */ void await(const std::chrono::milliseconds &timeoutInMilliseconds) const & { - auto timeout = timeoutInMilliseconds.count(); - - if (timeout > std::numeric_limits::max()) { - timeout = std::numeric_limits::max(); - } - - static_cast(this)->impl.await(timeout); + static_cast(this)->impl.await(dxfcpp::fitToType(timeoutInMilliseconds.count())); return getResult(); } @@ -312,13 +302,7 @@ template struct EventPromiseMixin { * @throws PromiseException if computation has completed exceptionally. */ std::shared_ptr await(const std::chrono::milliseconds &timeoutInMilliseconds) const & { - auto timeout = timeoutInMilliseconds.count(); - - if (timeout > std::numeric_limits::max()) { - timeout = std::numeric_limits::max(); - } - - static_cast(this)->impl.await(timeout); + static_cast(this)->impl.await(dxfcpp::fitToType(timeoutInMilliseconds.count())); return getResult(); } @@ -373,13 +357,7 @@ template struct EventsPromiseMixin { * @throws PromiseException if computation has completed exceptionally. */ std::vector> await(const std::chrono::milliseconds &timeoutInMilliseconds) const & { - auto timeout = timeoutInMilliseconds.count(); - - if (timeout > std::numeric_limits::max()) { - timeout = std::numeric_limits::max(); - } - - static_cast(this)->impl.await(static_cast(timeout)); + static_cast(this)->impl.await(dxfcpp::fitToType(timeoutInMilliseconds.count())); return getResult(); } @@ -397,10 +375,13 @@ template struct Promise {}; template <> struct Promise : CommonPromiseMixin>, VoidPromiseMixin> { friend struct CommonPromiseMixin; friend struct VoidPromiseMixin; + friend struct Promises; + private: VoidPromiseImpl impl; - explicit Promise(void *handle) : impl(handle) { + public: + explicit Promise(void *handle, bool own = true) : impl(handle, own) { } Promise(const Promise &) = delete; @@ -418,15 +399,18 @@ struct Promise> : CommonPromiseMixin>> { friend struct CommonPromiseMixin; friend struct EventPromiseMixin; + friend struct Promises; + private: EventPromiseImpl impl; - explicit Promise(void *handle) : impl(handle) { + public: + explicit Promise(void *handle, bool own = true) : impl(handle, own) { } Promise(const Promise &) = delete; Promise &operator=(const Promise &) = delete; - Promise(Promise &&) noexcept = delete; + Promise(Promise &&) noexcept = default; Promise &operator=(Promise &&) noexcept = delete; }; @@ -446,6 +430,8 @@ struct PromiseListImpl { * @tparam E The event type. */ template struct PromiseList { + friend struct Promises; + using data_type = std::vector>>; using iterator_category = std::random_access_iterator_tag; @@ -464,14 +450,16 @@ template struct PromiseList { using reverse_iterator = typename data_type::reverse_iterator; using const_reverse_iterator = typename data_type::const_reverse_iterator; + private: PromiseListImpl impl; data_type data_; + public: explicit PromiseList(void *handle = nullptr) : impl(handle) { } - std::shared_ptr create(void *handle) { + static std::shared_ptr create(void *handle) { if (!handle) { return {}; } @@ -595,10 +583,13 @@ struct Promise>> : CommonPromiseMixin>>> { friend struct CommonPromiseMixin; friend struct EventsPromiseMixin; + friend struct Promises; + private: EventsPromiseImpl impl; - explicit Promise(void *handle) : impl(handle) { + public: + explicit Promise(void *handle, bool own = true) : impl(handle, own) { } Promise(const Promise &) = delete; diff --git a/src/event/EventMapper.cpp b/src/event/EventMapper.cpp index e9a73983..19105f6e 100644 --- a/src/event/EventMapper.cpp +++ b/src/event/EventMapper.cpp @@ -18,7 +18,7 @@ DXFCPP_BEGIN_NAMESPACE std::shared_ptr EventMapper::fromGraal(void *graalNativeEvent) { if (!graalNativeEvent) { - throw InvalidArgumentException("The `graalNativeEvent` is nullptr"); + throw InvalidArgumentException("EventMapper::fromGraal: The `graalNativeEvent` is nullptr"); } // TODO: implement other types [EN-8235] From f605b62cbc23b50763f5bf5e6d1b9ef40fe4f3a9 Mon Sep 17 00:00:00 2001 From: ttldtor Date: Fri, 18 Oct 2024 18:02:31 +0300 Subject: [PATCH 7/7] [MDAPI-79] [C++] Retrieve promise-based events from feed ReleaseNotes.md --- ReleaseNotes.md | 73 +++++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index b9af882d..1bee3df9 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,34 +1,53 @@ -## v3.0.0 +* **\[MDAPI-79]\[C++]** Retrieve promise-based events from feed + * Added `PromiseList` class where `E` - event type: a list of event receiving results that will be completed + normally or exceptionally in the future. + It is a `std::vector>>` wrapper with Graal semantics. + * Added `Promise` class. + * Added `Promises` class: utility methods to manipulate `Promise<>` promises. + * Added `Promises::allOf(Collection &&collection)` method that returns a new promise that completes when all + promises from the given collection complete normally or exceptionally. + * Fixed the `Promise>` semantics. + * Added `DXFeed::getLastEventPromise` method. + * Added `DXFeed::getLastEventsPromises` method. + * Added `DXFeed::getIndexedEventsPromise` method. + * **\[BREAKING]** The `DXFeed::getTimeSeriesPromise` now returns `std::shared_ptr>>>` + * **\[BREAKING]** The `IndexedEventSubscriptionSymbol::getSource` method now returns `std::unique_ptr` + +## v3.0.0 * **\[MDAPI-37]\[C++]** Retrieve latest events from feed. - * Added `SymbolWrapper::toStringUnderlying` method that returns a string representation of the underlying symbol object. - * Added `EventType::assign`. All `EventType` successors can populate their fields using `EventType` successors of the same type. - * Added `DXFeed::getLastEventIfSubscribed` method. - * Added `DXFeed::getLastEvent` and `DXFeed::getLastEvents` methods that use the `DXFeed::getLastEventIfSubscribed` method. + * Added `SymbolWrapper::toStringUnderlying` method that returns a string representation of the underlying symbol + object. + * Added `EventType::assign`. All `EventType` successors can populate their fields using `EventType` successors of + the same type. + * Added `DXFeed::getLastEventIfSubscribed` method. + * Added `DXFeed::getLastEvent` and `DXFeed::getLastEvents` methods that use the `DXFeed::getLastEventIfSubscribed` + method. * **\[MDAPI-113]\[C++]\[Tools]** Tools should report invalid event type. - * Added classes: `RuntimeException`, `InvalidArgumentException`. - * `InvalidArgumentException`, `GraalException`, `JavaException` are now descendants of the `RuntimeException` class, which can collect stacktrace. - * Now an `InvalidArgumentException` exception is thrown instead of the `std::invalid_argument` exception. - * `Tools` now reports incorrect event types specified by the user. + * Added classes: `RuntimeException`, `InvalidArgumentException`. + * `InvalidArgumentException`, `GraalException`, `JavaException` are now descendants of the `RuntimeException` class, + which can collect stacktrace. + * Now an `InvalidArgumentException` exception is thrown instead of the `std::invalid_argument` exception. + * `Tools` now reports incorrect event types specified by the user. * **\[MDAPI-80]\[C++]\[IPF]** Implement custom fields in InstrumentProfile. - * The API was migrated to Graal SDK v1.1.22. - * Added methods: - * `InstrumentProfile::getField` - * `InstrumentProfile::setField` - * `InstrumentProfile::getNumericField` - * `InstrumentProfile::setNumericField` - * `InstrumentProfile::getDateField` - * `InstrumentProfile::setDateField` - * `InstrumentProfile::getNonEmptyCustomFieldNames` - * **\[BREAKING]**: All `toString` methods can now throw exceptions. + * The API was migrated to Graal SDK v1.1.22. + * Added methods: + * `InstrumentProfile::getField` + * `InstrumentProfile::setField` + * `InstrumentProfile::getNumericField` + * `InstrumentProfile::setNumericField` + * `InstrumentProfile::getDateField` + * `InstrumentProfile::setDateField` + * `InstrumentProfile::getNonEmptyCustomFieldNames` + * **\[BREAKING]**: All `toString` methods can now throw exceptions. * **\[MDAPI-26]\[C++]** Migrate to Graal SDK v1.1.21. - * Added `Day::getSessions` method. - * New order sources added: `CEDX` and `cedx`. - * Added the ability to use dxFeed Graal SDK stub. In other words, now API can be built for any 32 and 64 platforms. - * Added `TimePeriod` class. - * Added `DXFeedSubscription::getAggregationPeriod` and `DXFeedSubscription::setAggregationPeriod` methods. - * Added `DXFeedSubscription::getEventsBatchLimit` and `DXFeedSubscription::setEventsBatchLimit` methods. - * Added `AuthToken` class. - * Added `InstrumentProfileReader::readFromFile(address, token)` method. + * Added `Day::getSessions` method. + * New order sources added: `CEDX` and `cedx`. + * Added the ability to use dxFeed Graal SDK stub. In other words, now API can be built for any 32 and 64 platforms. + * Added `TimePeriod` class. + * Added `DXFeedSubscription::getAggregationPeriod` and `DXFeedSubscription::setAggregationPeriod` methods. + * Added `DXFeedSubscription::getEventsBatchLimit` and `DXFeedSubscription::setEventsBatchLimit` methods. + * Added `AuthToken` class. + * Added `InstrumentProfileReader::readFromFile(address, token)` method. ## v2.0.0 \ No newline at end of file