#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/YAMLTraits.h" #include "revng/ADT/KeyTraits.h" #include "revng/ADT/KeyedObjectContainer.h" #include "revng/ADT/KeyedObjectTraits.h" #include "revng/Support/Assert.h" #include "revng/Support/Debug.h" #include "revng/Support/YAMLTraits.h" // clang-format off template concept NotTupleTreeCompatible = (not IsContainer and not HasTupleSize and not IsUpcastablePointer); // clang-format on // clang-format off template concept Yamlizable = llvm::yaml::has_DocumentListTraits::value or llvm::yaml::has_MappingTraits::value or llvm::yaml::has_SequenceTraits::value or llvm::yaml::has_BlockScalarTraits::value or llvm::yaml::has_CustomMappingTraits::value or llvm::yaml::has_PolymorphicTraits::value or llvm::yaml::has_ScalarTraits::value or llvm::yaml::has_ScalarEnumerationTraits::value; // clang-format on template concept NotYamlizable = not Yamlizable; namespace detail { struct NoYaml {}; static_assert(NotYamlizable); } // end namespace detail static_assert(Yamlizable); static_assert(Yamlizable>); // // slice // /// Copy into a std::array a slice of an llvm::ArrayRef template std::array slice(llvm::ArrayRef Old) { std::array Result; auto StartIt = Old.begin() + Start; std::copy(StartIt, StartIt + Size, Result.begin()); return Result; } /// Copy into a std::array a slice of a std::array template std::array slice(const std::array &Old) { std::array Result; auto StartIt = Old.begin() + Start; std::copy(StartIt, StartIt + Size, Result.begin()); return Result; } // // TupleLikeTraits // /// Trait to provide name of the tuple-like class and its fields template struct TupleLikeTraits { // static const char *name(); // template // static const char *fieldName(); }; // // Implementation of MappingTraits for TupleLikeTraits implementors // /// Tuple-liek can implement llvm::yaml::MappingTraits inheriting this class template struct TupleLikeMappingTraits { // Recursive step template static void mapping(llvm::yaml::IO &io, T &Obj) { // Define the field using getTupleFieldName and the associated field io.mapRequired(TupleLikeTraits::template fieldName(), get(Obj)); // Recur mapping(io, Obj); } // Base case template<> void mapping>(llvm::yaml::IO &io, T &Obj) {} }; // // visitTupleTree implementation // namespace tupletree::detail { template requires IsTupleEnd void visitTuple(Visitor &V, T &Obj) { } template requires IsNotTupleEnd void visitTuple(Visitor &V, T &Obj) { // Visit the field visitTupleTree(V, get(Obj)); // Visit next element in tuple visitTuple(V, Obj); } } // namespace tupletree::detail // UpcastablePointerLike-like template void visitTupleTree(Visitor &V, T &Obj) { upcast(Obj, [&V](auto &Upcasted) { visitTupleTree(V, Upcasted); }); } // Tuple-like template void visitTupleTree(Visitor &V, T &Obj) { V.preVisit(Obj); tupletree::detail::visitTuple(V, Obj); V.postVisit(Obj); } // Container-like template void visitTupleTree(Visitor &V, T &Obj) { V.preVisit(Obj); using value_type = typename T::value_type; for (value_type &Element : Obj) { visitTupleTree(V, Element); } V.postVisit(Obj); } // All the others template void visitTupleTree(Visitor &V, T &Element) { V.preVisit(Element); V.postVisit(Element); } template void visitTupleTree(T &Element, const Pre &PreVisitor, const Post &PostVisitor) { struct { const Pre &preVisit; const Post &postVisit; } Visitor{ PreVisitor, PostVisitor }; visitTupleTree(Visitor, Element); } /// Default visitor, doing nothing struct DefaultTupleTreeVisitor { template void preVisit(T &) {} template void postVisit(T &) {} }; // // tupleIndexByName // template requires IsTupleEnd size_t tupleIndexByName(llvm::StringRef Name) { return -1; } template requires IsNotTupleEnd size_t tupleIndexByName(llvm::StringRef Name) { llvm::StringRef ThisName = TupleLikeTraits::template fieldName(); if (Name == ThisName) return I; else return tupleIndexByName(Name); } // // getByKey // namespace tupletree::detail { template requires IsTupleEnd ResultT *getByKeyTuple(RootT &M, KeyT Key) { return nullptr; } template requires IsNotTupleEnd ResultT *getByKeyTuple(RootT &M, KeyT Key) { if (I == Key) { using tuple_element = typename std::tuple_element::type; revng_assert((std::is_same_v) ); return reinterpret_cast(&get(M)); } else { return getByKeyTuple(M, Key); } } } // namespace tupletree::detail template ResultT getByKey(RootT &M, KeyT Key) { auto Dispatcher = [&](auto &Upcasted) { return getByKey(Upcasted, Key); }; return upcast(M, Dispatcher, ResultT{}); } template ResultT getByKey(RootT &M, KeyT Key) { return tupletree::detail::getByKeyTuple(M, Key); } template ResultT *getByKey(RootT &M, KeyT Key) { for (auto &Element : M) { using KOT = KeyedObjectTraits>; if (KOT::key(Element) == Key) return ∈ } return nullptr; } // // callOnPathSteps (no instance) // template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path); template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { return false; } template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { using element_type = pointee; return callOnPathStepsTuple(V, Path); } template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { using value_type = typename RootT::value_type; using KOT = KeyedObjectTraits; using key_type = decltype(KOT::key(std::declval())); constexpr size_t IntsCount = KeyTraits::IntsCount; auto PathStep = slice<0, IntsCount>(Path); auto TargetKey = KeyTraits::fromInts(PathStep); V.template visitContainerElement(TargetKey); if (Path.size() > IntsCount) { return callOnPathSteps(V, Path.slice(IntsCount)); } return true; } namespace tupletree::detail { template requires IsTupleEnd bool callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path) { return true; } template requires IsNotTupleEnd bool callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path) { if (Path[0] == I) { using next_type = typename std::tuple_element::type; V.template visitTupleElement(); if (Path.size() > 1) { return callOnPathSteps(V, Path.slice(1)); } } else { return callOnPathStepsTuple(V, Path); } return true; } } // namespace tupletree::detail template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { return tupletree::detail::callOnPathStepsTuple(V, Path); } // // callOnPathSteps (with instance) // namespace tupletree::detail { template requires IsTupleEnd bool callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path, RootT &M) { return true; } template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { return false; } template requires IsNotTupleEnd bool callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path, RootT &M) { if (Path[0] == I) { using next_type = typename std::tuple_element::type; next_type &Element = get(M); V.template visitTupleElement(Element); if (Path.size() > 1) { return callOnPathSteps(V, Path.slice(1), Element); } } else { return callOnPathStepsTuple(V, Path, M); } return true; } } // namespace tupletree::detail template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { auto Dispatcher = [&](auto &Upcasted) { return callOnPathStepsTuple(V, Path, Upcasted); }; // TODO: in case of nullptr we should abort return upcast(M, Dispatcher, false); } template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { return tupletree::detail::callOnPathStepsTuple(V, Path, M); } template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { using value_type = typename RootT::value_type; using KOT = KeyedObjectTraits; using key_type = decltype(KOT::key(std::declval())); constexpr size_t IntsCount = KeyTraits::IntsCount; auto PathStep = slice<0, IntsCount>(Path); auto TargetKey = KeyTraits::fromInts(PathStep); value_type *Matching = nullptr; for (value_type &Element : M) { using KOT = KeyedObjectTraits; if (KOT::key(Element) == TargetKey) { Matching = ∈ break; } } if (Matching == nullptr) return false; V.template visitContainerElement(TargetKey, *Matching); if (Path.size() > IntsCount) { return callOnPathSteps(V, Path.slice(IntsCount), *Matching); } return true; } // // callByPath (no instance) // namespace tupletree::detail { template struct CallByPathVisitor { size_t PathSize; Visitor &V; template void visitTupleElement() { --PathSize; if (PathSize == 0) V.template visitTupleElement(); } template void visitContainerElement(KeyT Key) { constexpr size_t IntsCount = KeyTraits::IntsCount; PathSize -= IntsCount; if (PathSize == 0) V.template visitContainerElement(Key); } }; } // namespace tupletree::detail template bool callByPath(Visitor &V, const KeyIntVector &Path) { using namespace tupletree::detail; CallByPathVisitor CBPV{ Path.size(), V }; return callOnPathSteps(CBPV, Path); } // // callByPath (with instance) // namespace tupletree::detail { template struct CallByPathVisitorWithInstance { size_t PathSize; Visitor &V; template void visitTupleElement(K &Element) { --PathSize; if (PathSize == 0) V.template visitTupleElement(Element); } template void visitContainerElement(KeyT Key, K &Element) { constexpr size_t IntsCount = KeyTraits::IntsCount; PathSize -= IntsCount; if (PathSize == 0) V.template visitContainerElement(Key, Element); } }; } // namespace tupletree::detail template bool callByPath(Visitor &V, const KeyIntVector &Path, RootT &M) { using namespace tupletree::detail; CallByPathVisitorWithInstance CBPV{ Path.size(), V }; return callOnPathSteps(CBPV, Path, M); } // // getByPath // namespace tupletree::detail { template struct GetByPathVisitor { ResultT *Result = nullptr; template void visitContainerElement(KeyT, K &) { Result = nullptr; } template void visitContainerElement(KeyT, ResultT &Element) { Result = ∈ } template void visitTupleElement(K &) { Result = nullptr; } template void visitTupleElement(ResultT &Element) { Result = ∈ } }; } // namespace tupletree::detail template ResultT *getByPath(const KeyIntVector &Path, RootT &M) { using namespace tupletree::detail; GetByPathVisitor GBPV; if (not callByPath(GBPV, Path, M)) return nullptr; else return GBPV.Result; } // // pathAsString // namespace tupletree::detail { class DumpPathVisitor { private: llvm::raw_string_ostream Stream; public: DumpPathVisitor(std::string &Result) : Stream(Result) {} template void visitTupleElement() { Stream << "/" << TupleLikeTraits::template fieldName(); } template void visitContainerElement(KeyT Key) { Stream << "/" << getNameFromYAMLScalar(Key); } }; } // namespace tupletree::detail template std::optional pathAsString(const KeyIntVector &Path) { std::string Result; { tupletree::detail::DumpPathVisitor PV(Result); if (not callOnPathSteps(PV, Path)) return {}; } return Result; } class PathMatcher { private: KeyIntVector Path; std::vector> Free; private: PathMatcher() = default; public: template static std::optional create(llvm::StringRef Path) { revng_assert(Path.startswith("/")); PathMatcher Result; if (visitTupleTreeNode(Path.substr(1), Result)) return Result; else return {}; } public: const KeyIntVector &path() const { return Path; } public: template KeyIntVector apply(Ts... Args) const { revng_assert(sizeof...(Args) == Free.size()); KeyIntVector Result = Path; applyImpl<0, Ts...>(Result, Args...); return Result; } template std::optional> match(const KeyIntVector &Search) { revng_assert(sizeof...(Args) == Free.size()); if (Path.size() != Search.size()) return {}; // // Check non-variable parts match // using Pair = std::pair; std::vector Terminator{ { Path.size(), 0 } }; size_t LastEnd = 0; for (auto [Start, Size] : llvm::concat(Free, Terminator)) { for (size_t I = LastEnd; I < Start; ++I) { if (Search[I] != Path[I]) return {}; } LastEnd = Start + Size; } // // Compute result // std::tuple Result; extractKeys(Search, Result); return Result; } private: template void depositKey(KeyIntVector &Result, T Arg) const { auto [Start, Size] = Free.at(I); revng_assert(Size == KeyTraits::IntsCount); for (auto P : llvm::enumerate(KeyTraits::toInts(Arg))) Result[Start + P.index()] = P.value(); } template void applyImpl(KeyIntVector &Result, T Arg) const { depositKey(Result, Arg); } template void applyImpl(KeyIntVector &Result, T Arg, Ts... Args) const { depositKey(Result, Arg); applyImpl(Result, Args...); } template void extractKeys(const KeyIntVector &Search, T &Tuple) const { if constexpr (I < std::tuple_size_v) { using element = std::tuple_element_t; using IntsArray = typename KeyTraits::IntsArray; IntsArray Ints; auto [Start, Size] = Free[I]; for (auto P : llvm::enumerate(Ints)) P.value() = Search[Start + P.index()]; std::get(Tuple) = KeyTraits::fromInts(Ints); extractKeys(Search, Tuple); } } private: template static bool visitTuple(llvm::StringRef Current, llvm::StringRef Rest, PathMatcher &Result); template static bool visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result); template static bool visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result); template static bool visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result); template static bool visitTupleTreeNode(llvm::StringRef Path, PathMatcher &Result); }; template bool PathMatcher::visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result) { using element_type = std::remove_reference_t())>; return PathMatcher::visitTupleTreeNode(String, Result); } template bool PathMatcher::visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result) { if (String.size() == 0) return true; auto [Before, After] = String.split('/'); return visitTuple(Before, After, Result); } template bool PathMatcher::visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result) { if (String.size() == 0) return true; auto [Before, After] = String.split('/'); using Key = std::remove_cv_t; using Value = typename T::value_type; if (Before == "*") { auto Count = KeyTraits::IntsCount; Result.Free.push_back({ Result.Path.size(), Count }); for (size_t I = 0; I < Count; ++I) Result.Path.push_back(0); } else { for (KeyInt I : KeyTraits::toInts(getValueFromYAMLScalar(Before))) Result.Path.push_back(I); } return visitTupleTreeNode(After, Result); } template bool PathMatcher::visitTupleTreeNode(llvm::StringRef Path, PathMatcher &Result) { return Path.size() == 0; } template bool PathMatcher::visitTuple(llvm::StringRef Current, llvm::StringRef Rest, PathMatcher &Result) { if constexpr (I < std::tuple_size_v) { if (TupleLikeTraits::template fieldName() == Current) { Result.Path.push_back(I); using element = typename std::tuple_element_t; return PathMatcher::visitTupleTreeNode(Rest, Result); } else { return visitTuple(Current, Rest, Result); } } else { // Not found return false; } } template std::optional stringAsPath(llvm::StringRef Path) { auto Result = PathMatcher::create(Path); if (Result) return Result->path(); else return {}; } template ResultT *getByPath(llvm::StringRef Path, RootT &M) { auto MaybeKeyVector = stringAsPath(Path); if (not MaybeKeyVector) return {}; else return getByPath(*MaybeKeyVector, M); } // // validateTupleTree // template constexpr bool validateTupleTree(L); template constexpr bool validateTupleTree(L); template constexpr bool validateTupleTree(L); template constexpr bool validateTupleTree(L); template constexpr bool validateTupleTree(L Check) { return Check((T *) nullptr) and validateTupleTree(Check); } template constexpr bool validateTupleTree(L Check) { return Check((T *) nullptr) and validateTupleTree(Check); } template constexpr bool validateTupleTree(L Check) { return Check((T *) nullptr); } template constexpr bool validateTupleTree(L Check) { if constexpr (I == 0 and not Check((T *) nullptr)) return false; if constexpr (I < std::tuple_size_v) { if constexpr (not validateTupleTree>(Check)) return false; return validateTupleTree(Check); } return true; } // // FOR_EACH macro implemenation // #define GET_MACRO(_0, \ _1, \ _2, \ _3, \ _4, \ _5, \ _6, \ _7, \ _8, \ _9, \ _10, \ _11, \ _12, \ _13, \ _14, \ _15, \ _16, \ NAME, \ ...) \ NAME #define NUMARGS(...) \ GET_MACRO(_0, \ __VA_ARGS__, \ 16, \ 15, \ 14, \ 13, \ 12, \ 11, \ 10, \ 9, \ 8, \ 7, \ 6, \ 5, \ 4, \ 3, \ 2, \ 1) #define FE_0(ACTION, TOTAL, ARG) #define FE_1(ACTION, TOTAL, ARG, X) ACTION(ARG, (TOTAL) -0, X) #define FE_2(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -1, X) \ FE_1(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_3(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -2, X) \ FE_2(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_4(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -3, X) \ FE_3(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_5(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -4, X) \ FE_4(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_6(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -5, X) \ FE_5(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_7(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -6, X) \ FE_6(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_8(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -7, X) \ FE_7(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_9(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -8, X) \ FE_8(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_10(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -9, X) \ FE_9(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_11(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -10, X) \ FE_10(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_12(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -11, X) \ FE_11(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_13(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -12, X) \ FE_12(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_14(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -13, X) \ FE_13(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_15(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -14, X) \ FE_14(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_16(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -15, X) \ FE_15(ACTION, TOTAL, ARG, __VA_ARGS__) /// Calls ACTION(ARG, INDEX, VA_ARG) for each VA_ARG in ... #define FOR_EACH(ACTION, ARG, ...) \ GET_MACRO(_0, \ __VA_ARGS__, \ FE_16, \ FE_15, \ FE_14, \ FE_13, \ FE_12, \ FE_11, \ FE_10, \ FE_9, \ FE_8, \ FE_7, \ FE_6, \ FE_5, \ FE_4, \ FE_3, \ FE_2, \ FE_1, \ FE_0) \ (ACTION, (NUMARGS(__VA_ARGS__) - 1), ARG, __VA_ARGS__) // // Macros to transform struct in tuple-like // #define TUPLE_ELEMENTS(class, index, field) \ template<> \ struct std::tuple_element { \ using type = decltype(class ::field); \ }; #define GET_IMPLEMENTATIONS(class, index, field) \ else if constexpr (I == index) return x.field; #define GET_TUPLE_FIELD_NAME(class, index, field) \ template<> \ const char *fieldName() { \ return #field; \ } #define INTROSPECTION_1(class, ...) \ template<> \ struct std::tuple_size \ : std::integral_constant {}; \ \ FOR_EACH(TUPLE_ELEMENTS, class, __VA_ARGS__) \ \ template<> \ struct TupleLikeTraits { \ static const char *name() { return #class; } \ \ template \ static const char *fieldName(); \ \ FOR_EACH(GET_TUPLE_FIELD_NAME, class, __VA_ARGS__) \ }; #define INTROSPECTION_2(class, ...) \ template \ auto &get(class &&x) { \ if constexpr (false) \ return NULL; \ FOR_EACH(GET_IMPLEMENTATIONS, class, __VA_ARGS__) \ } \ \ template \ const auto &get(const class &x) { \ if constexpr (false) \ return NULL; \ FOR_EACH(GET_IMPLEMENTATIONS, class, __VA_ARGS__) \ } \ \ template \ auto &get(class &x) { \ if constexpr (false) \ return NULL; \ FOR_EACH(GET_IMPLEMENTATIONS, class, __VA_ARGS__) \ } #define INTROSPECTION(class, ...) \ INTROSPECTION_1(class, __VA_ARGS__) \ INTROSPECTION_2(class, __VA_ARGS__) #define INTROSPECTION_NS(ns, class, ...) \ INTROSPECTION_1(ns::class, __VA_ARGS__) \ namespace ns { \ INTROSPECTION_2(class, __VA_ARGS__) \ } template class TupleTree; template class TupleTreeReference { friend class TupleTree; public: using pointee = T; public: RootT *Root = nullptr; KeyIntVector Path; public: static TupleTreeReference fromPath(const KeyIntVector &Path) { TupleTreeReference Result; Result.Path = Path; return Result; } static TupleTreeReference fromString(llvm::StringRef Path) { return fromPath(*stringAsPath(Path)); } public: std::string toString() const { return *pathAsString(Path); } const KeyIntVector &path() const { return Path; } T *get() const { revng_check(Root != nullptr); return getByPath(Path, *Root); } }; template concept IsTupleTreeReference = is_specialization_v; template struct llvm::yaml::ScalarTraits { static void output(const T &Obj, void *, llvm::raw_ostream &Out) { Out << Obj.toString(); } static llvm::StringRef input(llvm::StringRef Path, void *, T &Obj) { Obj = T::fromString(Path); return {}; } static auto mustQuote(llvm::StringRef) { return llvm::yaml::QuotingType::Double; } }; // How to improve performance without losing safety of a `TupleTree`: // // * `TupleTreeReference` must contain a `std::variant` between what they // have right now and a naked pointer. // * The `operator* const` of `UpcastablePointer` (which should be // renamed to *Variant*) should return a constant reference. Same // for `TupleTreeReference`. // * `TupleTree` should have: // * `const TupleTree freeze()`: `std::move` itself in the `const` // result and transforms all the `TupleTreeReference`s in direct // pointers. // * `TupleTree unfreeze()`: `std::move` itself in the `const` // result and transforms all the `TupleTreeReference`s in root + // key. // * Alternatively, we could push the functionality of `ModelWrapper` // into `TupleTree`. In this way, the default behavior would be to // be frozen. A RAII wrapper could take care of unfreeze and // refreeze the TupleTree. // TODO: `const` stuff is not YAML-serializable template void serialize(S &Stream, T &Element) { llvm::yaml::Output YAMLOutput(Stream); YAMLOutput << Element; } template class TupleTree { private: std::unique_ptr Root; public: TupleTree() : Root(new T) {} // Prevent accidental copy TupleTree(const TupleTree &Other) = delete; TupleTree &operator=(const TupleTree &Other) = delete; // Moving is fine TupleTree(TupleTree &&Other) = default; TupleTree &operator=(TupleTree &&Other) = default; // Explicit cloning TupleTree clone(const TupleTree &Other) const { TupleTree Result; // Copy the root Result.Root.reset(new T(*Root)); // Update references to root Result.initializeReferences(); return Result; } public: static TupleTree deserialize(llvm::StringRef YAMLString) { TupleTree Result; Result.Root = std::make_unique(); llvm::yaml::Input YAMLInput(YAMLString); YAMLInput >> *Result.Root; // Update references to root Result.initializeReferences(); return Result; } public: template void serialize(S &Stream) const { serialize(Stream, Root); } public: auto get() const noexcept { return Root.get(); } auto &operator*() const { return *Root; } auto *operator->() const noexcept { return Root.operator->(); } public: bool verify() const debug_function { return verifyReferences(); } void initializeReferences() { visitReferences([this](auto &Element) { Element.Root = Root.get(); }); } private: bool verifyReferences() const { bool Result = true; visitReferences([&Result, this](const auto &Element) { Result = Result and (Element.Root == Root.get()); }); return Result; } template void visitReferences(const L &InnerVisitor) { auto Visitor = [&InnerVisitor](auto &Element) { using type = std::remove_cvref_t; if constexpr (IsTupleTreeReference) InnerVisitor(Element); }; visitTupleTree(*Root, Visitor, [](auto) {}); } template void visitReferences(const L &InnerVisitor) const { auto Visitor = [&InnerVisitor](const auto &Element) { using type = std::remove_cvref_t; if constexpr (IsTupleTreeReference) InnerVisitor(Element); }; visitTupleTree(*Root, Visitor, [](auto) {}); } }; static_assert(std::is_default_constructible_v>); static_assert(not std::is_copy_assignable_v>); static_assert(not std::is_copy_constructible_v>); static_assert(std::is_move_assignable_v>); static_assert(std::is_move_constructible_v>);