VariantUtils.h#
Fully qualified name: carb/variant/VariantUtils.h
File members: carb/variant/VariantUtils.h
// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.
#pragma once
#include "VariantTypes.h"
#include <functional> // for std::hash
namespace carb
{
namespace variant
{
namespace detail
{
void leakVariantAfterUnload(Variant& v) noexcept;
} // namespace detail
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 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:
    friend void detail::leakVariantAfterUnload(Variant& v) noexcept;
    VariantData& data() noexcept
    {
        return *this;
    }
    friend bool operator==(const Variant&, const Variant&);
    friend bool operator!=(const Variant&, const Variant&);
};
// 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;
};
inline bool operator==(const Variant& lhs, const Variant& rhs)
{
    return traits::equals(lhs, rhs);
}
inline bool operator!=(const Variant& lhs, const Variant& rhs)
{
    return !(lhs == rhs);
}
namespace detail
{
// Only for use after carb.variant is unloaded.
inline void leakVariantAfterUnload(Variant& v) noexcept
{
    v.vtable = nullptr;
}
} // namespace detail
} // namespace variant
namespace variant_literals
{
[[nodiscard]] inline variant::Variant operator"" _v(unsigned long long val) noexcept
{
    return variant::Variant{ val };
}
[[nodiscard]] inline variant::Variant operator"" _v(long double val) noexcept
{
    return variant::Variant{ (double)val };
}
[[nodiscard]] inline variant::Variant operator"" _v(const char* str, [[maybe_unused]] size_t length) noexcept
{
    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);
}
} // namespace std
#endif