// Copyright Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #pragma once #include #include #include #include #include #include #include #include #ifdef ROCROLLER_USE_LLVM namespace llvm { namespace yaml { struct EmptyContext; } } #endif namespace rocRoller { namespace Serialization { #ifdef ROCROLLER_USE_LLVM using EmptyContext = llvm::yaml::EmptyContext; #else struct EmptyContext { }; #endif /** * Override this struct for a type to use a custom constructor. * * Useful for Variant and SharedPointer is the types default * contructor has been explicitly deleted. */ template struct DefaultConstruct { static T call() { return U(); } }; /** * Override this struct for a type to use a custom string serialization. * * You must implement: * 1. static std::string output(T) * or static std::string output(T const&) * * to convert a T to a string, and * * 2. * static void input(std::string const& string, T& ) * * to convert a string to a T. * */ template struct ScalarTraits { }; template struct IOTraits { }; template struct MappingTraits { static const bool flow = false; }; template struct CustomMappingTraits { // cppcheck-suppress duplInheritedMember static const bool flow = false; }; template struct SequenceTraits { using Value = int; static const bool flow = false; }; template struct EnumTraits { }; template struct EmptyMappingTraits { using iot = IOTraits; // static_assert(Object::HasValue == false, // "Object has a value. Use the value base class."); static void mapping(IO& io, Object& obj) {} static void mapping(IO& io, Object& obj, Context&) {} const static bool flow = true; }; template struct ValueMappingTraits { using iot = IOTraits; static_assert(Object::HasValue == true, "Object has no value. Use the empty base class."); static void mapping(IO& io, Object& obj) { iot::mapRequired(io, "value", obj.value); } const static bool flow = true; }; template struct IndexMappingTraits { using iot = IOTraits; static_assert(Object::HasIndex == true, "Object doesn't have index/value. Use the empty base class."); static void mapping(IO& io, Object& obj) { iot::mapRequired(io, "index", obj.index); } const static bool flow = true; }; template struct IndexValueMappingTraits { using iot = IOTraits; static_assert(Object::HasIndex == true && Object::HasValue == true, "Object doesn't have index/value. Use the empty base class."); static void mapping(IO& io, Object& obj) { iot::mapRequired(io, "index", obj.index); iot::mapRequired(io, "value", obj.value); } const static bool flow = true; }; template struct AutoMappingTraits { }; template struct AutoMappingTraits : public EmptyMappingTraits { }; template struct AutoMappingTraits : public ValueMappingTraits { }; template struct AutoMappingTraits : public IndexMappingTraits { }; template struct AutoMappingTraits : public IndexValueMappingTraits { }; template struct SubclassMappingTraits { }; template struct PointerMappingTraits; /** * Used by AutoMappingTraits to serialize an object via a std::shared_ptr where the object is of a type derived from Base. */ template struct PointerMappingTraits { using Subclass = typename SubclassPtr::element_type; using iot = IOTraits; template requires(std::derived_from) static bool mapping( IO& io, std::shared_ptr& p, Context& ctx) { std::shared_ptr sc; if(iot::outputting(io)) { sc = std::dynamic_pointer_cast(p); } else { sc = std::make_shared(); p = sc; } MappingTraits::mapping(io, *sc, ctx); return true; } template requires( std::same_as< EmptyContext, Context> && (std::derived_from)) static bool mapping(IO& io, std::shared_ptr< Base>& p) { Context ctx; return mapping(io, p, ctx); } }; /** * Used to non-polymorphically serialize an object via std::shared_ptr. * * Set Nullable to true to allow serializing `nullptr`. */ template struct SharedPointerMappingTraits { using Element = typename SharedPtr::element_type; using iot = IOTraits; static void mapping(IO& io, SharedPtr& p, Context& ctx) { bool isNull = false; if(Nullable) { if(iot::outputting(io)) { isNull = (p == nullptr); } if(isNull || !iot::outputting(io)) { iot::mapOptional(io, "is-null", isNull); } } if(!isNull && !iot::outputting(io)) { p = std::make_shared(DefaultConstruct::call()); } AssertFatal(Nullable || p != nullptr); if(p != nullptr) { MappingTraits::mapping(io, *p, ctx); } } static void mapping(IO& io, SharedPtr& p) { AssertFatal((std::same_as)); Context ctx; mapping(io, p, ctx); } }; template struct BaseClassMappingTraits { using iot = IOTraits; static void mapping(IO& io, std::shared_ptr& value) { std::string type; if(iot::outputting(io)) type = value->type(); iot::mapRequired(io, "type", type); if(!SubclassMappingTraits::mapping(io, type, value)) iot::setError(io, "Unknown subclass type " + type); } const static bool flow = Flow; }; template struct DefaultSubclassMappingTraits; template struct DefaultSubclassMappingTraits { using iot = IOTraits; using SubclassFn = bool(IO&, typename std::shared_ptr&, Context&); using SubclassMap = std::unordered_map>; template static typename SubclassMap::value_type Pair() { auto f = PointerMappingTraits::template mapping; return typename SubclassMap::value_type(Subclass::Type(), f); } static bool mapping(IO& io, std::string const& type, std::shared_ptr& p, Context& ctx) { auto iter = CRTP_Traits::subclasses.find(type); if(iter != CRTP_Traits::subclasses.end()) return iter->second(io, p, ctx); return false; } }; template struct DefaultSubclassMappingTraits { using iot = IOTraits; using SubclassFn = bool(IO&, typename std::shared_ptr&); using SubclassMap = std::unordered_map>; template static typename SubclassMap::value_type Pair() { auto f = PointerMappingTraits::template mapping; return typename SubclassMap::value_type(Subclass::Type(), f); } static bool mapping(IO& io, std::string const& type, std::shared_ptr& p) { auto iter = CRTP_Traits::subclasses.find(type); if(iter != CRTP_Traits::subclasses.end()) return iter->second(io, p); return false; } }; } }