carb/variant/VariantUtils.h

File members: carb/variant/VariantUtils.h

// Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "VariantTypes.h"

namespace carb
{
namespace variant
{

template <class Type>
VariantData translate(Type&& type) noexcept;

struct traits
{
    static void swap(VariantData& lhs, VariantData& rhs) noexcept;

    static void destruct(VariantData& self) noexcept;

    static VariantData copy(const VariantData& self) noexcept;

    static bool equals(const VariantData& self, const VariantData& other) noexcept;

    static omni::string toString(const VariantData& self) noexcept;

    static bool convertTo(const VariantData& self, const VTable* newType, VariantData& out) noexcept;

    static size_t hash(const VariantData& self) noexcept;
};

class Variant final : protected VariantData
{
public:
    Variant() noexcept;

#ifndef DOXYGEN_BUILD
    template <class T, typename std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value, bool> = true>
#else
    template <class T>
#endif
    explicit Variant(T&& val) noexcept
    {
        // This function cannot be externally inlined due to MSVC not understanding the enable_if in the inlined
        // function.
        data() = translate(std::forward<T>(val));
    }

    ~Variant() noexcept;

    Variant(const Variant& other) noexcept;

    Variant& operator=(const Variant& other) noexcept;

    Variant(Variant&& other) noexcept;

    Variant& operator=(Variant&& other) noexcept;

    bool operator==(const Variant& other) const noexcept;

    bool operator!=(const Variant& other) const noexcept;

    bool hasValue() const noexcept;

    omni::string toString() const noexcept;

    size_t getHash() const noexcept;

    template <class T>
    cpp::optional<T> getValue() const noexcept;

    template <class T>
    T getValueOr(T&& fallback) const noexcept;

    template <class T>
    Variant convertTo() const noexcept;

    const VariantData& data() const noexcept
    {
        return *this;
    }

private:
    VariantData& data() noexcept
    {
        return *this;
    }
};
// The Variant class is intended only to add functions on top of VariantData. Therefore, the size must match.
static_assert(sizeof(Variant) == sizeof(VariantData), "");

// This is an ABI-stable representation of std::pair<Variant, Variant>, used only by Translator and internally by
// carb.variant.plugin.
struct VariantPair
{
    Variant first;
    Variant second;
};
static_assert(std::is_standard_layout<VariantPair>::value, ""); // not interop-safe because not trivially copyable

struct KeyValuePair
{
    const Variant first;
    Variant second;
};
static_assert(std::is_standard_layout<KeyValuePair>::value, ""); // not interop-safe because not trivially copyable

class Registrar
{
public:
    constexpr Registrar() noexcept;

    Registrar(const VTable* vtable) noexcept;

    ~Registrar() noexcept;

    Registrar(Registrar&& other) noexcept;

    Registrar& operator=(Registrar&& other) noexcept;

    bool isEmpty() const noexcept;

    RString getType() const noexcept;

    void reset() noexcept;

private:
    RString m_type;
};

} // namespace variant

namespace variant_literals
{

CARB_NODISCARD inline variant::Variant operator"" _v(unsigned long long val) noexcept
{
    return variant::Variant{ val };
}

CARB_NODISCARD inline variant::Variant operator"" _v(long double val) noexcept
{
    return variant::Variant{ (double)val };
}

CARB_NODISCARD inline variant::Variant operator"" _v(const char* str, size_t length) noexcept
{
    CARB_UNUSED(length);
    return variant::Variant{ str };
}

} // namespace variant_literals

} // namespace carb

#ifndef DOXYGEN_SHOULD_SKIP_THIS
namespace std
{

template <>
struct hash<carb::variant::Variant>
{
    size_t operator()(const carb::variant::Variant& v) const noexcept
    {
        return v.getHash();
    }
};

inline std::string to_string(const carb::variant::Variant& v)
{
    auto str = v.toString();
    return std::string(str.begin(), str.end());
}

} // namespace std
#endif