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/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/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 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 60ebc51c..c46db5e7 100644 --- a/include/dxfeed_graal_cpp_api/api/DXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/api/DXFeed.hpp @@ -125,6 +125,13 @@ struct DXFCPP_EXPORT DXFeed : SharedEntity { JavaObjectHandle handle_; static std::shared_ptr create(void *feedHandle); + 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; + void *getTimeSeriesPromiseImpl(const EventTypeEnum &eventType, const SymbolWrapper &symbol, std::int64_t fromTime, std::int64_t toTime) const; @@ -395,21 +402,273 @@ 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 event type, + * 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> + std::shared_ptr>> getLastEventPromise(const SymbolWrapper &symbol) const { + 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 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()); + } + + /** + * 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> + std::shared_ptr>>> + getIndexedEventsPromise(const SymbolWrapper &symbol, const IndexedEventSource &source) const { + return std::make_shared>>>( + getIndexedEventsPromiseImpl(E::TYPE, symbol, source)); + } + /** * 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> - 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/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 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/EventMapper.hpp b/include/dxfeed_graal_cpp_api/event/EventMapper.hpp index 3879ca98..34878ef9 100644 --- a/include/dxfeed_graal_cpp_api/event/EventMapper.hpp +++ b/include/dxfeed_graal_cpp_api/event/EventMapper.hpp @@ -61,6 +61,10 @@ struct DXFCPP_EXPORT EventMapper { static void freeGraalList(void *graalList); + template 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/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 fdc576f1..da113a80 100644 --- a/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp +++ b/include/dxfeed_graal_cpp_api/isolated/api/IsolatedDXFeed.hpp @@ -46,12 +46,17 @@ 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); +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); + // 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/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 cb96f416..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; @@ -56,18 +57,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; }; @@ -160,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())); } /** @@ -182,6 +189,65 @@ 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 & { + static_cast(this)->impl.await(dxfcpp::fitToType(timeoutInMilliseconds.count())); + + return getResult(); + } +}; + /** * Mixin for wrapping Promise method calls for a single event. * @tparam E The event type. @@ -236,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(); } @@ -251,7 +311,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() @@ -297,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(); } @@ -315,6 +369,27 @@ 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; + friend struct Promises; + + private: + VoidPromiseImpl impl; + + 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 &operator=(Promise &&) noexcept = delete; +}; + /** * Result of an event receiving that will be completed normally or exceptionally in the future. * @tparam E The event type. @@ -324,10 +399,178 @@ struct Promise> : CommonPromiseMixin>> { friend struct CommonPromiseMixin; friend struct EventPromiseMixin; + friend struct Promises; - EventsPromiseImpl impl; + private: + EventPromiseImpl impl; + + public: + explicit Promise(void *handle, bool own = true) : impl(handle, own) { + } + + Promise(const Promise &) = delete; + Promise &operator=(const Promise &) = delete; + Promise(Promise &&) noexcept = default; + Promise &operator=(Promise &&) noexcept = delete; +}; + +struct PromiseListImpl { + void *handle = nullptr; + + explicit PromiseListImpl(void *handle); + ~PromiseListImpl(); + + static std::size_t getSize(void *handle); + static void *getElement(void *handle, std::size_t index); +}; + +/** + * 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. + * @tparam E The event type. + */ +template struct PromiseList { + friend struct Promises; + + 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; + + private: + PromiseListImpl impl; + + data_type data_; + + public: + explicit PromiseList(void *handle = nullptr) : impl(handle) { + } + + static 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(); + } - explicit Promise(void *handle) : impl(handle) { + 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(); } }; @@ -340,11 +583,19 @@ 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; + Promise &operator=(const Promise &) = delete; + Promise(Promise &&) noexcept = delete; + Promise &operator=(Promise &&) noexcept = delete; }; DXFCPP_END_NAMESPACE diff --git a/include/dxfeed_graal_cpp_api/promise/Promises.hpp b/include/dxfeed_graal_cpp_api/promise/Promises.hpp new file mode 100644 index 00000000..560f8342 --- /dev/null +++ b/include/dxfeed_graal_cpp_api/promise/Promises.hpp @@ -0,0 +1,61 @@ +// Copyright (c) 2024 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "../internal/Conf.hpp" +#include "Promise.hpp" + +DXFCXX_DISABLE_MSC_WARNINGS_PUSH(4251) + +#include +#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>>>. + * @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 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/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/samples/cpp/FetchDailyCandles/src/main.cpp b/samples/cpp/FetchDailyCandles/src/main.cpp index 8fb934ee..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"; @@ -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..957132a4 100644 --- a/src/api/DXFeed.cpp +++ b/src/api/DXFeed.cpp @@ -121,6 +121,19 @@ 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::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); +} + 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/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] 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/api/IsolatedDXFeed.cpp b/src/isolated/api/IsolatedDXFeed.cpp index 0fcec998..048876e5 100644 --- a/src/isolated/api/IsolatedDXFeed.cpp +++ b/src/isolated/api/IsolatedDXFeed.cpp @@ -63,6 +63,58 @@ 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(); + + 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"); + } + + 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 +// *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(); + + return dxfcpp::bit_cast(runGraalFunctionAndThrowIfNullptr( + dxfg_DXFeed_getIndexedEventsPromise, static_cast(feed.get()), + static_cast(eventType.getId()), static_cast(graalSymbol.get()), + static_cast(graalSource.get()))); +} + /* 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, @@ -74,12 +126,10 @@ dxfg_symbol_list *symbols); dxfg_promise_events_t* dxfg_DXFeed_getInd 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 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