Skip to content

Instantly share code, notes, and snippets.

@ecatmur
Last active July 4, 2022 22:38
Show Gist options
  • Save ecatmur/0b5cd64c3e3bd68c02eac1d264731900 to your computer and use it in GitHub Desktop.
Save ecatmur/0b5cd64c3e3bd68c02eac1d264731900 to your computer and use it in GitHub Desktop.
Advanced metaprogramming for low-latency trading, Maven-style
#include <boost/describe.hpp>
#include <boost/mp11.hpp>
#include <concepts>
#include <cstdint>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <ranges>
#include <variant>
BOOST_DEFINE_ENUM(Category,
Equity,
Debt,
Future,
Option,
Strategy,
)
template<auto C> struct Constant {
using type = std::remove_const_t<decltype(C)>;
static constexpr type value = C;
};
namespace concepts {
template<class T> concept BooleanTestable = requires(T t) { t ? void() : void(); };
template<class T, class U> concept ExplicitlyConvertibleTo = std::constructible_from<U, T>;
template<class T, template<class> class B> concept Satisfies = B<T>::value;
template<class T, class U> concept Optional = BooleanTestable<T> and requires(T opt) {
{ *opt } -> ExplicitlyConvertibleTo<U>; };
template<class P, class T, Category C> concept Properties =
(C != Category::Equity or requires(P const props) {
props; }) and
(C != Category::Debt or requires(P const props) {
props; }) and
(C != Category::Future or requires(P const props) {
props; }) and
(C != Category::Option or requires(P const props) {
{ props.getContractSize() } -> Satisfies<std::is_arithmetic>; }) and
(C != Category::Strategy or requires(P const props) {
{ props.getLegs() } -> std::ranges::forward_range; }) and
true;
template<class T, Category C> concept PropertiesCheck =
not requires(T const inst) { inst.template getProperties<C>(); } or
requires(T const inst) { {inst.template getProperties<C>()} -> Properties<T, C>; };
template<class T> concept Instrument = requires(T const inst) {
{ inst.getSymbol() } -> Optional<std::string>;
{ inst.getCategory() } -> std::same_as<Category>;
[]<Category C> -> decltype(inst.template getProperties<C>()) {}; } and
PropertiesCheck<T, Category::Equity> and
PropertiesCheck<T, Category::Debt> and
PropertiesCheck<T, Category::Future> and
PropertiesCheck<T, Category::Option> and
PropertiesCheck<T, Category::Strategy> and
true;
}
struct Empty {
[[noreturn]] explicit Empty(std::string_view reason) {
#if NDEBUG
__builtin_unreachable();
#else
throw std::logic_error(std::string(reason));
#endif
}
template<class T> operator T() const { __builtin_unreachable(); }
};
template<class T, template<class> class TQ, template<class> class UQ>
struct std::basic_common_reference<T, Empty, TQ, UQ> : std::basic_common_reference<T, T, TQ, UQ> {};
template<class U, template<class> class TQ, template<class> class UQ>
struct std::basic_common_reference<Empty, U, TQ, UQ> : std::basic_common_reference<U, U, TQ, UQ> {};
template<template<class> class TQ, template<class> class UQ>
struct std::basic_common_reference<Empty, Empty, TQ, UQ> {};
auto visitProperties(concepts::Instrument auto const& instr, auto f) {
using Categories = boost::describe::describe_enumerators<Category>;
auto ff = [&](auto C) {
static constexpr Category Cat = C.value;
if constexpr (requires { instr.template getProperties<Cat>(); })
return f(Constant<Cat>(), instr.template getProperties<C.value>());
else
return Empty("Unsupported Category");
};
using R = typename decltype([&]<template<class...> class L, class... D>(L<D...>) {
return std::common_reference<decltype(ff(Constant<D::value>()))...>();
}(Categories()))::type;
return boost::mp11::mp_with_index<boost::mp11::mp_size<Categories>>(
static_cast<unsigned>(instr.getCategory()),
[&]<std::size_t I>(std::integral_constant<std::size_t, I>) -> R {
return ff(Constant<static_cast<Category>(I)>());
});
}
namespace ace {
struct Option {
static constexpr auto Category = ::Category::Option;
std::uint32_t size;
auto getContractSize() const { return size; }
};
struct Strategy {
static constexpr auto Category = ::Category::Strategy;
struct Leg {};
std::span<Leg> getLegs() const;
};
struct Instrument {
std::string symbol;
std::variant<Option, Strategy> props;
auto getSymbol() const { return std::optional(std::string_view(symbol)); }
auto getCategory() const {
return std::visit([]<class P>(P const&) { return P::Category; }, props);
}
template<Category C> requires (C == Category::Option) auto const& getProperties() const {
return *std::get_if<Option>(&props); }
template<Category C> requires (C == Category::Strategy) auto const& getProperties() const {
return *std::get_if<Strategy>(&props); }
};
}
static_assert(concepts::Instrument<ace::Instrument>);
template<concepts::Instrument T>
auto getContractSize(T const& instr) {
using R = std::optional<decltype(visitProperties(instr, [](auto C, auto const& props) {
if constexpr (requires { props.getContractSize(); })
return props.getContractSize();
else
return Empty("");
}))>;
return visitProperties(instr, [&](auto C, auto const& props) -> R {
if constexpr (requires { props.getContractSize(); })
return props.getContractSize();
else
return std::nullopt;
});
}
auto test(ace::Instrument const& instr) {
return getContractSize(instr).value_or(0);
}
int main() {
ace::Instrument instr;
instr.props = ace::Option{.size = 100};
return getContractSize(instr).value_or(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment