Skip to content

Commit

Permalink
Support assigning nested collections via templated API
Browse files Browse the repository at this point in the history
  • Loading branch information
jedelbo committed Mar 14, 2024
1 parent ca1eeb6 commit 9eaf6c3
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 48 deletions.
2 changes: 1 addition & 1 deletion src/realm/collection_parent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ CollectionParent::~CollectionParent() {}

void CollectionParent::check_level() const
{
if (m_level + 1 > s_max_level) {
if (size_t(m_level) + 1 > s_max_level) {
throw LogicError(ErrorCodes::LimitExceeded, "Max nesting level reached");
}
}
Expand Down
49 changes: 36 additions & 13 deletions src/realm/object-store/dictionary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ class Dictionary : public object_store::Collection {
Obj get_object(StringData key) const;
};

} // namespace object_store
} // namespace realm

#include <realm/object-store/list.hpp>

namespace realm::object_store {

template <typename Fn>
auto Dictionary::dispatch(Fn&& fn) const
Expand Down Expand Up @@ -191,9 +197,27 @@ void Dictionary::insert(Context& ctx, StringData key, T&& value, CreatePolicy po
ctx.template unbox<Obj>(value, policy, obj_key);
return;
}
dispatch([&](auto t) {
this->insert(key, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
if (m_type == PropertyType::Mixed) {
Mixed new_val = ctx.template unbox<Mixed>(value, policy);
if (new_val.is_type(type_Dictionary)) {
insert_collection(key, CollectionType::Dictionary);
auto dict = get_dictionary(key);
dict.assign(ctx, value, policy);
return;
}
if (new_val.is_type(type_List)) {
insert_collection(key, CollectionType::List);
auto list = get_list(key);
list.assign(ctx, value, policy);
return;
}
this->insert(key, new_val);
}
else {
dispatch([&](auto t) {
this->insert(key, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
}
}

template <typename Context>
Expand All @@ -220,20 +244,19 @@ void Dictionary::assign(Context& ctx, T&& values, CreatePolicy policy)

ctx.enumerate_dictionary(values, [&](StringData key, auto&& value) {
if (policy.diff) {
util::Optional<Mixed> old_value = dict().try_get(key);
auto new_value = ctx.template unbox<Mixed>(value);
if (!old_value || *old_value != new_value) {
dict().insert(key, new_value);
Mixed new_value = ctx.template unbox<Mixed>(value);
if (!new_value.is_type(type_Dictionary, type_List)) {
util::Optional<Mixed> old_value = dict().try_get(key);
if (!old_value || *old_value != new_value) {
dict().insert(key, new_value);
}
return;
}
}
else {
this->insert(ctx, key, value, policy);
}
this->insert(ctx, key, value, policy);
});
}

} // namespace object_store
} // namespace realm

} // namespace realm::object_store

#endif /* REALM_OS_DICTIONARY_HPP */
6 changes: 6 additions & 0 deletions src/realm/object-store/impl/object_accessor_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,12 @@ inline Mixed CppContext::unbox(std::any& v, CreatePolicy, ObjKey) const
else if (this_type == typeid(UUID)) {
return Mixed(util::any_cast<UUID>(v));
}
else if (this_type == typeid(AnyDict)) {
return Mixed(0, CollectionType::Dictionary);
}
else if (this_type == typeid(AnyVector)) {
return Mixed(0, CollectionType::List);
}
}
return Mixed{};
}
Expand Down
126 changes: 92 additions & 34 deletions src/realm/object-store/list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@ class List : public object_store::Collection {

friend struct std::hash<List>;
};
} // namespace realm

#include <realm/object-store/dictionary.hpp>

namespace realm {
template <>
Obj List::get(size_t row_ndx) const;

Expand Down Expand Up @@ -183,15 +187,7 @@ size_t List::find(Context& ctx, T&& value) const
template <typename T, typename Context>
void List::add(Context& ctx, T&& value, CreatePolicy policy)
{
if (m_is_embedded) {
validate_embedded(ctx, value, policy);
auto key = as<Obj>().create_and_insert_linked_object(size()).get_key();
ctx.template unbox<Obj>(value, policy, key);
return;
}
dispatch([&](auto t) {
this->add(ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
this->insert(ctx, size(), std::move(value), policy);
}

template <typename T, typename Context>
Expand All @@ -203,9 +199,28 @@ void List::insert(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
ctx.template unbox<Obj>(value, policy, key);
return;
}
dispatch([&](auto t) {
this->insert(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
if (m_type == PropertyType::Mixed) {
Mixed new_val = ctx.template unbox<Mixed>(value, policy);
if (new_val.is_type(type_Dictionary)) {
insert_collection(list_ndx, CollectionType::Dictionary);
auto dict = get_dictionary(list_ndx);
dict.assign(ctx, value, policy);
return;
}
if (new_val.is_type(type_List)) {
insert_collection(list_ndx, CollectionType::List);
auto list = get_list(list_ndx);
list.assign(ctx, value, policy);
return;
}

this->insert(list_ndx, new_val);
}
else {
dispatch([&](auto t) {
this->insert(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
}
}

template <typename T, typename Context>
Expand All @@ -219,35 +234,75 @@ void List::set(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
ctx.template unbox<Obj>(value, policy, key);
return;
}
dispatch([&](auto t) {
this->set(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
if (m_type == PropertyType::Mixed) {
Mixed new_val = ctx.template unbox<Mixed>(value, policy);
if (new_val.is_type(type_Dictionary)) {
set_collection(list_ndx, CollectionType::Dictionary);
auto dict = get_dictionary(list_ndx);
dict.assign(ctx, value, policy);
return;
}
if (new_val.is_type(type_List)) {
set_collection(list_ndx, CollectionType::List);
auto list = get_list(list_ndx);
list.assign(ctx, value, policy);
return;
}

this->set(list_ndx, new_val);
}
else {
dispatch([&](auto t) {
this->set(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
}
}

template <typename T, typename Context>
void List::set_if_different(Context& ctx, size_t row_ndx, T&& value, CreatePolicy policy)
void List::set_if_different(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
{
if (m_is_embedded) {
validate_embedded(ctx, value, policy);
auto key = policy.diff ? this->get<Obj>(row_ndx) : as<Obj>().create_and_set_linked_object(row_ndx);
auto key = policy.diff ? this->get<Obj>(list_ndx) : as<Obj>().create_and_set_linked_object(list_ndx);
ctx.template unbox<Obj>(value, policy, key.get_key());
return;
}
dispatch([&](auto t) {
using U = std::decay_t<decltype(*t)>;
if constexpr (std::is_same_v<U, Obj>) {
auto old_value = this->get<U>(row_ndx);
auto new_value = ctx.template unbox<U>(value, policy, old_value.get_key());
if (new_value.get_key() != old_value.get_key())
this->set(row_ndx, new_value);
if (m_type == PropertyType::Mixed) {
Mixed new_val = ctx.template unbox<Mixed>(value, policy);
if (new_val.is_type(type_Dictionary)) {
set_collection(list_ndx, CollectionType::Dictionary);
auto dict = get_dictionary(list_ndx);
dict.assign(ctx, value, policy);
return;
}
else {
auto old_value = this->get<U>(row_ndx);
auto new_value = ctx.template unbox<U>(value, policy);
if (old_value != new_value)
this->set(row_ndx, new_value);
if (new_val.is_type(type_List)) {
set_collection(list_ndx, CollectionType::List);
auto list = get_list(list_ndx);
list.assign(ctx, value, policy);
return;
}
});
Mixed old_value = this->get<Mixed>(list_ndx);
Mixed new_value = ctx.template unbox<Mixed>(value, policy);
if (old_value != new_value)
this->set(list_ndx, new_value);
}
else {
dispatch([&](auto t) {
using U = std::decay_t<decltype(*t)>;
if constexpr (std::is_same_v<U, Obj>) {
auto old_value = this->get<U>(list_ndx);
auto new_value = ctx.template unbox<U>(value, policy, old_value.get_key());
if (new_value.get_key() != old_value.get_key())
this->set(list_ndx, new_value);
}
else {
auto old_value = this->get<U>(list_ndx);
auto new_value = ctx.template unbox<U>(value, policy);
if (old_value != new_value)
this->set(list_ndx, new_value);
}
});
}
}

template <typename T, typename Context>
Expand All @@ -267,12 +322,15 @@ void List::assign(Context& ctx, T&& values, CreatePolicy policy)
size_t sz = size();
size_t index = 0;
ctx.enumerate_collection(values, [&](auto&& element) {
if (index >= sz)
if (index >= sz) {
this->add(ctx, element, policy);
else if (policy.diff)
}
else {
// If index is within legal range, policy.diff must be true -
// otherwise the list would have been cleared
REALM_ASSERT(policy.diff);
this->set_if_different(ctx, index, element, policy);
else
this->set(ctx, index, element, policy);
}
index++;
});
while (index < sz)
Expand Down
20 changes: 20 additions & 0 deletions src/realm/object-store/object_accessor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,26 @@ void Object::set_property_value_impl(ContextType& ctx, const Property& property,
return;
}

if (property.type == PropertyType::Mixed) {
Mixed new_val = ctx.template unbox<Mixed>(value, policy);
if (new_val.is_type(type_Dictionary)) {
ContextType child_ctx(ctx, m_obj, property);
m_obj.set_collection(col, CollectionType::Dictionary);
object_store::Dictionary dict(m_realm, m_obj, col);
dict.assign(child_ctx, value, policy);
ctx.did_change();
return;
}
if (new_val.is_type(type_List)) {
ContextType child_ctx(ctx, m_obj, property);
m_obj.set_collection(col, CollectionType::List);
List list(m_realm, m_obj, col);
list.assign(child_ctx, value, policy);
ctx.did_change();
return;
}
}

ValueUpdater<ValueType, ContextType> updater{ctx, property, value, m_obj, col, policy, is_default};
switch_on_type(property.type, updater);
ctx.did_change();
Expand Down
Loading

0 comments on commit 9eaf6c3

Please sign in to comment.