Program Listing for File variant.hpp

Return to documentation for file (include/depthai/common/variant.hpp)

#pragma once

#include <nlohmann/json.hpp>
#include <stdexcept>
#include <variant>

using json = nlohmann::json;

namespace dai {
namespace internal {
template <std::size_t N>
struct VariantSwitch {
    template <typename Variant>
    void operator()(int index, json const& value, Variant& v) const {
        if(index == N)
            v = value.get<std::variant_alternative_t<N, Variant>>();
        else
            VariantSwitch<N - 1>{}(index, value, v);
    }
};

template <>
struct VariantSwitch<0> {
    template <typename Variant>
    void operator()(int index, json const& value, Variant& v) const {
        if(index == 0)
            v = value.get<std::variant_alternative_t<0, Variant>>();
        else {
            throw std::runtime_error("while converting json to variant: invalid index");
        }
    }
};
}  // namespace internal
}  // namespace dai

namespace nlohmann {
template <typename... Args>
struct adl_serializer<std::variant<Args...>> {
    static void to_json(json& j, std::variant<Args...> const& v) {  // NOLINT this is a specialization, naming conventi  ons don't apply
        std::visit(
            [&](auto&& value) {
                j["index"] = v.index();
                j["value"] = std::forward<decltype(value)>(value);
            },
            v);
    }

    static void from_json(json const& j, std::variant<Args...>& v) {  // NOLINT this is a specialization, naming conventi  ons don't apply
        auto const index = j.at("index").get<int>();
        ::dai::internal::VariantSwitch<sizeof...(Args) - 1>{}(index, j.at("value"), v);
    }
};
}  // namespace nlohmann

namespace dai {
namespace internal {
template <std::size_t N>
struct VariantReadNop {
    template <typename Variant, typename Reader>
    void operator()(int index, Reader* reader, Variant& v) const {
        if(index == N) {
            // using Element = typename std::decay<std::variant_alternative_t<N, Variant>>::type;
            using Element = typename std::variant_alternative_t<N, Variant>;
            Element element;
            const auto status = nop::Encoding<Element>::Read(&element, reader);
            if(!status) {
                throw std::runtime_error("converting libnop object to variant failed: nop::Encoding::Read failed");
            }
            v = element;
        } else {
            VariantReadNop<N - 1>{}(index, reader, v);
        }
    }
};

template <>
struct VariantReadNop<0> {
    template <typename Variant, typename Reader>
    void operator()(int index, Reader* reader, Variant& v) const {
        if(index == 0) {
            // using Element = typename std::decay<std::variant_alternative_t<0, Variant>>::type;
            using Element = typename std::variant_alternative_t<0, Variant>;
            Element element;
            const auto status = nop::Encoding<Element>::Read(&element, reader);
            if(!status) {
                throw std::runtime_error("converting libnop object to variant failed: nop::Encoding::Read failed");
            }
            v = element;
        } else {
            throw std::runtime_error("converting libnop object to variant failed: invalid index");
        }
    }
};
}  // namespace internal
}  // namespace dai

// std::variant serialization for libnop
namespace nop {
//
// std::variant<Ts...> encoding format:
//
// +-----+---------+-----------+
// | VAR | INT32:I | ELEMENT I |
// +-----+---------+-----------+
//
// Elements are expected to be valid encodings for their element type.
//
// EmptyVariant encoding format:
//
// +-----+
// | NIL |
// +-----+
//
// Therefore a Variant in the empty state has this specific encoding:
//
// +-----+----+-----+
// | VAR | -1 | NIL |
// +-----+----+-----+
//

template <typename... Ts>
struct Encoding<std::variant<Ts...>> : EncodingIO<std::variant<Ts...>> {
    using Type = std::variant<Ts...>;

    static constexpr EncodingByte Prefix(const Type& /*value*/) {
        return EncodingByte::Variant;
    }

    static constexpr std::size_t Size(const Type& value) {
        return BaseEncodingSize(Prefix(value)) + Encoding<std::int32_t>::Size(value.index())
               + std::visit(
                   [](const auto& element) {
                       using Element = typename std::decay<decltype(element)>::type;
                       return Encoding<Element>::Size(element);
                   },
                   value);
    }

    static constexpr bool Match(EncodingByte prefix) {
        return prefix == EncodingByte::Variant;
    }

    template <typename Writer>
    static constexpr Status<void> WritePayload(EncodingByte /*prefix*/, const Type& value, Writer* writer) {
        auto status = Encoding<std::int32_t>::Write(value.index(), writer);
        if(!status) return status;

        return std::visit(
            [writer](const auto& element) {
                using Element = typename std::decay<decltype(element)>::type;
                return Encoding<Element>::Write(element, writer);
            },
            value);
    }

    template <typename Reader>
    static constexpr Status<void> ReadPayload(EncodingByte /*prefix*/, Type* value, Reader* reader) {
        std::int32_t index = 0;
        auto status = Encoding<std::int32_t>::Read(&index, reader);
        if(!status) {
            return status;
        } else if(index < 0 || index >= static_cast<std::int32_t>(sizeof...(Ts))) {
            return ErrorStatus::UnexpectedVariantType;
        }
        ::dai::internal::VariantReadNop<sizeof...(Ts) - 1>{}(index, reader, *value);
        return {};
    }
};
}  // namespace nop