Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 4 additions & 191 deletions include/optionx_cpp/market_data/MarketDataRouter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,204 +3,17 @@
#define OPTIONX_HEADER_MARKET_DATA_MARKET_DATA_ROUTER_HPP_INCLUDED

/// \file MarketDataRouter.hpp
/// \brief Defines subscription-scoped market-data routing utilities.
/// \brief Defines the subscription-scoped market-data Router facade.

#include "MarketDataRouterIds.hpp"
#include "MarketDataRouterSubscription.hpp"

namespace optionx::market_data {

namespace detail {
class MarketDataRouterState;
} // namespace detail

/// \class RoutedSubscriptionId
/// \brief Strong Router-local identifier of a logical market-data subscription.
class RoutedSubscriptionId {
public:
/// \brief Constructs an invalid identifier.
constexpr RoutedSubscriptionId() noexcept = default;

/// \brief Returns true when the Router assigned an identifier.
[[nodiscard]] constexpr bool valid() const noexcept {
return m_value != 0;
}

/// \brief Allows identifiers to be checked directly in conditions.
constexpr explicit operator bool() const noexcept {
return valid();
}

/// \brief Returns the numeric value for logging and diagnostics.
[[nodiscard]] constexpr std::uint64_t value() const noexcept {
return m_value;
}

friend constexpr bool operator==(
RoutedSubscriptionId lhs,
RoutedSubscriptionId rhs) noexcept {
return lhs.m_value == rhs.m_value;
}

friend constexpr bool operator!=(
RoutedSubscriptionId lhs,
RoutedSubscriptionId rhs) noexcept {
return !(lhs == rhs);
}

private:
std::uint64_t m_value = 0;

explicit constexpr RoutedSubscriptionId(std::uint64_t value) noexcept
: m_value(value) {}

friend class detail::MarketDataRouterState;
};

/// \struct RoutedSubscriptionIdHash
/// \brief Hashes a routed subscription identifier for unordered containers.
struct RoutedSubscriptionIdHash {
[[nodiscard]] std::size_t operator()(RoutedSubscriptionId id) const noexcept {
return std::hash<std::uint64_t>{}(id.value());
}
};

/// \class MarketDataProviderId
/// \brief Stable application-assigned identifier of a registered provider.
class MarketDataProviderId {
public:
/// \brief Constructs an invalid provider identifier.
constexpr MarketDataProviderId() noexcept = default;

/// \brief Constructs a stable provider identifier from application data.
explicit constexpr MarketDataProviderId(std::uint64_t value) noexcept
: m_value(value) {}

/// \brief Returns true when the identifier can select a provider.
[[nodiscard]] constexpr bool valid() const noexcept {
return m_value != 0;
}

/// \brief Allows identifiers to be checked directly in conditions.
constexpr explicit operator bool() const noexcept {
return valid();
}

/// \brief Returns the application-assigned numeric value.
[[nodiscard]] constexpr std::uint64_t value() const noexcept {
return m_value;
}

friend constexpr bool operator==(
MarketDataProviderId lhs,
MarketDataProviderId rhs) noexcept {
return lhs.m_value == rhs.m_value;
}

friend constexpr bool operator!=(
MarketDataProviderId lhs,
MarketDataProviderId rhs) noexcept {
return !(lhs == rhs);
}

private:
std::uint64_t m_value = 0;
};

/// \struct MarketDataProviderIdHash
/// \brief Hashes an application-assigned provider identifier.
struct MarketDataProviderIdHash {
[[nodiscard]] std::size_t operator()(MarketDataProviderId id) const noexcept {
return std::hash<std::uint64_t>{}(id.value());
}
};

namespace detail {

struct MarketDataRouterSubscriptionControl {
mutable std::mutex mutex;
RoutedSubscriptionId router_id;
MarketDataProviderId registered_provider_id;
MarketDataSubscriptionHandle provider_subscription;
std::weak_ptr<MarketDataRouterState> router;
bool active = false;
bool released = false;
};
} // namespace detail

/// \class MarketDataRouterSubscription
/// \brief Move-only RAII owner of one subscription created by MarketDataRouter.
/// \details Destroying or resetting the handle requests unsubscription. The
/// handle is created before an asynchronous provider necessarily
/// accepts the subscription, so valid() can be true while pending()
/// is also true. provider_subscription() becomes valid after the
/// provider reports SUBSCRIBED.
class MarketDataRouterSubscription {
public:
/// \brief Default-constructs an empty handle.
MarketDataRouterSubscription() = default;

/// \brief Copy construction is disabled because ownership is unique.
MarketDataRouterSubscription(const MarketDataRouterSubscription&) = delete;
/// \brief Copy assignment is disabled because ownership is unique.
MarketDataRouterSubscription& operator=(const MarketDataRouterSubscription&) = delete;

/// \brief Transfers subscription ownership.
MarketDataRouterSubscription(MarketDataRouterSubscription&& other) noexcept
: m_control(std::move(other.m_control)) {}

/// \brief Releases the current subscription and transfers ownership.
MarketDataRouterSubscription& operator=(MarketDataRouterSubscription&& other) noexcept;

/// \brief Requests unsubscription for an owned subscription.
~MarketDataRouterSubscription() {
reset();
}

/// \brief Returns the stable router-local subscription ID.
[[nodiscard]] RoutedSubscriptionId router_id() const noexcept;

/// \brief Returns the provider-assigned subscription descriptor, if accepted.
[[nodiscard]] MarketDataSubscriptionHandle provider_subscription() const;

/// \brief Returns the stable registered provider ID used to create this route.
/// \details Direct provider-reference subscriptions return an invalid ID.
[[nodiscard]] MarketDataProviderId registered_provider_id() const;

/// \brief Returns true while this object owns a pending or active route.
[[nodiscard]] bool valid() const;

/// \brief Returns true after the provider accepted the subscription.
[[nodiscard]] bool active() const;

/// \brief Returns true while provider acceptance is still pending.
[[nodiscard]] bool pending() const {
return valid() && !active();
}

/// \brief Allows handles to be used in boolean contexts.
explicit operator bool() const {
return valid();
}

/// \brief Explicitly releases the route and requests provider unsubscription.
/// \param callback Optional callback receiving the provider unsubscribe result.
/// \return True when an unsubscribe or pending cancellation was accepted.
/// \details The logical route is released even when physical provider cleanup
/// fails. MarketDataRouter retains failed cleanup ownership for retry.
bool unsubscribe(BaseMarketDataProvider::subscription_callback_t callback = {});

/// \brief Releases the route without an unsubscribe completion callback.
void reset() noexcept;

private:
std::shared_ptr<detail::MarketDataRouterSubscriptionControl> m_control;

explicit MarketDataRouterSubscription(
std::shared_ptr<detail::MarketDataRouterSubscriptionControl> control)
: m_control(std::move(control)) {}

friend class MarketDataRouter;
friend class detail::MarketDataRouterState;
};

/// \class MarketDataRouter
/// \brief Binds provider subscriptions to concrete subscriber objects.
/// \details Unlike MarketDataHub, the router owns provider subscription
Expand Down
121 changes: 121 additions & 0 deletions include/optionx_cpp/market_data/MarketDataRouterIds.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#pragma once
#ifndef OPTIONX_HEADER_MARKET_DATA_MARKET_DATA_ROUTER_IDS_HPP_INCLUDED
#define OPTIONX_HEADER_MARKET_DATA_MARKET_DATA_ROUTER_IDS_HPP_INCLUDED

/// \file MarketDataRouterIds.hpp
/// \brief Defines the strong identifiers used by MarketDataRouter.

#include <cstddef>
#include <cstdint>
#include <functional>

namespace optionx::market_data {

namespace detail {
class MarketDataRouterState;
} // namespace detail

/// \class RoutedSubscriptionId
/// \brief Strong Router-local identifier of a logical market-data subscription.
class RoutedSubscriptionId {
public:
/// \brief Constructs an invalid identifier.
constexpr RoutedSubscriptionId() noexcept = default;

/// \brief Returns true when the Router assigned an identifier.
[[nodiscard]] constexpr bool valid() const noexcept {
return m_value != 0;
}

/// \brief Allows identifiers to be checked directly in conditions.
constexpr explicit operator bool() const noexcept {
return valid();
}

/// \brief Returns the numeric value for logging and diagnostics.
[[nodiscard]] constexpr std::uint64_t value() const noexcept {
return m_value;
}

friend constexpr bool operator==(
RoutedSubscriptionId lhs,
RoutedSubscriptionId rhs) noexcept {
return lhs.m_value == rhs.m_value;
}

friend constexpr bool operator!=(
RoutedSubscriptionId lhs,
RoutedSubscriptionId rhs) noexcept {
return !(lhs == rhs);
}

private:
std::uint64_t m_value = 0;

explicit constexpr RoutedSubscriptionId(std::uint64_t value) noexcept
: m_value(value) {}

friend class detail::MarketDataRouterState;
};

/// \struct RoutedSubscriptionIdHash
/// \brief Hashes a routed subscription identifier for unordered containers.
struct RoutedSubscriptionIdHash {
[[nodiscard]] std::size_t operator()(RoutedSubscriptionId id) const noexcept {
return std::hash<std::uint64_t>{}(id.value());
}
};

/// \class MarketDataProviderId
/// \brief Stable application-assigned identifier of a registered provider.
class MarketDataProviderId {
public:
/// \brief Constructs an invalid provider identifier.
constexpr MarketDataProviderId() noexcept = default;

/// \brief Constructs a stable provider identifier from application data.
explicit constexpr MarketDataProviderId(std::uint64_t value) noexcept
: m_value(value) {}

/// \brief Returns true when the identifier can select a provider.
[[nodiscard]] constexpr bool valid() const noexcept {
return m_value != 0;
}

/// \brief Allows identifiers to be checked directly in conditions.
constexpr explicit operator bool() const noexcept {
return valid();
}

/// \brief Returns the application-assigned numeric value.
[[nodiscard]] constexpr std::uint64_t value() const noexcept {
return m_value;
}

friend constexpr bool operator==(
MarketDataProviderId lhs,
MarketDataProviderId rhs) noexcept {
return lhs.m_value == rhs.m_value;
}

friend constexpr bool operator!=(
MarketDataProviderId lhs,
MarketDataProviderId rhs) noexcept {
return !(lhs == rhs);
}

private:
std::uint64_t m_value = 0;
};

/// \struct MarketDataProviderIdHash
/// \brief Hashes an application-assigned provider identifier.
struct MarketDataProviderIdHash {
[[nodiscard]] std::size_t operator()(MarketDataProviderId id) const noexcept {
return std::hash<std::uint64_t>{}(id.value());
}
};

} // namespace optionx::market_data

#endif // OPTIONX_HEADER_MARKET_DATA_MARKET_DATA_ROUTER_IDS_HPP_INCLUDED
Loading
Loading