12#include <promeki/config.h>
13#if PROMEKI_ENABLE_CORE
34PROMEKI_NAMESPACE_BEGIN
53 template <
typename T,
typename =
void>
struct HasVariantLookupDispatch : std::false_type {};
56 struct HasVariantLookupDispatch<T, std::void_t<decltype(std::declval<const T &>().variantLookupResolve(
57 std::declval<const String &>(), std::declval<Error *>()))>>
63 template <
typename T>
inline constexpr bool hasVariantLookupDispatchV = HasVariantLookupDispatch<T>::value;
79 struct VariantLookupSegment {
87 bool hasIndex =
false;
108 inline bool parseLeadingSegment(
const String &key, VariantLookupSegment &out, Error *err =
nullptr) {
109 if (err !=
nullptr) *err = Error::Ok;
110 const size_t len = key.byteCount();
112 if (err !=
nullptr) *err = Error::ParseFailed;
115 const char *s = key.cstr();
117 auto isNameChar = [](
char c) ->
bool {
118 return (c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z') || (c >=
'0' && c <=
'9') || c ==
'_' ||
121 while (i < len && isNameChar(s[i])) ++i;
123 if (err !=
nullptr) *err = Error::ParseFailed;
126 out.name = String(s, i);
127 out.hasIndex =
false;
132 if (i < len && s[i] ==
'[') {
134 const size_t numStart = i;
135 while (i < len && s[i] >=
'0' && s[i] <=
'9') ++i;
137 if (err !=
nullptr) *err = Error::ParseFailed;
140 if (i >= len || s[i] !=
']') {
141 if (err !=
nullptr) *err = Error::ParseFailed;
144 char *endp =
nullptr;
145 out.index =
static_cast<size_t>(std::strtoull(s + numStart, &endp, 10));
150 if (i == len)
return true;
153 if (err !=
nullptr) *err = Error::ParseFailed;
158 if (err !=
nullptr) *err = Error::ParseFailed;
161 out.rest = String(s + i, len - i);
188 inline const VariantSpec *walkSpecPath(
const VariantSpec *cur,
const String &path, Error *err =
nullptr) {
189 if (cur ==
nullptr) {
190 if (err !=
nullptr) *err = Error::IdNotFound;
193 if (err !=
nullptr) *err = Error::Ok;
194 String remaining = path;
195 while (!remaining.isEmpty() && cur !=
nullptr) {
196 VariantLookupSegment seg;
200 bool bracketStart = (remaining[0] ==
'[');
204 const char *p = remaining.cstr();
205 const size_t len = remaining.byteCount();
206 while (end < len && p[end] >=
'0' && p[end] <=
'9') ++end;
207 if (end == 1 || end >= len || p[end] !=
']') {
208 if (err !=
nullptr) *err = Error::ParseFailed;
211 cur = cur->elementSpec();
212 if (cur ==
nullptr) {
213 if (err !=
nullptr) *err = Error::IdNotFound;
218 remaining = String();
219 }
else if (p[end] ==
'.') {
220 remaining = remaining.mid(end + 1);
222 if (err !=
nullptr) *err = Error::ParseFailed;
227 if (!parseLeadingSegment(remaining, seg, err))
return nullptr;
232 cur = cur->valueSpec();
233 if (cur ==
nullptr) {
234 if (err !=
nullptr) *err = Error::IdNotFound;
238 cur = cur->elementSpec();
239 if (cur ==
nullptr) {
240 if (err !=
nullptr) *err = Error::IdNotFound;
244 remaining = seg.hasRest ? seg.rest : String();
374template <
typename T>
class VariantLookup {
400 constexpr Key() =
default;
414 static constexpr Key literal(
const char *name) {
return Key(fnv1a(name)); }
423 static Key find(
const String &name) {
424 const Registry &r = registry();
425 ReadWriteLock::ReadLocker lock(r.lock);
426 uint64_t
id = r.findId(name);
427 return id == Invalid ? Key() : Key(id);
436 static constexpr Key fromId(uint64_t
id) {
return Key(
id); }
439 constexpr uint64_t id()
const {
return _id; }
452 String name()
const {
453 const Registry &r = registry();
454 ReadWriteLock::ReadLocker lock(r.lock);
455 auto it = r.names.find(_id);
456 if (it == r.names.end())
return String();
461 constexpr bool isValid()
const {
return _id != Invalid; }
463 constexpr bool operator==(
const Key &other)
const {
return _id == other._id; }
464 constexpr bool operator!=(
const Key &other)
const {
return _id != other._id; }
465 constexpr bool operator<(
const Key &other)
const {
return _id < other._id; }
468 static constexpr uint64_t Invalid = UINT64_MAX;
469 uint64_t _id = Invalid;
471 constexpr explicit Key(uint64_t
id) : _id(id) {}
473 friend class VariantLookup<T>;
481 using ScalarGet = Function<Optional<Variant>(
const T &)>;
491 using ScalarSet = Function<Error(T &,
const Variant &)>;
493 using IndexedScalarGet = Function<Optional<Variant>(
const T &,
size_t)>;
495 using IndexedScalarSet = Function<Error(T &,
size_t,
const Variant &)>;
498 using ComposeResolve = Function<Optional<Variant>(
const T &,
const String &, Error *)>;
500 using ComposeAssign = Function<bool(T &,
const String &,
const Variant &, Error *)>;
503 using IndexedCompResolve =
504 Function<Optional<Variant>(
const T &,
size_t,
const String &, Error *)>;
506 using IndexedCompAssign = Function<bool(T &,
size_t,
const String &,
const Variant &, Error *)>;
519 using ComposeSpecFor = Function<
const VariantSpec *(
const String &, Error *)>;
532 using VariantTreeGet = Function<Variant(
const T &)>;
557 Registrar scalar(
const String &name, ScalarGet get,
558 const VariantSpec *spec =
nullptr)
const {
559 Registry &r = registry();
560 ReadWriteLock::WriteLocker lock(r.lock);
561 uint64_t
id = r.declareName(name);
562 r.scalars.insert(
id, ScalarEntry{std::move(get), ScalarSet(), spec});
567 Registrar scalar(
const String &name, ScalarGet get, ScalarSet set,
568 const VariantSpec *spec =
nullptr)
const {
569 Registry &r = registry();
570 ReadWriteLock::WriteLocker lock(r.lock);
571 uint64_t
id = r.declareName(name);
572 r.scalars.insert(
id, ScalarEntry{std::move(get), std::move(set), spec});
577 Registrar indexedScalar(
const String &name, IndexedScalarGet get,
578 const VariantSpec *spec =
nullptr)
const {
579 Registry &r = registry();
580 ReadWriteLock::WriteLocker lock(r.lock);
581 uint64_t
id = r.declareName(name);
582 r.indexedScalars.insert(
583 id, IndexedScalarEntry{std::move(get), IndexedScalarSet(), spec});
588 Registrar indexedScalar(
const String &name, IndexedScalarGet get, IndexedScalarSet set,
589 const VariantSpec *spec =
nullptr)
const {
590 Registry &r = registry();
591 ReadWriteLock::WriteLocker lock(r.lock);
592 uint64_t
id = r.declareName(name);
593 r.indexedScalars.insert(
594 id, IndexedScalarEntry{std::move(get), std::move(set), spec});
616 template <
typename U>
617 Registrar child(
const String &name, Function<
const U *(
const T &)> get,
618 Function<U *(T &)> getMut =
nullptr)
const {
619 ComposeResolve resolveFn = [get](
const T &t,
const String &rest,
620 Error *err) -> Optional<Variant> {
623 if (err !=
nullptr) *err = Error::IdNotFound;
626 return VariantLookup<U>::resolve(*u, rest, err);
628 ComposeAssign assignFn;
630 assignFn = [getMut](T &t,
const String &rest,
const Variant &v,
631 Error *err) ->
bool {
634 if (err !=
nullptr) *err = Error::IdNotFound;
637 return VariantLookup<U>::assign(*u, rest, v, err);
640 ComposeSpecFor specFn = [](
const String &rest,
641 Error *err) ->
const VariantSpec * {
642 return VariantLookup<U>::specFor(rest, err);
644 Function<StringList(
const T &,
const String &)> dumpFn =
645 [get](
const T &t,
const String &indent) -> StringList {
647 if (u ==
nullptr)
return StringList();
648 return VariantLookup<U>::dump(*u, indent);
650 Registry &r = registry();
651 ReadWriteLock::WriteLocker lock(r.lock);
652 uint64_t
id = r.declareName(name);
653 r.children.insert(
id, ComposeEntry{std::move(resolveFn), std::move(assignFn),
654 std::move(specFn), std::move(dumpFn)});
668 template <
typename U>
669 Registrar indexedChild(
const String &name,
670 Function<
const U *(
const T &,
size_t)> get,
671 Function<U *(T &,
size_t)> getMut =
nullptr)
const {
672 IndexedCompResolve resolveFn = [get](
const T &t,
size_t idx,
const String &rest,
673 Error *err) -> Optional<Variant> {
674 const U *u = get(t, idx);
676 if (err !=
nullptr) *err = Error::OutOfRange;
679 return VariantLookup<U>::resolve(*u, rest, err);
681 IndexedCompAssign assignFn;
683 assignFn = [getMut](T &t,
size_t idx,
const String &rest,
684 const Variant &v, Error *err) ->
bool {
685 U *u = getMut(t, idx);
687 if (err !=
nullptr) *err = Error::OutOfRange;
690 return VariantLookup<U>::assign(*u, rest, v, err);
693 ComposeSpecFor specFn = [](
const String &rest,
694 Error *err) ->
const VariantSpec * {
695 return VariantLookup<U>::specFor(rest, err);
697 Function<StringList(
const T &,
const String &)> dumpFn =
698 [get](
const T &t,
const String &indent) -> StringList {
706 const String childIndent = indent +
" ";
707 for (
size_t i = 0;; ++i) {
708 const U *u = get(t, i);
709 if (u ==
nullptr)
break;
710 StringList sub = VariantLookup<U>::dump(*u, childIndent);
711 out.pushToBack(indent + String::sprintf(
"[%zu]:", i));
712 for (
const String &l : sub) out.pushToBack(l);
716 Registry &r = registry();
717 ReadWriteLock::WriteLocker lock(r.lock);
718 uint64_t
id = r.declareName(name);
719 r.indexedChildren.insert(
720 id, IndexedCompEntry{std::move(resolveFn), std::move(assignFn),
721 std::move(specFn), std::move(dumpFn)});
758 template <
typename U>
760 indexedChildByValue(
const String &name,
761 Function<Optional<U>(
const T &,
size_t)> get)
const {
762 IndexedCompResolve resolveFn = [get](
const T &t,
size_t idx,
const String &rest,
763 Error *err) -> Optional<Variant> {
764 auto u = get(t, idx);
766 if (err !=
nullptr) *err = Error::OutOfRange;
769 return VariantLookup<U>::resolve(*u, rest, err);
772 IndexedCompAssign assignFn;
773 ComposeSpecFor specFn = [](
const String &rest,
774 Error *err) ->
const VariantSpec *{
775 return VariantLookup<U>::specFor(rest, err);
777 Function<StringList(
const T &,
const String &)> dumpFn =
778 [get](
const T &t,
const String &indent) -> StringList {
784 const String childIndent = indent +
" ";
785 for (
size_t i = 0;; ++i) {
787 if (!u.hasValue())
break;
788 StringList sub = VariantLookup<U>::dump(*u, childIndent);
789 out.pushToBack(indent + String::sprintf(
"[%zu]:", i));
790 for (
const String &l : sub) out.pushToBack(l);
794 Registry &r = registry();
795 ReadWriteLock::WriteLocker lock(r.lock);
796 uint64_t
id = r.declareName(name);
797 r.indexedChildren.insert(
798 id, IndexedCompEntry{std::move(resolveFn), std::move(assignFn),
799 std::move(specFn), std::move(dumpFn)});
825 template <CompiledString DbName>
827 database(
const String &prefix,
828 Function<
const VariantDatabase<DbName> *(
const T &)> get,
829 Function<VariantDatabase<DbName> *(T &)> getMut =
nullptr)
const {
830 ComposeResolve resolveFn = [get](
const T &t,
const String &rest,
831 Error *err) -> Optional<Variant> {
832 const VariantDatabase<DbName> *db = get(t);
834 if (err !=
nullptr) *err = Error::IdNotFound;
837 auto id = VariantDatabase<DbName>::ID::find(rest);
838 if (!
id.isValid() || !db->contains(
id)) {
839 if (err !=
nullptr) *err = Error::IdNotFound;
842 if (err !=
nullptr) *err = Error::Ok;
845 ComposeAssign assignFn;
847 assignFn = [getMut](T &t,
const String &rest,
const Variant &v,
848 Error *err) ->
bool {
849 VariantDatabase<DbName> *db = getMut(t);
851 if (err !=
nullptr) *err = Error::IdNotFound;
854 typename VariantDatabase<DbName>::ID id(rest);
855 if (!db->set(
id, v)) {
856 if (err !=
nullptr) *err = Error::ConversionFailed;
859 if (err !=
nullptr) *err = Error::Ok;
863 ComposeSpecFor specFn = [](
const String &rest,
864 Error *err) ->
const VariantSpec * {
865 const VariantSpec *sp = VariantDatabase<DbName>::specFor(rest);
866 if (sp ==
nullptr && err !=
nullptr) *err = Error::IdNotFound;
869 Function<StringList(
const T &,
const String &)> dumpFn =
870 [get](
const T &t,
const String &indent) -> StringList {
871 const VariantDatabase<DbName> *db = get(t);
873 if (db ==
nullptr)
return out;
881 db->forEach([&out, &indent](
typename VariantDatabase<DbName>::ID
id,
882 const Variant &value) {
886 s += value.typeName();
888 s += value.format(String());
893 Registry &r = registry();
894 ReadWriteLock::WriteLocker lock(r.lock);
895 uint64_t
id = r.declareName(prefix);
896 r.databases.insert(
id, ComposeEntry{std::move(resolveFn), std::move(assignFn),
897 std::move(specFn), std::move(dumpFn)});
949 Registrar variantTree(
const String &name, VariantTreeGet get,
950 const VariantSpec *spec =
nullptr)
const {
957 ComposeResolve resolveFn = [get](
const T &t,
const String &rest,
958 Error *err) -> Optional<Variant> {
959 Variant root = get(t);
960 if (!root.isValid()) {
961 if (err !=
nullptr) *err = Error::IdNotFound;
964 if (rest.isEmpty())
return root;
966 Variant out = promekiResolveVariantPath(root, rest, &pe);
968 if (err !=
nullptr) *err = pe;
976 IndexedCompResolve indexedResolveFn = [get](
const T &t,
size_t idx,
978 Error *err) -> Optional<Variant> {
979 Variant root = get(t);
980 if (!root.isValid()) {
981 if (err !=
nullptr) *err = Error::IdNotFound;
984 String path = String::sprintf(
"[%zu]", idx);
985 if (!rest.isEmpty()) path +=
"." + rest;
987 Variant out = promekiResolveVariantPath(root, path, &pe);
989 if (err !=
nullptr) *err = pe;
997 ComposeSpecFor specFn = [spec](
const String &rest,
998 Error *err) ->
const VariantSpec * {
999 if (spec ==
nullptr) {
1000 if (err !=
nullptr) *err = Error::IdNotFound;
1003 if (rest.isEmpty())
return spec;
1004 return detail::walkSpecPath(spec, rest, err);
1008 ComposeSpecFor indexedSpecFn = [spec](
const String &rest,
1009 Error *err) ->
const VariantSpec * {
1010 if (spec ==
nullptr) {
1011 if (err !=
nullptr) *err = Error::IdNotFound;
1015 if (!rest.isEmpty()) path +=
"." + rest;
1016 return detail::walkSpecPath(spec, path, err);
1018 Function<StringList(
const T &,
const String &)> dumpFn =
1019 [get](
const T &t,
const String &indent) -> StringList {
1021 Variant root = get(t);
1022 if (!root.isValid())
return out;
1025 s += root.typeName();
1027 s += root.format(String());
1031 Registry &r = registry();
1032 ReadWriteLock::WriteLocker lock(r.lock);
1033 uint64_t
id = r.declareName(name);
1034 r.variantTrees.insert(
id, ComposeEntry{std::move(resolveFn), ComposeAssign(),
1035 std::move(specFn), dumpFn});
1042 r.indexedVariantTrees.insert(
1043 id, IndexedCompEntry{std::move(indexedResolveFn), IndexedCompAssign(),
1044 std::move(indexedSpecFn),
1045 Function<StringList(const T &, const String &)>()});
1068 template <
typename Base> Registrar inheritsFrom()
const {
1069 static_assert(std::is_base_of_v<Base, T>,
1070 "VariantLookup::inheritsFrom<Base>(): "
1071 "Base must be a public base of T");
1072 static_assert(!std::is_same_v<Base, T>,
"VariantLookup::inheritsFrom<Base>(): "
1073 "Base must differ from T");
1074 Registry &r = registry();
1075 ReadWriteLock::WriteLocker lock(r.lock);
1076 r.inherit.resolve = [](
const T &t,
const String &key,
1077 Error *err) -> Optional<Variant> {
1078 return VariantLookup<Base>::resolveDirect(
static_cast<const Base &
>(t),
1081 r.inherit.assign = [](T &t,
const String &key,
const Variant &v,
1082 Error *err) ->
bool {
1083 return VariantLookup<Base>::assignDirect(
static_cast<Base &
>(t), key, v,
1086 r.inherit.specFor = [](
const String &key, Error *err) ->
const VariantSpec * {
1087 return VariantLookup<Base>::specForDirect(key, err);
1089 r.inherit.resolveByKey = [](
const T &t, uint64_t id,
1090 Error *err) -> Optional<Variant> {
1091 return VariantLookup<Base>::resolveKeyIdDirect(
1092 static_cast<const Base &
>(t),
id, err);
1094 r.inherit.assignByKey = [](T &t, uint64_t id,
const Variant &v,
1095 Error *err) ->
bool {
1096 return VariantLookup<Base>::assignKeyIdDirect(
static_cast<Base &
>(t),
1099 r.inherit.specForByKey = [](uint64_t id, Error *err) ->
const VariantSpec * {
1100 return VariantLookup<Base>::specForKeyIdDirect(
id, err);
1102 r.inherit.resolveIndexedByKey = [](
const T &t, uint64_t id,
size_t index,
1103 Error *err) -> Optional<Variant> {
1104 return VariantLookup<Base>::resolveIndexedKeyIdDirect(
1105 static_cast<const Base &
>(t),
id, index, err);
1107 r.inherit.assignIndexedByKey = [](T &t, uint64_t id,
size_t index,
1108 const Variant &v, Error *err) ->
bool {
1109 return VariantLookup<Base>::assignIndexedKeyIdDirect(
1110 static_cast<Base &
>(t),
id, index, v, err);
1112 r.inherit.forEachScalar = [](
const Function<void(
const String &)> &fn) {
1113 VariantLookup<Base>::forEachScalar(fn);
1115 r.inherit.registeredScalars = []() {
1116 return VariantLookup<Base>::registeredScalars();
1118 r.inherit.registeredIndexedScalars = []() {
1119 return VariantLookup<Base>::registeredIndexedScalars();
1121 r.inherit.registeredChildren = []() {
1122 return VariantLookup<Base>::registeredChildren();
1124 r.inherit.registeredIndexedChildren = []() {
1125 return VariantLookup<Base>::registeredIndexedChildren();
1127 r.inherit.registeredDatabases = []() {
1128 return VariantLookup<Base>::registeredDatabases();
1130 r.inherit.dumpComposites = [](
const T &t,
const String &indent) -> StringList {
1131 return VariantLookup<Base>::dumpComposites(
static_cast<const Base &
>(t),
1139 static Registrar registrar() {
return Registrar{}; }
1165 static Optional<Variant> resolve(
const T &instance,
const String &key, Error *err =
nullptr) {
1166 if constexpr (detail::hasVariantLookupDispatchV<T>) {
1167 return instance.variantLookupResolve(key, err);
1169 return resolveDirect(instance, key, err);
1183 static Optional<Variant> resolveDirect(
const T &instance,
const String &key,
1184 Error *err =
nullptr) {
1185 if (err !=
nullptr) *err = Error::Ok;
1186 detail::VariantLookupSegment seg;
1187 if (!detail::parseLeadingSegment(key, seg, err))
return std::nullopt;
1189 const Registry &r = registry();
1190 ReadWriteLock::ReadLocker lock(r.lock);
1191 const uint64_t
id = r.findId(seg.name);
1192 if (
id == Registry::Invalid) {
1199 if (r.inherit.resolve) {
1200 return r.inherit.resolve(instance, key, err);
1202 if (err !=
nullptr) *err = Error::IdNotFound;
1203 return std::nullopt;
1208 auto it = r.indexedScalars.find(
id);
1209 if (it != r.indexedScalars.end()) {
1210 auto v = it->second.get(instance, seg.index);
1211 if (!v.hasValue()) {
1212 if (err !=
nullptr) *err = Error::OutOfRange;
1218 auto vtIt = r.indexedVariantTrees.find(
id);
1219 if (vtIt != r.indexedVariantTrees.end()) {
1220 return vtIt->second.resolve(instance, seg.index, String(), err);
1222 if (r.inherit.resolve) {
1223 return r.inherit.resolve(instance, key, err);
1225 if (err !=
nullptr) *err = Error::IdNotFound;
1226 return std::nullopt;
1228 auto it = r.scalars.find(
id);
1229 if (it != r.scalars.end()) {
1230 auto v = it->second.get(instance);
1231 if (!v.hasValue()) {
1232 if (err !=
nullptr) *err = Error::IdNotFound;
1238 auto vtIt = r.variantTrees.find(
id);
1239 if (vtIt != r.variantTrees.end()) {
1240 return vtIt->second.resolve(instance, String(), err);
1242 if (r.inherit.resolve) {
1243 return r.inherit.resolve(instance, key, err);
1245 if (err !=
nullptr) *err = Error::IdNotFound;
1246 return std::nullopt;
1250 auto it = r.indexedChildren.find(
id);
1251 if (it != r.indexedChildren.end()) {
1252 return it->second.resolve(instance, seg.index, seg.rest, err);
1255 auto vtIt = r.indexedVariantTrees.find(
id);
1256 if (vtIt != r.indexedVariantTrees.end()) {
1257 return vtIt->second.resolve(instance, seg.index, seg.rest, err);
1259 if (r.inherit.resolve) {
1260 return r.inherit.resolve(instance, key, err);
1262 if (err !=
nullptr) *err = Error::IdNotFound;
1263 return std::nullopt;
1266 auto itChild = r.children.find(
id);
1267 if (itChild != r.children.end()) {
1268 return itChild->second.resolve(instance, seg.rest, err);
1270 auto itDb = r.databases.find(
id);
1271 if (itDb != r.databases.end()) {
1272 return itDb->second.resolve(instance, seg.rest, err);
1276 auto itVt = r.variantTrees.find(
id);
1277 if (itVt != r.variantTrees.end()) {
1278 return itVt->second.resolve(instance, seg.rest, err);
1280 if (r.inherit.resolve) {
1281 return r.inherit.resolve(instance, key, err);
1283 if (err !=
nullptr) *err = Error::IdNotFound;
1284 return std::nullopt;
1297 static Optional<Variant> resolve(
const T &instance, Key key, Error *err =
nullptr) {
1298 if constexpr (detail::hasVariantLookupDispatchV<T>) {
1304 String name = key.name();
1305 if (!name.isEmpty()) {
1306 return instance.variantLookupResolve(name, err);
1308 return resolveKeyIdDirect(instance, key.id(), err);
1310 return resolveKeyIdDirect(instance, key.id(), err);
1320 static Optional<Variant> resolve(
const T &instance, Key key,
size_t index, Error *err =
nullptr) {
1321 return resolveIndexedKeyIdDirect(instance, key.id(), index, err);
1333 static Optional<Variant> resolveKeyIdDirect(
const T &instance, uint64_t
id, Error *err =
nullptr) {
1334 if (err !=
nullptr) *err = Error::Ok;
1335 const Registry &r = registry();
1336 ReadWriteLock::ReadLocker lock(r.lock);
1337 auto it = r.scalars.find(
id);
1338 if (it == r.scalars.end()) {
1339 if (r.inherit.resolveByKey) {
1340 return r.inherit.resolveByKey(instance,
id, err);
1342 if (err !=
nullptr) *err = Error::IdNotFound;
1343 return std::nullopt;
1345 auto v = it->second.get(instance);
1346 if (!v.hasValue() && err !=
nullptr) *err = Error::IdNotFound;
1355 static Optional<Variant> resolveIndexedKeyIdDirect(
const T &instance, uint64_t
id,
size_t index,
1356 Error *err =
nullptr) {
1357 if (err !=
nullptr) *err = Error::Ok;
1358 const Registry &r = registry();
1359 ReadWriteLock::ReadLocker lock(r.lock);
1360 auto it = r.indexedScalars.find(
id);
1361 if (it == r.indexedScalars.end()) {
1362 if (r.inherit.resolveIndexedByKey) {
1363 return r.inherit.resolveIndexedByKey(instance,
id, index, err);
1365 if (err !=
nullptr) *err = Error::IdNotFound;
1366 return std::nullopt;
1368 auto v = it->second.get(instance, index);
1369 if (!v.hasValue() && err !=
nullptr) *err = Error::OutOfRange;
1389 static bool assign(T &instance,
const String &key,
const Variant &value, Error *err =
nullptr) {
1390 if constexpr (detail::hasVariantLookupDispatchV<T>) {
1391 return instance.variantLookupAssign(key, value, err);
1393 return assignDirect(instance, key, value, err);
1405 static bool assignDirect(T &instance,
const String &key,
const Variant &value, Error *err =
nullptr) {
1406 if (err !=
nullptr) *err = Error::Ok;
1407 detail::VariantLookupSegment seg;
1408 if (!detail::parseLeadingSegment(key, seg, err))
return false;
1410 const Registry &r = registry();
1411 ReadWriteLock::ReadLocker lock(r.lock);
1412 const uint64_t
id = r.findId(seg.name);
1413 if (
id == Registry::Invalid) {
1414 if (r.inherit.assign) {
1415 return r.inherit.assign(instance, key, value, err);
1417 if (err !=
nullptr) *err = Error::IdNotFound;
1423 auto it = r.indexedScalars.find(
id);
1424 if (it != r.indexedScalars.end()) {
1425 if (!it->second.set) {
1426 if (err !=
nullptr) *err = Error::ReadOnly;
1429 Error setErr = it->second.set(instance, seg.index, value);
1430 if (setErr.isError()) {
1431 if (err !=
nullptr) *err = setErr;
1439 if (r.indexedVariantTrees.find(
id) != r.indexedVariantTrees.end()) {
1440 if (err !=
nullptr) *err = Error::ReadOnly;
1443 if (r.inherit.assign) {
1444 return r.inherit.assign(instance, key, value, err);
1446 if (err !=
nullptr) *err = Error::IdNotFound;
1449 auto it = r.scalars.find(
id);
1450 if (it != r.scalars.end()) {
1451 if (!it->second.set) {
1452 if (err !=
nullptr) *err = Error::ReadOnly;
1455 Error setErr = it->second.set(instance, value);
1456 if (setErr.isError()) {
1457 if (err !=
nullptr) *err = setErr;
1462 if (r.variantTrees.find(
id) != r.variantTrees.end()) {
1463 if (err !=
nullptr) *err = Error::ReadOnly;
1466 if (r.inherit.assign) {
1467 return r.inherit.assign(instance, key, value, err);
1469 if (err !=
nullptr) *err = Error::IdNotFound;
1474 auto it = r.indexedChildren.find(
id);
1475 if (it != r.indexedChildren.end()) {
1476 if (!it->second.assign) {
1477 if (err !=
nullptr) *err = Error::ReadOnly;
1480 return it->second.assign(instance, seg.index, seg.rest, value, err);
1482 if (r.indexedVariantTrees.find(
id) != r.indexedVariantTrees.end()) {
1483 if (err !=
nullptr) *err = Error::ReadOnly;
1486 if (r.inherit.assign) {
1487 return r.inherit.assign(instance, key, value, err);
1489 if (err !=
nullptr) *err = Error::IdNotFound;
1493 auto itChild = r.children.find(
id);
1494 if (itChild != r.children.end()) {
1495 if (!itChild->second.assign) {
1496 if (err !=
nullptr) *err = Error::ReadOnly;
1499 return itChild->second.assign(instance, seg.rest, value, err);
1501 auto itDb = r.databases.find(
id);
1502 if (itDb != r.databases.end()) {
1503 if (!itDb->second.assign) {
1504 if (err !=
nullptr) *err = Error::ReadOnly;
1507 return itDb->second.assign(instance, seg.rest, value, err);
1509 if (r.variantTrees.find(
id) != r.variantTrees.end()) {
1510 if (err !=
nullptr) *err = Error::ReadOnly;
1513 if (r.inherit.assign) {
1514 return r.inherit.assign(instance, key, value, err);
1516 if (err !=
nullptr) *err = Error::IdNotFound;
1523 static bool assign(T &instance, Key key,
const Variant &value, Error *err =
nullptr) {
1524 if constexpr (detail::hasVariantLookupDispatchV<T>) {
1525 String name = key.name();
1526 if (!name.isEmpty()) {
1527 return instance.variantLookupAssign(name, value, err);
1529 return assignKeyIdDirect(instance, key.id(), value, err);
1531 return assignKeyIdDirect(instance, key.id(), value, err);
1538 static bool assign(T &instance, Key key,
size_t index,
const Variant &value, Error *err =
nullptr) {
1539 return assignIndexedKeyIdDirect(instance, key.id(), index, value, err);
1546 static bool assignKeyIdDirect(T &instance, uint64_t
id,
const Variant &value, Error *err =
nullptr) {
1547 if (err !=
nullptr) *err = Error::Ok;
1548 const Registry &r = registry();
1549 ReadWriteLock::ReadLocker lock(r.lock);
1550 auto it = r.scalars.find(
id);
1551 if (it == r.scalars.end()) {
1552 if (r.inherit.assignByKey) {
1553 return r.inherit.assignByKey(instance,
id, value, err);
1555 if (err !=
nullptr) *err = Error::IdNotFound;
1558 if (!it->second.set) {
1559 if (err !=
nullptr) *err = Error::ReadOnly;
1562 Error setErr = it->second.set(instance, value);
1563 if (setErr.isError()) {
1564 if (err !=
nullptr) *err = setErr;
1575 static bool assignIndexedKeyIdDirect(T &instance, uint64_t
id,
size_t index,
const Variant &value,
1576 Error *err =
nullptr) {
1577 if (err !=
nullptr) *err = Error::Ok;
1578 const Registry &r = registry();
1579 ReadWriteLock::ReadLocker lock(r.lock);
1580 auto it = r.indexedScalars.find(
id);
1581 if (it == r.indexedScalars.end()) {
1582 if (r.inherit.assignIndexedByKey) {
1583 return r.inherit.assignIndexedByKey(instance,
id, index, value, err);
1585 if (err !=
nullptr) *err = Error::IdNotFound;
1588 if (!it->second.set) {
1589 if (err !=
nullptr) *err = Error::ReadOnly;
1592 Error setErr = it->second.set(instance, index, value);
1593 if (setErr.isError()) {
1594 if (err !=
nullptr) *err = setErr;
1626 static const VariantSpec *specFor(
const String &key, Error *err =
nullptr) {
1627 if constexpr (detail::hasVariantLookupDispatchV<T>) {
1641 return specForDirect(key, err);
1643 return specForDirect(key, err);
1650 static const VariantSpec *specForDirect(
const String &key, Error *err =
nullptr) {
1651 if (err !=
nullptr) *err = Error::Ok;
1652 detail::VariantLookupSegment seg;
1653 if (!detail::parseLeadingSegment(key, seg, err))
return nullptr;
1655 const Registry &r = registry();
1656 ReadWriteLock::ReadLocker lock(r.lock);
1657 const uint64_t
id = r.findId(seg.name);
1658 if (
id == Registry::Invalid) {
1659 if (r.inherit.specFor) {
1660 return r.inherit.specFor(key, err);
1662 if (err !=
nullptr) *err = Error::IdNotFound;
1668 auto it = r.indexedScalars.find(
id);
1669 if (it != r.indexedScalars.end())
return it->second.spec;
1674 auto vtIt = r.indexedVariantTrees.find(
id);
1675 if (vtIt != r.indexedVariantTrees.end()) {
1676 return vtIt->second.specFor(String(), err);
1678 if (r.inherit.specFor) {
1679 return r.inherit.specFor(key, err);
1681 if (err !=
nullptr) *err = Error::IdNotFound;
1684 auto it = r.scalars.find(
id);
1685 if (it != r.scalars.end())
return it->second.spec;
1688 auto vtIt = r.variantTrees.find(
id);
1689 if (vtIt != r.variantTrees.end()) {
1690 return vtIt->second.specFor(String(), err);
1692 if (r.inherit.specFor) {
1693 return r.inherit.specFor(key, err);
1695 if (err !=
nullptr) *err = Error::IdNotFound;
1700 auto it = r.indexedChildren.find(
id);
1701 if (it != r.indexedChildren.end()) {
1702 return it->second.specFor(seg.rest, err);
1704 auto vtIt = r.indexedVariantTrees.find(
id);
1705 if (vtIt != r.indexedVariantTrees.end()) {
1706 return vtIt->second.specFor(seg.rest, err);
1708 if (r.inherit.specFor) {
1709 return r.inherit.specFor(key, err);
1711 if (err !=
nullptr) *err = Error::IdNotFound;
1715 auto itChild = r.children.find(
id);
1716 if (itChild != r.children.end()) {
1717 return itChild->second.specFor(seg.rest, err);
1719 auto itDb = r.databases.find(
id);
1720 if (itDb != r.databases.end()) {
1721 return itDb->second.specFor(seg.rest, err);
1723 auto itVt = r.variantTrees.find(
id);
1724 if (itVt != r.variantTrees.end()) {
1725 return itVt->second.specFor(seg.rest, err);
1727 if (r.inherit.specFor) {
1728 return r.inherit.specFor(key, err);
1730 if (err !=
nullptr) *err = Error::IdNotFound;
1743 static const VariantSpec *specFor(Key key, Error *err =
nullptr) {
1744 return specForKeyIdDirect(key.id(), err);
1751 static const VariantSpec *specForKeyIdDirect(uint64_t
id, Error *err =
nullptr) {
1752 if (err !=
nullptr) *err = Error::Ok;
1753 const Registry &r = registry();
1754 ReadWriteLock::ReadLocker lock(r.lock);
1755 auto it = r.scalars.find(
id);
1756 if (it != r.scalars.end())
return it->second.spec;
1757 auto itIdx = r.indexedScalars.find(
id);
1758 if (itIdx != r.indexedScalars.end())
return itIdx->second.spec;
1759 if (r.inherit.specForByKey) {
1760 return r.inherit.specForByKey(
id, err);
1762 if (err !=
nullptr) *err = Error::IdNotFound;
1777 static StringList registeredScalars() {
1778 return mergeWithInherited(collectNames(registry().scalars),
1779 registry().inherit.registeredScalars);
1783 static StringList registeredIndexedScalars() {
1784 return mergeWithInherited(collectNames(registry().indexedScalars),
1785 registry().inherit.registeredIndexedScalars);
1789 static StringList registeredChildren() {
1790 return mergeWithInherited(collectNames(registry().children),
1791 registry().inherit.registeredChildren);
1795 static StringList registeredIndexedChildren() {
1796 return mergeWithInherited(collectNames(registry().indexedChildren),
1797 registry().inherit.registeredIndexedChildren);
1801 static StringList registeredDatabases() {
1802 return mergeWithInherited(collectNames(registry().databases),
1803 registry().inherit.registeredDatabases);
1823 template <
typename Fn>
static void forEachScalar(Fn &&fn) {
1824 const Registry &r = registry();
1826 Function<void(
const Function<
void(
const String &)> &)> inheritForEach;
1828 ReadWriteLock::ReadLocker lock(r.lock);
1829 for (
auto it = r.scalars.cbegin(); it != r.scalars.cend(); ++it) {
1830 auto n = r.names.find(it->first);
1831 if (n != r.names.end()) names.pushToBack(n->second);
1833 inheritForEach = r.inherit.forEachScalar;
1839 if (inheritForEach) {
1840 inheritForEach([&names](
const String &n) { names.pushToBack(n); });
1842 names = names.sort();
1847 for (
const String &name : names) {
1848 if (!prev.isEmpty() && name == prev)
continue;
1883 static StringList dump(
const T &instance,
const String &indent = String()) {
1895 forEachScalar([&out, &instance, &indent](
const String &name) {
1896 auto v = VariantLookup<T>::resolve(instance, name);
1898 out.pushToBack(indent + name +
" [" + v->typeName() +
1899 "]: " + v->format(String()));
1906 StringList composites = dumpComposites(instance, indent);
1907 for (
const String &l : composites) out.pushToBack(l);
1920 static StringList dumpComposites(
const T &instance,
const String &indent = String()) {
1931 Function<StringList(
const T &,
const String &)> dump;
1933 List<Item> children;
1934 List<Item> indexedChildren;
1935 List<Item> databases;
1936 List<Item> variantTrees;
1937 Function<StringList(
const T &,
const String &)> inheritFn;
1939 const Registry &r = registry();
1940 ReadWriteLock::ReadLocker lock(r.lock);
1941 for (
auto it = r.children.cbegin(); it != r.children.cend(); ++it) {
1942 if (!it->second.dump)
continue;
1943 auto n = r.names.find(it->first);
1944 if (n == r.names.end())
continue;
1945 children.pushToBack(Item{n->second, it->second.dump});
1947 for (
auto it = r.indexedChildren.cbegin(); it != r.indexedChildren.cend(); ++it) {
1948 if (!it->second.dump)
continue;
1949 auto n = r.names.find(it->first);
1950 if (n == r.names.end())
continue;
1951 indexedChildren.pushToBack(Item{n->second, it->second.dump});
1953 for (
auto it = r.databases.cbegin(); it != r.databases.cend(); ++it) {
1954 if (!it->second.dump)
continue;
1955 auto n = r.names.find(it->first);
1956 if (n == r.names.end())
continue;
1957 databases.pushToBack(Item{n->second, it->second.dump});
1966 for (
auto it = r.variantTrees.cbegin(); it != r.variantTrees.cend(); ++it) {
1967 if (!it->second.dump)
continue;
1968 auto n = r.names.find(it->first);
1969 if (n == r.names.end())
continue;
1970 variantTrees.pushToBack(Item{n->second, it->second.dump});
1972 inheritFn = r.inherit.dumpComposites;
1975 const String sub = indent +
" ";
1977 auto emit = [&out, &indent, &sub](
const String &name,
const StringList &lines) {
1978 if (lines.isEmpty())
return;
1979 out.pushToBack(indent + name +
":");
1980 for (
const String &l : lines) out.pushToBack(l);
1983 for (
const Item &c : children) {
1984 emit(c.name, c.dump(instance, sub));
1986 for (
const Item &c : indexedChildren) {
1987 emit(c.name, c.dump(instance, sub));
1989 for (
const Item &d : databases) {
1990 emit(d.name, d.dump(instance, sub));
1992 for (
const Item &d : variantTrees) {
1993 emit(d.name, d.dump(instance, sub));
1999 StringList inherited = inheritFn(instance, indent);
2000 for (
const String &l : inherited) out.pushToBack(l);
2039 template <
typename Resolver>
2040 static String format(
const T &instance,
const String &tmpl, Resolver &&resolver, Error *err =
nullptr) {
2041 if (err !=
nullptr) *err = Error::Ok;
2043 out.reserve(tmpl.byteCount());
2044 const char *src = tmpl.cstr();
2045 const size_t len = tmpl.byteCount();
2046 bool sawUnresolved =
false;
2051 if (i + 1 < len && src[i + 1] ==
'{') {
2057 while (end < len && src[end] !=
'}') ++end;
2059 out += String(src + i, len - i);
2062 const char *bodyData = src + i + 1;
2063 const size_t bodyLen = end - (i + 1);
2064 size_t colon = bodyLen;
2065 for (
size_t p = 0; p < bodyLen; ++p) {
2066 if (bodyData[p] ==
':') {
2071 const size_t keyLen = colon;
2072 const size_t specOff = (colon == bodyLen) ? bodyLen : colon + 1;
2073 String keyName(bodyData, keyLen);
2074 String specStr(bodyData + specOff, bodyLen - specOff);
2075 auto v = resolve(instance, keyName);
2077 out += v->format(specStr);
2079 Optional<String> resolved;
2080 if constexpr (!std::is_same_v<std::decay_t<Resolver>, std::nullptr_t>) {
2081 resolved = resolver(keyName, specStr);
2083 if (resolved.hasValue()) {
2086 sawUnresolved =
true;
2088 out += String(bodyData, keyLen);
2093 }
else if (c ==
'}') {
2094 if (i + 1 < len && src[i + 1] ==
'}') {
2106 if (sawUnresolved && err !=
nullptr) *err = Error::IdNotFound;
2117 static String format(
const T &instance,
const String &tmpl, Error *err =
nullptr) {
2118 return format(instance, tmpl,
nullptr, err);
2122 struct ScalarEntry {
2125 const VariantSpec *spec =
nullptr;
2127 struct IndexedScalarEntry {
2128 IndexedScalarGet get;
2129 IndexedScalarSet set;
2130 const VariantSpec *spec =
nullptr;
2132 struct ComposeEntry {
2133 ComposeResolve resolve;
2134 ComposeAssign assign;
2135 ComposeSpecFor specFor;
2149 Function<StringList(
const T &,
const String &)> dump;
2151 struct IndexedCompEntry {
2152 IndexedCompResolve resolve;
2153 IndexedCompAssign assign;
2154 ComposeSpecFor specFor;
2166 Function<StringList(
const T &,
const String &)> dump;
2179 struct InheritEntry {
2180 Function<Optional<Variant>(
const T &,
const String &, Error *)> resolve;
2181 Function<bool(T &,
const String &,
const Variant &, Error *)> assign;
2182 Function<
const VariantSpec *(
const String &, Error *)> specFor;
2183 Function<Optional<Variant>(
const T &, uint64_t, Error *)> resolveByKey;
2184 Function<bool(T &, uint64_t,
const Variant &, Error *)> assignByKey;
2185 Function<
const VariantSpec *(uint64_t, Error *)> specForByKey;
2186 Function<Optional<Variant>(
const T &, uint64_t,
size_t, Error *)>
2187 resolveIndexedByKey;
2188 Function<bool(T &, uint64_t,
size_t,
const Variant &, Error *)> assignIndexedByKey;
2189 Function<void(
const Function<
void(
const String &)> &)> forEachScalar;
2190 Function<StringList()> registeredScalars;
2191 Function<StringList()> registeredIndexedScalars;
2192 Function<StringList()> registeredChildren;
2193 Function<StringList()> registeredIndexedChildren;
2194 Function<StringList()> registeredDatabases;
2205 Function<StringList(
const T &,
const String &)> dumpComposites;
2221 static constexpr uint64_t Invalid = UINT64_MAX;
2223 Map<uint64_t, String> names;
2224 Map<uint64_t, ScalarEntry> scalars;
2225 Map<uint64_t, IndexedScalarEntry> indexedScalars;
2226 Map<uint64_t, ComposeEntry> children;
2227 Map<uint64_t, IndexedCompEntry> indexedChildren;
2228 Map<uint64_t, ComposeEntry> databases;
2229 Map<uint64_t, ComposeEntry> variantTrees;
2230 Map<uint64_t, IndexedCompEntry> indexedVariantTrees;
2231 InheritEntry inherit;
2232 mutable ReadWriteLock lock;
2245 uint64_t declareName(
const String &name) {
2246 const uint64_t h = fnv1a(name.cstr());
2247 auto it = names.find(h);
2248 if (it != names.end()) {
2249 if (it->second == name)
return h;
2250 detail::stringRegistryCollisionAbort(
typeid(T).name(), it->second, name,
2253 names.insert(h, name);
2265 uint64_t findId(
const String &name)
const {
2266 const uint64_t h = fnv1a(name.cstr());
2267 auto it = names.find(h);
2268 if (it == names.end())
return Invalid;
2269 if (it->second != name)
return Invalid;
2274 static Registry ®istry() {
2288 template <
typename MapT>
static StringList collectNames(
const MapT &m) {
2289 const Registry &r = registry();
2290 ReadWriteLock::ReadLocker lock(r.lock);
2292 for (
auto it = m.cbegin(); it != m.cend(); ++it) {
2293 auto n = r.names.find(it->first);
2294 if (n != r.names.end()) out.pushToBack(n->second);
2308 static StringList mergeWithInherited(StringList ownNames,
2309 const Function<StringList()> &inheritFn) {
2310 if (!inheritFn)
return ownNames;
2311 StringList inherited = inheritFn();
2312 for (
const String &n : inherited) ownNames.pushToBack(n);
2313 ownNames = ownNames.sort();
2316 for (
const String &n : ownNames) {
2317 if (!prev.isEmpty() && n == prev)
continue;
2325PROMEKI_NAMESPACE_END
2353#define PROMEKI_LOOKUP_REGISTER(Type) \
2354 [[maybe_unused]] static inline const ::promeki::VariantLookup<Type>::Registrar PROMEKI_CONCAT( \
2355 _promeki_lookup_reg_, __COUNTER__) = ::promeki::VariantLookup<Type>::registrar()