From 552ddd11d8a568e4d40be11ea88bbd0534f5e2d6 Mon Sep 17 00:00:00 2001 From: Lee Maguire Date: Fri, 23 Feb 2024 13:18:56 +0100 Subject: [PATCH] RCPP-45 object lifetime issues, add more restrictions on how managed properties can be instantiated (#163) --- CHANGELOG.md | 24 ++ src/cpprealm/internal/bridge/list.cpp | 4 + src/cpprealm/internal/bridge/list.hpp | 1 + src/cpprealm/internal/bridge/obj_key.cpp | 17 ++ src/cpprealm/internal/bridge/obj_key.hpp | 6 +- .../internal/bridge/object_schema.cpp | 3 + .../internal/bridge/object_schema.hpp | 1 + src/cpprealm/internal/bridge/table.cpp | 8 + src/cpprealm/internal/bridge/table.hpp | 2 + src/cpprealm/link.hpp | 28 +- src/cpprealm/macros.hpp | 143 +++++++++- src/cpprealm/managed_binary.hpp | 18 ++ src/cpprealm/managed_decimal.hpp | 18 ++ src/cpprealm/managed_dictionary.hpp | 158 +---------- src/cpprealm/managed_list.hpp | 35 ++- src/cpprealm/managed_mixed.hpp | 154 ++++++++++ src/cpprealm/managed_numeric.hpp | 64 ++++- src/cpprealm/managed_objectid.hpp | 18 ++ src/cpprealm/managed_primary_key.hpp | 107 ++++++- src/cpprealm/managed_set.hpp | 23 +- src/cpprealm/managed_string.cpp | 8 - src/cpprealm/managed_string.hpp | 35 ++- src/cpprealm/managed_timestamp.hpp | 20 ++ src/cpprealm/managed_uuid.hpp | 20 ++ src/cpprealm/results.hpp | 265 +++++++++++++----- tests/db/list_tests.cpp | 96 +++++++ tests/db/map_tests.cpp | 29 +- tests/db/mixed_tests.cpp | 113 ++++++++ tests/db/object_tests.cpp | 80 +++++- tests/db/results_tests.cpp | 71 +++-- tests/db/set_tests.cpp | 47 +++- tests/db/test_objects.hpp | 10 +- 32 files changed, 1307 insertions(+), 319 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d47f56d8..35793b30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +X.Y.Z Release notes (YYYY-MM-DD) +============================================================= + +### Fixed +* Managed objects would exhibit undefined behaviour when returned from the subscript operator in `std::vector` & `std::map`. + +### Enhancements +* Add `realm::holds_alternative` which acts as a substitute to `std::holds_alternative` when using `managed`. +* Add `managed::get_stored_link` for retrieving a link from a mixed proeprty type. +* Add `managed::set_link` for setting a link in a mixed proeprty type. +* Add compile time checking to prevent misuse of managed property types. +* Add `managed>::as_results()` to allow the ability to derive a `realm::results<>` collection from a managed vector. + +### Breaking Changes +* None + +### Compatibility +* Fileformat: Generates files with format v23. Reads and automatically upgrade from fileformat v5. + +### Internals +* None + +---------------------------------------------- + 1.0.0 Release notes (2024-01-08) ============================================================= diff --git a/src/cpprealm/internal/bridge/list.cpp b/src/cpprealm/internal/bridge/list.cpp index 5841d0de..8008213a 100644 --- a/src/cpprealm/internal/bridge/list.cpp +++ b/src/cpprealm/internal/bridge/list.cpp @@ -130,6 +130,10 @@ namespace realm::internal::bridge { return get_list()->sort(results_descriptors); } + [[nodiscard]] results list::as_results() const { + return get_list()->as_results(); + } + table list::get_table() const { return get_list()->get_table(); } diff --git a/src/cpprealm/internal/bridge/list.hpp b/src/cpprealm/internal/bridge/list.hpp index 7ccb34ce..3193063f 100644 --- a/src/cpprealm/internal/bridge/list.hpp +++ b/src/cpprealm/internal/bridge/list.hpp @@ -100,6 +100,7 @@ namespace realm::internal::bridge { size_t find(const obj_key&); results sort(const std::vector&); + [[nodiscard]] results as_results() const; notification_token add_notification_callback(std::shared_ptr); private: diff --git a/src/cpprealm/internal/bridge/obj_key.cpp b/src/cpprealm/internal/bridge/obj_key.cpp index 2216ad61..9610e828 100644 --- a/src/cpprealm/internal/bridge/obj_key.cpp +++ b/src/cpprealm/internal/bridge/obj_key.cpp @@ -44,6 +44,15 @@ namespace realm::internal::bridge { #endif } + obj_link::obj_link(uint32_t table_key, obj_key obj_key) { +#ifdef CPPREALM_HAVE_GENERATED_BRIDGE_TYPES + new (&m_obj_link) ObjLink(TableKey(table_key), obj_key); +#else + m_obj_link = std::make_shared(TableKey(table_key), obj_key); +#endif + } + + obj_link& obj_link::operator=(const obj_link& other) { #ifdef CPPREALM_HAVE_GENERATED_BRIDGE_TYPES if (this != &other) { @@ -104,6 +113,14 @@ namespace realm::internal::bridge { #endif } + uint32_t obj_link::get_table_key() { +#ifdef CPPREALM_HAVE_GENERATED_BRIDGE_TYPES + return reinterpret_cast(&m_obj_link)->get_table_key().value; +#else + return m_obj_link->get_table_key().value; +#endif + } + bool operator==(obj_link const& lhs, obj_link const& rhs) { return static_cast(lhs) == static_cast(rhs); } diff --git a/src/cpprealm/internal/bridge/obj_key.hpp b/src/cpprealm/internal/bridge/obj_key.hpp index dc629414..06af6b6e 100644 --- a/src/cpprealm/internal/bridge/obj_key.hpp +++ b/src/cpprealm/internal/bridge/obj_key.hpp @@ -53,13 +53,15 @@ namespace realm::internal::bridge { struct obj_link { obj_link(const ObjLink&); obj_link(); - obj_link(const obj_link& other) ; - obj_link& operator=(const obj_link& other) ; + obj_link(const obj_link& other); + obj_link(uint32_t table_key, obj_key obj_key); + obj_link& operator=(const obj_link& other); obj_link(obj_link&& other); obj_link& operator=(obj_link&& other); ~obj_link(); operator ObjLink() const; obj_key get_obj_key(); + uint32_t get_table_key(); private: #ifdef CPPREALM_HAVE_GENERATED_BRIDGE_TYPES storage::ObjLink m_obj_link[1]; diff --git a/src/cpprealm/internal/bridge/object_schema.cpp b/src/cpprealm/internal/bridge/object_schema.cpp index 9f8619e9..099a65b5 100644 --- a/src/cpprealm/internal/bridge/object_schema.cpp +++ b/src/cpprealm/internal/bridge/object_schema.cpp @@ -117,6 +117,9 @@ namespace realm::internal::bridge { void object_schema::set_name(const std::string &name) { get_object_schema()->name = name; } + std::string object_schema::get_name() const { + return get_object_schema()->name; + } property object_schema::property_for_name(const std::string &v) { return *get_object_schema()->property_for_name(v); } diff --git a/src/cpprealm/internal/bridge/object_schema.hpp b/src/cpprealm/internal/bridge/object_schema.hpp index c1b22b5d..0e786eda 100644 --- a/src/cpprealm/internal/bridge/object_schema.hpp +++ b/src/cpprealm/internal/bridge/object_schema.hpp @@ -52,6 +52,7 @@ namespace realm::internal::bridge { void add_property(const property&); void set_name(const std::string& name); + std::string get_name() const; void set_primary_key(const std::string& primary_key); void set_object_type(object_type); property property_for_name(const std::string&); diff --git a/src/cpprealm/internal/bridge/table.cpp b/src/cpprealm/internal/bridge/table.cpp index 3b83b99b..158aa657 100644 --- a/src/cpprealm/internal/bridge/table.cpp +++ b/src/cpprealm/internal/bridge/table.cpp @@ -122,6 +122,14 @@ namespace realm::internal::bridge { return static_cast(*this)->get_column_key(name); } + uint32_t table::get_key() const { + return static_cast(*this)->get_key().value; + } + + std::string table::get_name() const { + return static_cast(*this)->get_name(); + } + table table::get_link_target(const col_key col_key) const { return static_cast(*this)->get_link_target(col_key); } diff --git a/src/cpprealm/internal/bridge/table.hpp b/src/cpprealm/internal/bridge/table.hpp index 54321271..47590f50 100644 --- a/src/cpprealm/internal/bridge/table.hpp +++ b/src/cpprealm/internal/bridge/table.hpp @@ -48,6 +48,8 @@ namespace realm { operator ConstTableRef() const; col_key get_column_key(const std::string_view &name) const; + uint32_t get_key() const; + std::string get_name() const; obj create_object_with_primary_key(const mixed &key) const; diff --git a/src/cpprealm/link.hpp b/src/cpprealm/link.hpp index 8a48c58b..9fd55f10 100644 --- a/src/cpprealm/link.hpp +++ b/src/cpprealm/link.hpp @@ -43,14 +43,21 @@ namespace realm { } struct ref_type { - managed m_managed; - managed* operator ->() const { - return const_cast*>(&m_managed); + explicit ref_type(managed&& value) : m_managed(std::move(value)) { } + const managed* operator ->() const { + return &m_managed; } managed* operator ->() { return &m_managed; } + const managed& operator *() const { + return m_managed; + } + managed& operator *() { + return m_managed; + } + bool operator ==(const managed& rhs) const { if (this->m_managed.m_realm != *rhs.m_realm) { return false; @@ -80,16 +87,16 @@ namespace realm { bool operator !=(const ref_type& rhs) const { return !this->operator==(rhs); } + private: + managed m_managed; }; ref_type operator ->() const { if (should_detect_usage_for_queries) { - managed m = managed::prepare_for_query(*m_realm); - return {std::move(m)}; + return ref_type(managed::prepare_for_query(*m_realm)); } - managed m(m_obj->get_linked_object(m_key), *m_realm); - return {std::move(m)}; + return ref_type(managed(m_obj->get_linked_object(m_key), *m_realm)); } - operator bool() { + operator bool() const { if (m_obj && m_key) { return !m_obj->is_null(m_key); } @@ -163,6 +170,11 @@ namespace realm { bool operator !=(const managed& rhs) const { return !this->operator==(rhs); } + + private: + managed() = default; + template + friend struct managed; }; } //namespace realm #endif //CPPREALM_BRIDGE_LINK_HPP diff --git a/src/cpprealm/macros.hpp b/src/cpprealm/macros.hpp index 4ec3a286..768cf99f 100644 --- a/src/cpprealm/macros.hpp +++ b/src/cpprealm/macros.hpp @@ -119,6 +119,106 @@ #define FE_97(WHAT, cls, X, ...) WHAT(cls, X)FE_96(WHAT, cls, __VA_ARGS__) #define FE_98(WHAT, cls, X, ...) WHAT(cls, X)FE_97(WHAT, cls, __VA_ARGS__) #define FE_99(WHAT, cls, X, ...) WHAT(cls, X)FE_98(WHAT, cls, __VA_ARGS__) +#define FE_100(WHAT, cls, X, ...) WHAT(cls, X) FE_99(WHAT, cls, __VA_ARGS__) +#define FE_101(WHAT, cls, X, ...) WHAT(cls, X) FE_100(WHAT, cls, __VA_ARGS__) +#define FE_102(WHAT, cls, X, ...) WHAT(cls, X) FE_101(WHAT, cls, __VA_ARGS__) +#define FE_103(WHAT, cls, X, ...) WHAT(cls, X) FE_102(WHAT, cls, __VA_ARGS__) +#define FE_104(WHAT, cls, X, ...) WHAT(cls, X) FE_103(WHAT, cls, __VA_ARGS__) +#define FE_105(WHAT, cls, X, ...) WHAT(cls, X) FE_104(WHAT, cls, __VA_ARGS__) +#define FE_106(WHAT, cls, X, ...) WHAT(cls, X) FE_105(WHAT, cls, __VA_ARGS__) +#define FE_107(WHAT, cls, X, ...) WHAT(cls, X) FE_106(WHAT, cls, __VA_ARGS__) +#define FE_108(WHAT, cls, X, ...) WHAT(cls, X) FE_107(WHAT, cls, __VA_ARGS__) +#define FE_109(WHAT, cls, X, ...) WHAT(cls, X) FE_108(WHAT, cls, __VA_ARGS__) +#define FE_110(WHAT, cls, X, ...) WHAT(cls, X) FE_109(WHAT, cls, __VA_ARGS__) +#define FE_111(WHAT, cls, X, ...) WHAT(cls, X) FE_110(WHAT, cls, __VA_ARGS__) +#define FE_112(WHAT, cls, X, ...) WHAT(cls, X) FE_111(WHAT, cls, __VA_ARGS__) +#define FE_113(WHAT, cls, X, ...) WHAT(cls, X) FE_112(WHAT, cls, __VA_ARGS__) +#define FE_114(WHAT, cls, X, ...) WHAT(cls, X) FE_113(WHAT, cls, __VA_ARGS__) +#define FE_115(WHAT, cls, X, ...) WHAT(cls, X) FE_114(WHAT, cls, __VA_ARGS__) +#define FE_116(WHAT, cls, X, ...) WHAT(cls, X) FE_115(WHAT, cls, __VA_ARGS__) +#define FE_117(WHAT, cls, X, ...) WHAT(cls, X) FE_116(WHAT, cls, __VA_ARGS__) +#define FE_118(WHAT, cls, X, ...) WHAT(cls, X) FE_117(WHAT, cls, __VA_ARGS__) +#define FE_119(WHAT, cls, X, ...) WHAT(cls, X) FE_118(WHAT, cls, __VA_ARGS__) +#define FE_120(WHAT, cls, X, ...) WHAT(cls, X) FE_119(WHAT, cls, __VA_ARGS__) +#define FE_121(WHAT, cls, X, ...) WHAT(cls, X) FE_120(WHAT, cls, __VA_ARGS__) +#define FE_122(WHAT, cls, X, ...) WHAT(cls, X) FE_121(WHAT, cls, __VA_ARGS__) +#define FE_123(WHAT, cls, X, ...) WHAT(cls, X) FE_122(WHAT, cls, __VA_ARGS__) +#define FE_124(WHAT, cls, X, ...) WHAT(cls, X) FE_123(WHAT, cls, __VA_ARGS__) +#define FE_125(WHAT, cls, X, ...) WHAT(cls, X) FE_124(WHAT, cls, __VA_ARGS__) +#define FE_126(WHAT, cls, X, ...) WHAT(cls, X) FE_125(WHAT, cls, __VA_ARGS__) +#define FE_127(WHAT, cls, X, ...) WHAT(cls, X) FE_126(WHAT, cls, __VA_ARGS__) +#define FE_128(WHAT, cls, X, ...) WHAT(cls, X) FE_127(WHAT, cls, __VA_ARGS__) +#define FE_129(WHAT, cls, X, ...) WHAT(cls, X) FE_128(WHAT, cls, __VA_ARGS__) +#define FE_130(WHAT, cls, X, ...) WHAT(cls, X) FE_129(WHAT, cls, __VA_ARGS__) +#define FE_131(WHAT, cls, X, ...) WHAT(cls, X) FE_130(WHAT, cls, __VA_ARGS__) +#define FE_132(WHAT, cls, X, ...) WHAT(cls, X) FE_131(WHAT, cls, __VA_ARGS__) +#define FE_133(WHAT, cls, X, ...) WHAT(cls, X) FE_132(WHAT, cls, __VA_ARGS__) +#define FE_134(WHAT, cls, X, ...) WHAT(cls, X) FE_133(WHAT, cls, __VA_ARGS__) +#define FE_135(WHAT, cls, X, ...) WHAT(cls, X) FE_134(WHAT, cls, __VA_ARGS__) +#define FE_136(WHAT, cls, X, ...) WHAT(cls, X) FE_135(WHAT, cls, __VA_ARGS__) +#define FE_137(WHAT, cls, X, ...) WHAT(cls, X) FE_136(WHAT, cls, __VA_ARGS__) +#define FE_138(WHAT, cls, X, ...) WHAT(cls, X) FE_137(WHAT, cls, __VA_ARGS__) +#define FE_139(WHAT, cls, X, ...) WHAT(cls, X) FE_138(WHAT, cls, __VA_ARGS__) +#define FE_140(WHAT, cls, X, ...) WHAT(cls, X) FE_139(WHAT, cls, __VA_ARGS__) +#define FE_141(WHAT, cls, X, ...) WHAT(cls, X) FE_140(WHAT, cls, __VA_ARGS__) +#define FE_142(WHAT, cls, X, ...) WHAT(cls, X) FE_141(WHAT, cls, __VA_ARGS__) +#define FE_143(WHAT, cls, X, ...) WHAT(cls, X) FE_142(WHAT, cls, __VA_ARGS__) +#define FE_144(WHAT, cls, X, ...) WHAT(cls, X) FE_143(WHAT, cls, __VA_ARGS__) +#define FE_145(WHAT, cls, X, ...) WHAT(cls, X) FE_144(WHAT, cls, __VA_ARGS__) +#define FE_146(WHAT, cls, X, ...) WHAT(cls, X) FE_145(WHAT, cls, __VA_ARGS__) +#define FE_147(WHAT, cls, X, ...) WHAT(cls, X) FE_146(WHAT, cls, __VA_ARGS__) +#define FE_148(WHAT, cls, X, ...) WHAT(cls, X) FE_147(WHAT, cls, __VA_ARGS__) +#define FE_149(WHAT, cls, X, ...) WHAT(cls, X) FE_148(WHAT, cls, __VA_ARGS__) +#define FE_150(WHAT, cls, X, ...) WHAT(cls, X) FE_149(WHAT, cls, __VA_ARGS__) +#define FE_151(WHAT, cls, X, ...) WHAT(cls, X) FE_150(WHAT, cls, __VA_ARGS__) +#define FE_152(WHAT, cls, X, ...) WHAT(cls, X) FE_151(WHAT, cls, __VA_ARGS__) +#define FE_153(WHAT, cls, X, ...) WHAT(cls, X) FE_152(WHAT, cls, __VA_ARGS__) +#define FE_154(WHAT, cls, X, ...) WHAT(cls, X) FE_153(WHAT, cls, __VA_ARGS__) +#define FE_155(WHAT, cls, X, ...) WHAT(cls, X) FE_154(WHAT, cls, __VA_ARGS__) +#define FE_156(WHAT, cls, X, ...) WHAT(cls, X) FE_155(WHAT, cls, __VA_ARGS__) +#define FE_157(WHAT, cls, X, ...) WHAT(cls, X) FE_156(WHAT, cls, __VA_ARGS__) +#define FE_158(WHAT, cls, X, ...) WHAT(cls, X) FE_157(WHAT, cls, __VA_ARGS__) +#define FE_159(WHAT, cls, X, ...) WHAT(cls, X) FE_158(WHAT, cls, __VA_ARGS__) +#define FE_160(WHAT, cls, X, ...) WHAT(cls, X) FE_159(WHAT, cls, __VA_ARGS__) +#define FE_161(WHAT, cls, X, ...) WHAT(cls, X) FE_160(WHAT, cls, __VA_ARGS__) +#define FE_162(WHAT, cls, X, ...) WHAT(cls, X) FE_161(WHAT, cls, __VA_ARGS__) +#define FE_163(WHAT, cls, X, ...) WHAT(cls, X) FE_162(WHAT, cls, __VA_ARGS__) +#define FE_164(WHAT, cls, X, ...) WHAT(cls, X) FE_163(WHAT, cls, __VA_ARGS__) +#define FE_165(WHAT, cls, X, ...) WHAT(cls, X) FE_164(WHAT, cls, __VA_ARGS__) +#define FE_166(WHAT, cls, X, ...) WHAT(cls, X) FE_165(WHAT, cls, __VA_ARGS__) +#define FE_167(WHAT, cls, X, ...) WHAT(cls, X) FE_166(WHAT, cls, __VA_ARGS__) +#define FE_168(WHAT, cls, X, ...) WHAT(cls, X) FE_167(WHAT, cls, __VA_ARGS__) +#define FE_169(WHAT, cls, X, ...) WHAT(cls, X) FE_168(WHAT, cls, __VA_ARGS__) +#define FE_170(WHAT, cls, X, ...) WHAT(cls, X) FE_169(WHAT, cls, __VA_ARGS__) +#define FE_171(WHAT, cls, X, ...) WHAT(cls, X) FE_170(WHAT, cls, __VA_ARGS__) +#define FE_172(WHAT, cls, X, ...) WHAT(cls, X) FE_171(WHAT, cls, __VA_ARGS__) +#define FE_173(WHAT, cls, X, ...) WHAT(cls, X) FE_172(WHAT, cls, __VA_ARGS__) +#define FE_174(WHAT, cls, X, ...) WHAT(cls, X) FE_173(WHAT, cls, __VA_ARGS__) +#define FE_175(WHAT, cls, X, ...) WHAT(cls, X) FE_174(WHAT, cls, __VA_ARGS__) +#define FE_176(WHAT, cls, X, ...) WHAT(cls, X) FE_175(WHAT, cls, __VA_ARGS__) +#define FE_177(WHAT, cls, X, ...) WHAT(cls, X) FE_176(WHAT, cls, __VA_ARGS__) +#define FE_178(WHAT, cls, X, ...) WHAT(cls, X) FE_177(WHAT, cls, __VA_ARGS__) +#define FE_179(WHAT, cls, X, ...) WHAT(cls, X) FE_178(WHAT, cls, __VA_ARGS__) +#define FE_180(WHAT, cls, X, ...) WHAT(cls, X) FE_179(WHAT, cls, __VA_ARGS__) +#define FE_181(WHAT, cls, X, ...) WHAT(cls, X) FE_180(WHAT, cls, __VA_ARGS__) +#define FE_182(WHAT, cls, X, ...) WHAT(cls, X) FE_181(WHAT, cls, __VA_ARGS__) +#define FE_183(WHAT, cls, X, ...) WHAT(cls, X) FE_182(WHAT, cls, __VA_ARGS__) +#define FE_184(WHAT, cls, X, ...) WHAT(cls, X) FE_183(WHAT, cls, __VA_ARGS__) +#define FE_185(WHAT, cls, X, ...) WHAT(cls, X) FE_184(WHAT, cls, __VA_ARGS__) +#define FE_186(WHAT, cls, X, ...) WHAT(cls, X) FE_185(WHAT, cls, __VA_ARGS__) +#define FE_187(WHAT, cls, X, ...) WHAT(cls, X) FE_186(WHAT, cls, __VA_ARGS__) +#define FE_188(WHAT, cls, X, ...) WHAT(cls, X) FE_187(WHAT, cls, __VA_ARGS__) +#define FE_189(WHAT, cls, X, ...) WHAT(cls, X) FE_188(WHAT, cls, __VA_ARGS__) +#define FE_190(WHAT, cls, X, ...) WHAT(cls, X) FE_189(WHAT, cls, __VA_ARGS__) +#define FE_191(WHAT, cls, X, ...) WHAT(cls, X) FE_190(WHAT, cls, __VA_ARGS__) +#define FE_192(WHAT, cls, X, ...) WHAT(cls, X) FE_191(WHAT, cls, __VA_ARGS__) +#define FE_193(WHAT, cls, X, ...) WHAT(cls, X) FE_192(WHAT, cls, __VA_ARGS__) +#define FE_194(WHAT, cls, X, ...) WHAT(cls, X) FE_193(WHAT, cls, __VA_ARGS__) +#define FE_195(WHAT, cls, X, ...) WHAT(cls, X) FE_194(WHAT, cls, __VA_ARGS__) +#define FE_196(WHAT, cls, X, ...) WHAT(cls, X) FE_195(WHAT, cls, __VA_ARGS__) +#define FE_197(WHAT, cls, X, ...) WHAT(cls, X) FE_196(WHAT, cls, __VA_ARGS__) +#define FE_198(WHAT, cls, X, ...) WHAT(cls, X) FE_197(WHAT, cls, __VA_ARGS__) +#define FE_199(WHAT, cls, X, ...) WHAT(cls, X) FE_198(WHAT, cls, __VA_ARGS__) #define GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \ _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26,\ @@ -126,19 +226,39 @@ _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54,\ _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68,\ _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82,\ - _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, NAME, ...) NAME + _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, \ + _97, _98, _99, _100, _101, _102, _103, _104, _105, _106, _107, _108, _109, \ + _110, _111, _112, _113, _114, _115, _116, _117, _118, _119, _120, _121, \ + _122, _123, _124, _125, _126, _127, _128, _129, _130, _131, _132, _133, \ + _134, _135, _136, _137, _138, _139, _140, _141, _142, _143, _144, _145, \ + _146, _147, _148, _149, _150, _151, _152, _153, _154, _155, _156, _157, \ + _158, _159, _160, _161, _162, _163, _164, _165, _166, _167, _168, _169, \ + _170, _171, _172, _173, _174, _175, _176, _177, _178, _179, _180, _181, \ + _182, _183, _184, _185, _186, _187, _188, _189, _190, _191, _192, _193, \ + _194, _195, _196, _197, _198, _199, NAME, ...) NAME \ #define FOR_EACH(action, cls, ...) \ GET_MACRO(_0, __VA_ARGS__, \ - FE_99, FE_98, FE_97, FE_96, FE_95, FE_94, FE_93, FE_92, FE_91, FE_90, FE_89, FE_88, \ - FE_87, FE_86, FE_85, FE_84, FE_83, FE_82, FE_81, FE_80, FE_79, FE_78, FE_77, FE_76, \ - FE_75, FE_74, FE_73, FE_72, FE_71, FE_70, FE_69, FE_68, FE_67, FE_66, FE_65, FE_64, \ - FE_63, FE_62, FE_61, FE_60, FE_59, FE_58, FE_57, FE_56, FE_55, FE_54, FE_53, FE_52, \ - FE_51, FE_50, FE_49, FE_48, FE_47, FE_46, FE_45, FE_44, FE_43, FE_42, FE_41, FE_40, \ - FE_39, FE_38, FE_37, FE_36, FE_35, FE_34, FE_33, FE_32, FE_31, FE_30, FE_29, FE_28, \ - FE_27, FE_26, FE_25, FE_24, FE_23, FE_22, FE_21, FE_20, FE_19, FE_18, FE_17, 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, cls, __VA_ARGS__) + FE_199, FE_198, FE_197, FE_196, FE_195, FE_194, FE_193, FE_192, FE_191, FE_190, \ + FE_189, FE_188, FE_187, FE_186, FE_185, FE_184, FE_183, FE_182, FE_181, FE_180, \ + FE_179, FE_178, FE_177, FE_176, FE_175, FE_174, FE_173, FE_172, FE_171, FE_170, \ + FE_169, FE_168, FE_167, FE_166, FE_165, FE_164, FE_163, FE_162, FE_161, FE_160, \ + FE_159, FE_158, FE_157, FE_156, FE_155, FE_154, FE_153, FE_152, FE_151, FE_150, \ + FE_149, FE_148, FE_147, FE_146, FE_145, FE_144, FE_143, FE_142, FE_141, FE_140, \ + FE_139, FE_138, FE_137, FE_136, FE_135, FE_134, FE_133, FE_132, FE_131, FE_130, \ + FE_129, FE_128, FE_127, FE_126, FE_125, FE_124, FE_123, FE_122, FE_121, FE_120, \ + FE_119, FE_118, FE_117, FE_116, FE_115, FE_114, FE_113, FE_112, FE_111, FE_110, \ + FE_109, FE_108, FE_107, FE_106, FE_105, FE_104, FE_103, FE_102, FE_101, FE_100, \ + FE_99, FE_98, FE_97, FE_96, FE_95, FE_94, FE_93, FE_92, FE_91, FE_90, \ + FE_89, FE_88, FE_87, FE_86, FE_85, FE_84, FE_83, FE_82, FE_81, FE_80, \ + FE_79, FE_78, FE_77, FE_76, FE_75, FE_74, FE_73, FE_72, FE_71, FE_70, \ + FE_69, FE_68, FE_67, FE_66, FE_65, FE_64, FE_63, FE_62, FE_61, FE_60, \ + FE_59, FE_58, FE_57, FE_56, FE_55, FE_54, FE_53, FE_52, FE_51, FE_50, \ + FE_49, FE_48, FE_47, FE_46, FE_45, FE_44, FE_43, FE_42, FE_41, FE_40, \ + FE_39, FE_38, FE_37, FE_36, FE_35, FE_34, FE_33, FE_32, FE_31, FE_30, \ + FE_29, FE_28, FE_27, FE_26, FE_25, FE_24, FE_23, FE_22, FE_21, FE_20, \ + FE_19, FE_18, FE_17, 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, cls, __VA_ARGS__) #define DECLARE_PERSISTED(cls, property) managed property; #define DECLARE_PROPERTY(cls, p) realm::property<&cls::p>(#p), @@ -177,6 +297,7 @@ constexpr constant_index< acc > counter_crumb( id, constant_index< rank >, const namespace realm { struct managed_base { + protected: managed_base() = default; managed_base(const managed_base& other) { m_obj = other.m_obj; @@ -219,7 +340,7 @@ namespace realm { should_detect_usage_for_queries = false; query = nullptr; } - + public: static constexpr bool is_object = false; internal::bridge::obj *m_obj = nullptr; internal::bridge::realm *m_realm = nullptr; diff --git a/src/cpprealm/managed_binary.hpp b/src/cpprealm/managed_binary.hpp index e96357da..2e3c6dca 100644 --- a/src/cpprealm/managed_binary.hpp +++ b/src/cpprealm/managed_binary.hpp @@ -43,6 +43,15 @@ namespace realm { //MARK: - comparison operators rbool operator==(const std::vector& rhs) const noexcept; rbool operator!=(const std::vector& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> @@ -74,6 +83,15 @@ namespace realm { //MARK: - comparison operators rbool operator==(const std::optional>& rhs) const noexcept; rbool operator!=(const std::optional>& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } diff --git a/src/cpprealm/managed_decimal.hpp b/src/cpprealm/managed_decimal.hpp index 99841de5..5f9150e0 100644 --- a/src/cpprealm/managed_decimal.hpp +++ b/src/cpprealm/managed_decimal.hpp @@ -59,6 +59,15 @@ namespace realm { managed& operator-=(const decimal128& o); managed& operator*=(const decimal128& o); managed& operator/=(const decimal128& o); + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> @@ -97,6 +106,15 @@ namespace realm { managed>& operator-=(const decimal128& o); managed>& operator*=(const decimal128& o); managed>& operator/=(const decimal128& o); + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } // namespace realm diff --git a/src/cpprealm/managed_dictionary.hpp b/src/cpprealm/managed_dictionary.hpp index fad917d6..c91c94cd 100644 --- a/src/cpprealm/managed_dictionary.hpp +++ b/src/cpprealm/managed_dictionary.hpp @@ -117,19 +117,6 @@ namespace realm { } }; template<> - struct box> : public box_base> { - using box_base>::box_base; - using box_base>::operator=; - std::optional operator*() { - auto v = m_backing_map.get(m_key); - if (v.is_null()) { - return std::nullopt; - } else { - return v.operator int64_t(); - }; - } - }; - template<> struct box : public box_base { using box_base::box_base; using box_base::operator=; @@ -138,19 +125,6 @@ namespace realm { } }; template<> - struct box> : public box_base> { - using box_base>::box_base; - using box_base>::operator=; - std::optional operator*() { - auto v = m_backing_map.get(m_key); - if (v.is_null()) { - return std::nullopt; - } else { - return v.operator double(); - }; - } - }; - template<> struct box : public box_base { using box_base::box_base; using box_base::operator=; @@ -158,19 +132,6 @@ namespace realm { return m_backing_map.get(m_key).operator bool(); } }; - template<> - struct box> : public box_base> { - using box_base>::box_base; - using box_base>::operator=; - std::optional operator*() { - auto v = m_backing_map.get(m_key); - if (v.is_null()) { - return std::nullopt; - } else { - return v.operator bool(); - }; - } - }; template struct box>> : public box_base { using box_base::box_base; @@ -219,19 +180,6 @@ namespace realm { return this->m_backing_map.get(this->m_key).operator internal::bridge::uuid().operator ::realm::uuid(); } }; - template<> - struct box> : public box_base> { - using box_base>::box_base; - using box_base>::operator=; - std::optional operator*() { - auto v = this->m_backing_map.get(this->m_key); - if (v.is_null()) { - return std::nullopt; - } else { - return v.operator internal::bridge::uuid().operator ::realm::uuid(); - }; - } - }; template struct box::value>> : public box_base { using box_base::box_base; @@ -246,19 +194,6 @@ namespace realm { } }; template<> - struct box> : public box_base> { - using box_base>::box_base; - using box_base>::operator=; - std::optional operator*() { - auto v = this->m_backing_map.get(this->m_key); - if (v.is_null()) { - return std::nullopt; - } else { - return v.operator internal::bridge::object_id().operator ::realm::object_id(); - }; - } - }; - template<> struct box : public box_base { using box_base::box_base; using box_base::operator=; @@ -267,19 +202,6 @@ namespace realm { } }; template<> - struct box> : public box_base> { - using box_base>::box_base; - using box_base>::operator=; - std::optional operator*() { - auto v = this->m_backing_map.get(this->m_key); - if (v.is_null()) { - return std::nullopt; - } else { - return v.operator internal::bridge::decimal128().operator ::realm::decimal128(); - }; - } - }; - template<> struct box> : public box_base> { using box_base>::box_base; using box_base>::operator=; @@ -288,19 +210,6 @@ namespace realm { } }; template<> - struct box>> : public box_base>> { - using box_base>>::box_base; - using box_base>>::operator=; - std::optional> operator*() { - auto v = this->m_backing_map.get(this->m_key); - if (v.is_null()) { - return std::nullopt; - } else { - return this->m_backing_map.get(this->m_key).operator internal::bridge::timestamp().operator std::chrono::time_point(); - }; - } - }; - template<> struct box> : public box_base> { using box_base>::box_base; using box_base>::operator=; @@ -309,19 +218,6 @@ namespace realm { } }; template<> - struct box>> : public box_base>> { - using box_base>>::box_base; - using box_base>>::operator=; - std::optional> operator*() { - auto v = this->m_backing_map.get(this->m_key); - if (v.is_null()) { - return std::nullopt; - } else { - return this->m_backing_map.get(this->m_key).operator internal::bridge::binary().operator std::vector(); - }; - } - }; - template<> struct box : public box_base { using box_base::box_base; using box_base::operator=; @@ -329,19 +225,6 @@ namespace realm { return this->m_backing_map.get(this->m_key).operator std::string(); } }; - template<> - struct box> : public box_base> { - using box_base>::box_base; - using box_base>::operator=; - std::optional operator*() { - auto v = this->m_backing_map.get(this->m_key); - if (v.is_null()) { - return std::nullopt; - } else { - return this->m_backing_map.get(this->m_key).operator std::string(); - }; - } - }; //MARK: - Boxed Link template @@ -386,31 +269,17 @@ namespace realm { return !this->operator==(rhs); } - typename managed::ref_type operator*() { + std::optional::ref_type> operator*() { auto obj = this->m_backing_map.get_object(this->m_key); if (!obj.is_valid()) { return std::nullopt; } - auto m = managed(std::move(obj), this->m_realm); - std::apply([&m](auto &&...ptr) { - std::apply([&](auto &&...name) { - ((m.*ptr).assign(&m.m_obj, &m.m_realm, m.m_obj.get_table().get_column_key(name)), ...); - }, - managed::managed_pointers_names); - }, - managed::managed_pointers()); - return m; + return typename managed::ref_type(managed(std::move(obj), this->m_realm)); } typename managed::ref_type operator->() { auto obj = this->m_backing_map.get_object(this->m_key); - auto m = managed(std::move(obj), this->m_realm); - std::apply([&m](auto &&...ptr) { - std::apply([&](auto &&...name) { - ((m.*ptr).assign(&m.m_obj, &m.m_realm, m.m_obj.get_table().get_column_key(name)), ...); - }, managed::managed_pointers_names); - }, managed::managed_pointers()); - return {std::move(m)}; + return typename managed::ref_type(managed(std::move(obj), this->m_realm)); } box& operator=(V* o) { @@ -457,18 +326,6 @@ namespace realm { return !this->operator==(rhs); } - bool operator==(const V& rhs) const { - auto a = const_cast> *>(this)->m_backing_map.get_object(this->m_key); - auto &b = rhs.m_obj; - if (this->m_realm != rhs.m_realm) { - return false; - } - return a.get_key() == b.get_key(); - } - bool operator!=(const V& rhs) const { - return !this->operator==(rhs); - } - bool operator==(const box& rhs) { auto a = const_cast> *>(this)->m_backing_map.get_object(this->m_key); auto &b = (&rhs)->m_obj; @@ -618,6 +475,15 @@ namespace realm { token.m_dictionary = dict; return token; } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } // namespace realm diff --git a/src/cpprealm/managed_list.hpp b/src/cpprealm/managed_list.hpp index 9586753a..bc51d914 100644 --- a/src/cpprealm/managed_list.hpp +++ b/src/cpprealm/managed_list.hpp @@ -103,6 +103,10 @@ namespace realm { return ret; } + [[nodiscard]] results as_results() const { + return results(realm::internal::bridge::list(*m_realm, *m_obj, m_key).as_results()); + } + realm::notification_token observe(std::function&& fn) { auto list = std::make_shared(*m_realm, *m_obj, m_key); realm::notification_token token = list->add_notification_callback( @@ -158,10 +162,20 @@ namespace realm { return results(internal::bridge::list(*m_realm, *m_obj, m_key) .sort(std::vector({{"self", ascending}}))); } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template struct managed> : managed_base { + public: [[nodiscard]] std::vector detach() const { auto list = realm::internal::bridge::list(*m_realm, *m_obj, m_key); size_t count = list.size(); @@ -185,6 +199,10 @@ namespace realm { return ret; } + [[nodiscard]] results as_results() const { + return results(realm::internal::bridge::list(*m_realm, *m_obj, m_key).as_results()); + } + class iterator { public: using value_type = managed; @@ -311,13 +329,7 @@ namespace realm { } typename managed::ref_type operator[](size_t idx) const { auto list = realm::internal::bridge::list(*m_realm, *m_obj, m_key); - managed m(realm::internal::bridge::get(list, idx), *m_realm); - std::apply([&m](auto &&...ptr) { - std::apply([&](auto &&...name) { - ((m.*ptr).assign(&m.m_obj, &m.m_realm, m.m_obj.get_table().get_column_key(name)), ...); - }, managed::managed_pointers_names); - }, managed::managed_pointers()); - return {std::move(m)}; + return typename managed::ref_type(managed(realm::internal::bridge::get(list, idx), *m_realm)); } realm::notification_token observe(std::function&& fn) { @@ -358,6 +370,15 @@ namespace realm { auto table_ref = m_obj->get_target_table(m_key); return results(internal::bridge::results(*m_realm, table_ref)).sort(sort_descriptors); } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } // namespace realm diff --git a/src/cpprealm/managed_mixed.hpp b/src/cpprealm/managed_mixed.hpp index 4e751a8a..54bf07d0 100644 --- a/src/cpprealm/managed_mixed.hpp +++ b/src/cpprealm/managed_mixed.hpp @@ -28,6 +28,24 @@ namespace realm { struct managed::value>> : public managed_base { using managed::managed_base::operator=; + enum stored_type { + Int = 0, + Bool = 1, + String = 2, + Binary = 4, + Mixed = 6, + Timestamp = 8, + Float = 9, + Double = 10, + Decimal = 11, + Link = 12, + LinkList = 13, + ObjectId = 15, + TypedLink = 16, + UUID = 17, + Null = 18, + }; + managed& operator =(const T& v) { m_obj->set(m_key, std::visit([](auto&& arg) { using M = typename internal::type_info::type_info>::internal_type; @@ -42,6 +60,15 @@ namespace realm { return *this; } + [[nodiscard]] stored_type get_stored_type() const { + auto val = m_obj->get(m_key); + if (val.is_null()) { + return stored_type::Null; + } else { + return static_cast(val.type()); + } + } + [[nodiscard]] T detach() const { return deserialize(m_obj->get(m_key)); } @@ -86,7 +113,134 @@ namespace realm { } return detach() != T(std::monostate()); } + + bool has_link() const { + return (get_stored_type() == stored_type::TypedLink); + } + + template + typename managed::ref_type get_stored_link() const { + m_realm->read_group(); + realm::internal::bridge::mixed m = m_obj->get(m_key); + + auto obj = internal::bridge::object(*m_realm, m.operator internal::bridge::obj_link()); + uint32_t alternative_key = m_realm->table_for_object_type(managed>::schema.name).get_key(); + uint32_t stored_table = obj.get_obj().get_table().get_key(); + + if (alternative_key != stored_table) { + throw std::runtime_error("Different link type stored in mixed type. Stored type: " + obj.get_object_schema().get_name()); + } + return typename managed::ref_type(managed>(obj.get_obj(), *m_realm)); + } + + template + void set_link(U &&v) { + static_assert(sizeof(managed), "Must declare schema for T"); + static_assert(managed::object_type == ObjectType::TopLevel, "Mixed properties can only store Top Level objects."); + auto table = m_realm->table_for_object_type(managed::schema.name); + internal::bridge::obj o; + if constexpr (managed::schema.HasPrimaryKeyProperty) { + auto pk = v.*(managed::schema.primary_key().ptr); + o = table.create_object_with_primary_key(realm::internal::bridge::mixed(serialize(pk.value))); + } else { + o = table.create_object(); + } + + std::apply([&o, &v, this](auto && ...p) { + (accessor::Result>::set( + o, o.get_table().get_column_key(p.name), *this->m_realm, v.*(std::decay_t::ptr) + ), ...); + }, managed::schema.ps); + m_obj->set(m_key, internal::bridge::mixed(o.get_link())); + } + + template + std::enable_if_t::is_object && managed::object_type == ObjectType::TopLevel, void> + set_link(managed& link) { + m_obj->set(m_key, internal::bridge::mixed(internal::bridge::obj_link(link.m_obj.get_table().get_key(), link.m_obj.get_key()))); + } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; + + template + const bool holds_alternative(const realm::managed& v) noexcept { + auto val = v.get_stored_type(); + switch (val) { + case realm::managed::stored_type::Int: + if constexpr (std::is_same_v) + return true; + break; + case realm::managed::stored_type::Bool: + if constexpr (std::is_same_v) + return true; + break; + case realm::managed::stored_type::String: + if constexpr (std::is_same_v) + return true; + break; + case realm::managed::stored_type::Binary: + if constexpr (std::is_same_v>) + return true; + break; + case realm::managed::stored_type::Mixed: + if constexpr (std::is_same_v) + return true; + break; + case realm::managed::stored_type::Timestamp: + if constexpr (std::is_same_v>) + return true; + break; + case realm::managed::stored_type::Float: + if constexpr (std::is_same_v) + return true; + break; + case realm::managed::stored_type::Double: + if constexpr (std::is_same_v) + return true; + break; + case realm::managed::stored_type::Decimal: + if constexpr (std::is_same_v) + return true; + break; + case realm::managed::stored_type::ObjectId: + if constexpr (std::is_same_v) + return true; + break; + + case realm::managed::stored_type::UUID: + if constexpr (std::is_same_v) + return true; + break; + case realm::managed::stored_type::Null: + if constexpr (std::is_same_v) + return true; + break; + case realm::managed::stored_type::TypedLink: { + if constexpr (std::is_pointer_v) { + auto m = v.m_obj->template get(v.m_key); + uint32_t alternative_key = v.m_realm->table_for_object_type(managed>::schema.name).get_key(); + uint32_t stored_key = internal::bridge::object(*v.m_realm, m.operator internal::bridge::obj_link()).get_object_schema().table_key(); + return alternative_key == stored_key; + } + break; + } + case realm::managed::stored_type::Link: + case realm::managed::stored_type::LinkList: + break; + default: + break; + } + + return false; + } } #endif//CPPREALM_MANAGED_MIXED_HPP diff --git a/src/cpprealm/managed_numeric.hpp b/src/cpprealm/managed_numeric.hpp index d6a4ecdf..4c985761 100644 --- a/src/cpprealm/managed_numeric.hpp +++ b/src/cpprealm/managed_numeric.hpp @@ -135,6 +135,15 @@ namespace realm { m_obj->template set(this->m_key, old_val * o); return *this; } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> @@ -250,6 +259,15 @@ namespace realm { auto old_val = m_obj->template get(m_key); m_obj->template set(this->m_key, old_val * o); } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> @@ -268,6 +286,15 @@ namespace realm { rbool operator==(const bool& rhs) const noexcept; rbool operator!=(const bool& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; #define CPP_REALM_MANAGED_OPTIONAL_NUMERIC(type) \ @@ -345,7 +372,15 @@ namespace realm { throw std::runtime_error("Cannot perform arithmetic on null value."); \ } \ m_obj->template set(this->m_key, (*old_val) / o); \ - } \ + } \ + private: \ + managed() = default; \ + managed(const managed&) = delete; \ + managed(managed &&) = delete; \ + managed& operator=(const managed&) = delete; \ + managed& operator=(managed&&) = delete; \ + template \ + friend struct managed; \ }; \ CPP_REALM_MANAGED_OPTIONAL_NUMERIC(int64_t); @@ -369,6 +404,15 @@ CPP_REALM_MANAGED_OPTIONAL_NUMERIC(double); rbool operator==(const std::optional& rhs) const noexcept; rbool operator!=(const std::optional& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template @@ -439,6 +483,15 @@ CPP_REALM_MANAGED_OPTIONAL_NUMERIC(double); } return detach() <= rhs; } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template @@ -524,6 +577,15 @@ CPP_REALM_MANAGED_OPTIONAL_NUMERIC(double); } return detach() <= rhs; } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } // namespace realm diff --git a/src/cpprealm/managed_objectid.hpp b/src/cpprealm/managed_objectid.hpp index e101f4e3..0845b437 100644 --- a/src/cpprealm/managed_objectid.hpp +++ b/src/cpprealm/managed_objectid.hpp @@ -47,6 +47,15 @@ namespace realm { //MARK: - comparison operators rbool operator==(const realm::object_id& rhs) const noexcept; rbool operator!=(const realm::object_id& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> @@ -73,6 +82,15 @@ namespace realm { //MARK: - comparison operators rbool operator==(const std::optional& rhs) const noexcept; rbool operator!=(const std::optional& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } // namespace realm diff --git a/src/cpprealm/managed_primary_key.hpp b/src/cpprealm/managed_primary_key.hpp index af7000dd..2de9a0f4 100644 --- a/src/cpprealm/managed_primary_key.hpp +++ b/src/cpprealm/managed_primary_key.hpp @@ -182,7 +182,7 @@ namespace realm { }; template<> - struct managed> : managed_base { + struct managed> final : managed_base { primary_key detach() const { return operator int64_t(); } @@ -203,10 +203,19 @@ namespace realm { rbool operator>=(const int& rhs) const noexcept; rbool operator<(const int& rhs) const noexcept; rbool operator<=(const int& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> - struct managed> : managed_base { + struct managed> final : managed_base { primary_key detach() const { return operator std::string(); } @@ -219,6 +228,15 @@ namespace realm { rbool operator!=(const std::string& rhs) const noexcept; rbool operator==(const char* rhs) const noexcept; rbool operator!=(const char* rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = default; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> @@ -233,10 +251,19 @@ namespace realm { rbool operator==(const realm::uuid& rhs) const noexcept; rbool operator!=(const realm::uuid& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> - struct managed> : managed_base { + struct managed> final : managed_base { primary_key detach() const { return operator realm::object_id(); } @@ -247,10 +274,19 @@ namespace realm { rbool operator==(const realm::object_id& rhs) const noexcept; rbool operator!=(const realm::object_id& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template - struct managed, std::enable_if_t>> : managed_base { + struct managed, std::enable_if_t>> final : managed_base { primary_key detach() const { return operator T(); } @@ -275,10 +311,19 @@ namespace realm { } return serialize(detach().value) != serialize(rhs); } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> - struct managed>> : managed_base { + struct managed>> final : managed_base { primary_key> detach() const { return operator std::optional(); } @@ -297,11 +342,19 @@ namespace realm { rbool operator>=(const int& rhs) const noexcept; rbool operator<(const int& rhs) const noexcept; rbool operator<=(const int& rhs) const noexcept; + + private: + managed() = default; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template struct managed, std::enable_if_t, - std::is_enum >>> : managed_base { + std::is_enum >>> final : managed_base { primary_key detach() const { return operator T(); } @@ -339,10 +392,19 @@ namespace realm { } return serialize(detach().value) != serialize(rhs); } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> - struct managed>> : managed_base { + struct managed>> final : managed_base { primary_key> detach() const { return operator std::optional(); } @@ -355,10 +417,19 @@ namespace realm { rbool operator!=(const std::optional& rhs) const noexcept; rbool operator==(const char* rhs) const noexcept; rbool operator!=(const char* rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> - struct managed>> : managed_base { + struct managed>> final : managed_base { primary_key> detach() const { return operator std::optional(); } @@ -373,10 +444,19 @@ namespace realm { rbool operator==(const std::optional& rhs) const noexcept; rbool operator!=(const std::optional& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> - struct managed>> : managed_base { + struct managed>> final : managed_base { std::optional detach() const { return operator std::optional(); } @@ -391,6 +471,15 @@ namespace realm { rbool operator==(const std::optional& rhs) const noexcept; rbool operator!=(const std::optional& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } diff --git a/src/cpprealm/managed_set.hpp b/src/cpprealm/managed_set.hpp index ebcd63ed..06e1b413 100644 --- a/src/cpprealm/managed_set.hpp +++ b/src/cpprealm/managed_set.hpp @@ -150,6 +150,15 @@ namespace realm { { return internal::bridge::set(*m_realm, *m_obj, m_key).size(); } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template @@ -180,11 +189,6 @@ namespace realm { { auto s = realm::internal::bridge::set(*m_parent->m_realm, *m_parent->m_obj, m_parent->m_key); managed m(s.get_obj(m_i), *m_parent->m_realm); - std::apply([&m](auto &&...ptr) { - std::apply([&](auto &&...name) { - ((m.*ptr).assign(&m.m_obj, &m.m_realm, m.m_obj.get_table().get_column_key(name)), ...); - }, managed::managed_pointers_names); - }, managed::managed_pointers()); return {std::move(m)}; } @@ -356,6 +360,15 @@ namespace realm { { return internal::bridge::set(*m_realm, *m_obj, m_key).size(); } + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } // namespace realm diff --git a/src/cpprealm/managed_string.cpp b/src/cpprealm/managed_string.cpp index 06d393e4..ef5c4778 100644 --- a/src/cpprealm/managed_string.cpp +++ b/src/cpprealm/managed_string.cpp @@ -140,12 +140,4 @@ namespace realm { __cpprealm_build_optional_query(==, equal, std::string) __cpprealm_build_optional_query(!=, not_equal, std::string) -#ifdef __cpp_lib_starts_ends_with - bool managed_string::starts_with(std::string_view v) const noexcept { - return get().starts_with(v); - } - bool managed_string::ends_with(std::string_view v) const noexcept { - return get().ends_with(v); - } -#endif } diff --git a/src/cpprealm/managed_string.hpp b/src/cpprealm/managed_string.hpp index 84b39198..9d9cab51 100644 --- a/src/cpprealm/managed_string.hpp +++ b/src/cpprealm/managed_string.hpp @@ -65,6 +65,7 @@ namespace realm { //MARK: - managed string template <> struct managed : managed_base { + using value_type = std::string; using managed::managed_base::managed_base; using managed::managed_base::operator=; @@ -102,14 +103,6 @@ namespace realm { [[nodiscard]] size_t size() const noexcept; -#ifdef __cpp_lib_starts_ends_with - [[nodiscard]] bool starts_with(std::string_view) const noexcept; - [[nodiscard]] bool ends_with(std::string_view) const noexcept; -#endif -#ifdef __cpp_lib_string_contains - [[nodiscard]] bool contains(std::string_view) const noexcept; -#endif - //MARK: - operations void clear() noexcept; void push_back(char c); @@ -127,23 +120,22 @@ namespace realm { rbool operator!=(const char* rhs) const noexcept; rbool contains(const std::string &s) const noexcept; rbool empty() const noexcept; -#ifdef __cpp_impl_three_way_comparison - inline auto operator<=>(const std::string& rhs) const noexcept { - return get().compare(rhs) <=> 0; - } - inline auto operator<=>(const char* rhs) const noexcept { - return get().compare(rhs) <=> 0; - } -#else -#endif private: friend struct char_reference; friend struct const_char_reference; void inline set(const std::string& v) { m_obj->template set(m_key, v); } [[nodiscard]] inline std::string get() const { return m_obj->get(m_key); } + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; - template <> struct managed> : public managed { + template <> struct managed> final : public managed { + using value_type = std::optional; using managed::operator=; managed& operator =(std::optional&& v) { set(std::move(v)); return *this; } managed& operator =(const std::optional& v) { set(v); return *this; } @@ -165,6 +157,13 @@ namespace realm { rbool operator!=(const std::optional& rhs) const noexcept; private: void inline set(const std::optional& v) { m_obj->template set>(m_key, v); } + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } diff --git a/src/cpprealm/managed_timestamp.hpp b/src/cpprealm/managed_timestamp.hpp index 44fd1ff5..800f9c26 100644 --- a/src/cpprealm/managed_timestamp.hpp +++ b/src/cpprealm/managed_timestamp.hpp @@ -29,6 +29,7 @@ namespace realm { template<> struct managed> : public managed_base { + using value_type = std::chrono::time_point; using managed>::managed_base::operator=; [[nodiscard]] std::chrono::time_point detach() const { @@ -57,10 +58,20 @@ namespace realm { rbool operator>=(const std::chrono::time_point& rhs) const noexcept; rbool operator<(const std::chrono::time_point& rhs) const noexcept; rbool operator<=(const std::chrono::time_point& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> struct managed>> : managed_base { + using value_type = std::optional>; using managed>>::managed_base::operator=; [[nodiscard]] std::optional> detach() const { @@ -98,6 +109,15 @@ namespace realm { //MARK: - comparison operators rbool operator==(const std::optional>& rhs) const noexcept; rbool operator!=(const std::optional>& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } // namespace realm diff --git a/src/cpprealm/managed_uuid.hpp b/src/cpprealm/managed_uuid.hpp index fb139ffb..4364ecfc 100644 --- a/src/cpprealm/managed_uuid.hpp +++ b/src/cpprealm/managed_uuid.hpp @@ -29,6 +29,7 @@ namespace realm { template<> struct managed : managed_base { + using value_type = realm::uuid; using managed::managed_base::operator=; [[nodiscard]] realm::uuid detach() const { @@ -46,10 +47,20 @@ namespace realm { //MARK: - comparison operators rbool operator==(const realm::uuid& rhs) const noexcept; rbool operator!=(const realm::uuid& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; template<> struct managed> : managed_base { + using value_type = std::optional; using managed>::managed_base::operator=; [[nodiscard]] std::optional detach() const { @@ -72,6 +83,15 @@ namespace realm { //MARK: - comparison operators rbool operator==(const std::optional& rhs) const noexcept; rbool operator!=(const std::optional& rhs) const noexcept; + + private: + managed() = default; + managed(const managed&) = delete; + managed(managed &&) = delete; + managed& operator=(const managed&) = delete; + managed& operator=(managed&&) = delete; + template + friend struct managed; }; } // namespace realm diff --git a/src/cpprealm/results.hpp b/src/cpprealm/results.hpp index de6a5ed3..351f0dae 100644 --- a/src/cpprealm/results.hpp +++ b/src/cpprealm/results.hpp @@ -104,80 +104,21 @@ namespace realm { !collection_root_was_deleted; } }; - class iterator { - public: - using difference_type = size_t; - using value_type = managed; - using pointer = std::unique_ptr; - using reference = value_type &; - using iterator_category = std::input_iterator_tag; - - bool operator!=(const iterator &other) const { - return !(*this == other); - } - - bool operator==(const iterator &other) const { - return (m_parent == other.m_parent) && (m_idx == other.m_idx); - } - - reference operator*() noexcept { - internal::bridge::obj obj = internal::bridge::get(m_parent->m_parent, m_idx); - value = managed(std::move(obj), this->m_parent->m_parent.get_realm()); - return value; - } - - pointer operator->() const noexcept { - auto obj = internal::bridge::get(*m_parent, m_idx); - return T::schema::create_unique(std::move(obj), m_parent->m_parent.get_realm()); - } - - iterator &operator++() { - m_idx++; - return *this; - } - - iterator operator++(int i) { - m_idx += i; - return *this; - } - - private: - iterator(size_t idx, Derived *parent) - : m_idx(idx), m_parent(parent) { - } - - size_t m_idx; - Derived *m_parent; - managed value; - - template - friend struct linking_objects; - template - friend struct results_common_base; - }; - virtual ~results_common_base() = default; - iterator begin() { - return iterator(0, static_cast(this)); - } - - iterator end() { - return iterator(m_parent.size(), static_cast(this)); - } size_t size() { return m_parent.size(); } - Derived& where(const std::string &query, const std::vector& arguments) { + virtual ~results_common_base() = default; + Derived where(const std::string &query, const std::vector& arguments) { std::vector mixed_args; for(auto& a : arguments) mixed_args.push_back(serialize(a)); - m_parent = internal::bridge::results(m_parent.get_realm(), - m_parent.get_table().query(query, std::move(mixed_args))); - return static_cast(*this); + return Derived(internal::bridge::results(m_parent.get_realm(), + m_parent.get_table().query(query, std::move(mixed_args)))); } - Derived& where(std::function&)>&& fn) { + Derived where(std::function&)>&& fn) { static_assert(sizeof(managed), "Must declare schema for T"); auto realm = m_parent.get_realm(); auto schema = realm.schema().find(managed::schema.name); @@ -186,8 +127,7 @@ namespace realm { auto builder = internal::bridge::query(table_ref); auto q = realm::query>(builder, std::move(schema), realm); auto full_query = fn(q).q; - m_parent = internal::bridge::results(m_parent.get_realm(), full_query); - return static_cast(*this); + return Derived(internal::bridge::results(m_parent.get_realm(), full_query)); } struct results_callback_wrapper : internal::bridge::collection_change_callback { @@ -252,14 +192,12 @@ namespace realm { return m_parent.get_realm().is_frozen(); } - Derived& sort(const std::string& key_path, bool ascending) { - m_parent = m_parent.sort({{key_path, ascending}}); - return static_cast(*this); + Derived sort(const std::string& key_path, bool ascending) { + return Derived(m_parent.sort({{key_path, ascending}})); } - Derived& sort(const std::vector& sort_descriptors) { - m_parent = m_parent.sort(sort_descriptors); - return static_cast(*this); + Derived sort(const std::vector& sort_descriptors) { + return Derived(m_parent.sort(sort_descriptors)); } protected: @@ -285,6 +223,50 @@ namespace realm { throw std::out_of_range("Index out of range."); return internal::bridge::get(this->m_parent, index); } + + class iterator { + public: + using difference_type = size_t; + using value_type = T; + using iterator_category = std::input_iterator_tag; + + bool operator!=(const iterator &other) const { + return !(*this == other); + } + + bool operator==(const iterator &other) const { + return (m_parent == other.m_parent) && (m_idx == other.m_idx); + } + + value_type operator*() noexcept { + return m_parent->operator[](m_idx); + } + + iterator &operator++() { + m_idx++; + return *this; + } + + iterator operator++(int i) { + m_idx += i; + return *this; + } + + explicit iterator(size_t idx, Derived *parent) + : m_idx(idx), m_parent(parent) { + } + private: + size_t m_idx; + Derived *m_parent; + }; + + iterator begin() { + return iterator(0, static_cast(this)); + } + + iterator end() { + return iterator(this->m_parent.size(), static_cast(this)); + } }; template @@ -298,6 +280,56 @@ namespace realm { throw std::out_of_range("Index out of range."); return deserialize(internal::bridge::get(this->m_parent, index)); } + + // TODO: The current impl of realm::mixed does not allow managed object types, + // to be accessed from the iterator as it would be required to be wrapped in a + // managed<> template. As these templates require on a col & obj key as they act as managed + // properties on an object this use case is broken. Ideally we should replace realm::mixed to + // not be a std::variant, but rather be a type-safe union we define in the SDK so that + // realm::mixed could have a managed context itself. + class iterator { + public: + using difference_type = size_t; + using value_type = T; + using iterator_category = std::input_iterator_tag; + + bool operator!=(const iterator &other) const { + return !(*this == other); + } + + bool operator==(const iterator &other) const { + return (m_parent == other.m_parent) && (m_idx == other.m_idx); + } + + value_type operator*() noexcept { + return m_parent->operator[](m_idx); + } + + iterator &operator++() { + m_idx++; + return *this; + } + + iterator operator++(int i) { + m_idx += i; + return *this; + } + + explicit iterator(size_t idx, Derived *parent) + : m_idx(idx), m_parent(parent) { + } + private: + size_t m_idx; + Derived *m_parent; + }; + + iterator begin() { + return iterator(0, static_cast(this)); + } + + iterator end() { + return iterator(this->m_parent.size(), static_cast(this)); + } }; template @@ -311,6 +343,50 @@ namespace realm { throw std::out_of_range("Index out of range."); return static_cast(internal::bridge::get(this->m_parent, index)); } + + class iterator { + public: + using difference_type = size_t; + using value_type = T; + using iterator_category = std::input_iterator_tag; + + bool operator!=(const iterator &other) const { + return !(*this == other); + } + + bool operator==(const iterator &other) const { + return (m_parent == other.m_parent) && (m_idx == other.m_idx); + } + + value_type operator*() noexcept { + return m_parent->operator[](m_idx); + } + + iterator &operator++() { + m_idx++; + return *this; + } + + iterator operator++(int i) { + m_idx += i; + return *this; + } + + explicit iterator(size_t idx, Derived *parent) + : m_idx(idx), m_parent(parent) { + } + private: + size_t m_idx; + Derived *m_parent; + }; + + iterator begin() { + return iterator(0, static_cast(this)); + } + + iterator end() { + return iterator(this->m_parent.size(), static_cast(this)); + } }; template @@ -324,6 +400,51 @@ namespace realm { throw std::out_of_range("Index out of range."); return managed(internal::bridge::get(this->m_parent, index), this->m_parent.get_realm()); } + + class iterator { + public: + using difference_type = size_t; + using value_type = managed; + using iterator_category = std::input_iterator_tag; + + bool operator!=(const iterator &other) const { + return !(*this == other); + } + + bool operator==(const iterator &other) const { + return (m_parent == other.m_parent) && (m_idx == other.m_idx); + } + + value_type operator*() noexcept { + internal::bridge::obj obj = internal::bridge::get(m_parent->m_parent, m_idx); + return managed(std::move(obj), this->m_parent->m_parent.get_realm()); + } + + iterator &operator++() { + m_idx++; + return *this; + } + + iterator operator++(int i) { + m_idx += i; + return *this; + } + + explicit iterator(size_t idx, Derived *parent) + : m_idx(idx), m_parent(parent) { + } + private: + size_t m_idx; + Derived *m_parent; + }; + + iterator begin() { + return iterator(0, static_cast(this)); + } + + iterator end() { + return iterator(this->m_parent.size(), static_cast(this)); + } }; template diff --git a/tests/db/list_tests.cpp b/tests/db/list_tests.cpp index 0b143c9d..582d889f 100644 --- a/tests/db/list_tests.cpp +++ b/tests/db/list_tests.cpp @@ -746,4 +746,100 @@ TEST_CASE("list", "[list]") { CHECK(sorted_object_results24[0] == AllTypesObject::Enum::two); CHECK(sorted_object_results24[1] == AllTypesObject::Enum::one); } + + SECTION("object lifetime") { + managed out_of_scope_obj; + { + auto realm = db(std::move(config)); + auto obj1 = AllTypesObject(); + obj1._id = 1; + + auto link1 = AllTypesObjectLink(); + link1.str_col = "bar1"; + obj1.opt_obj_col = &link1; + + auto link2 = AllTypesObjectLink(); + link2.str_col = "bar2"; + obj1.list_obj_col.push_back(&link2); + + StringObject s1; + s1.str_col = "objA"; + s1._id = 1; + StringObject s2; + s2.str_col = "objB"; + s2._id = 2; + link1.list_obj_col.push_back(&s1); + link1.list_obj_col.push_back(&s2); + + auto managed_obj = realm.write([&] { + return realm.add(std::move(obj1)); + }); + auto link_obj = managed_obj.list_obj_col[0]; + + auto query = realm.objects(); + CHECK(query.size() == 1); + size_t run_count = 0; + for (size_t i = 0; i < query.size(); i++) { + auto entry = query[i]; + if (entry.opt_obj_col) { + auto size = entry.opt_obj_col->list_obj_col.size(); + for (size_t j = 0; j < entry.opt_obj_col->list_obj_col.size(); j++) { + if (j == 0) { + CHECK(entry.opt_obj_col->list_obj_col[j]->str_col.detach() == "objA"); + } else { + CHECK(entry.opt_obj_col->list_obj_col[j]->str_col.detach() == "objB"); + } + run_count++; + } + } + + auto obj = entry.opt_obj_col->list_obj_col[0]; + CHECK(obj->str_col == "objA"); + + out_of_scope_obj = *obj; + CHECK(run_count == 2); + CHECK(out_of_scope_obj.str_col.detach() == "objA"); + } + } + // Check object copied correctly after scope exit. + CHECK(out_of_scope_obj.str_col.detach() == "objA"); + } + + SECTION("as_results managed objects") { + auto realm = realm::db(std::move(config)); + auto obj = realm::AllTypesObject(); + auto managed_obj = realm.write([&]() { + return realm.add(std::move(obj)); + }); + + AllTypesObjectLink link; + link._id = 1; + link.str_col = "foo"; + + AllTypesObjectLink link2; + link2._id = 2; + link2.str_col = "bar"; + + realm.write([&]() { + managed_obj.list_obj_col.push_back(&link); + managed_obj.list_obj_col.push_back(&link2); + }); + + CHECK(managed_obj.list_obj_col.as_results().size() == 2); + } + + SECTION("as_results managed objects") { + auto realm = realm::db(std::move(config)); + auto obj = realm::AllTypesObject(); + obj.list_int_col = {1, 2, 3}; + auto managed_obj = realm.write([&]() { + return realm.add(std::move(obj)); + }); + + CHECK(managed_obj.list_int_col.as_results().size() == 3); + auto res = managed_obj.list_int_col.as_results(); + CHECK(res[0] == 1); + CHECK(res[1] == 2); + CHECK(res[2] == 3); + } } diff --git a/tests/db/map_tests.cpp b/tests/db/map_tests.cpp index 8457b451..fb45f460 100644 --- a/tests/db/map_tests.cpp +++ b/tests/db/map_tests.cpp @@ -70,6 +70,8 @@ TEST_CASE("map", "[map]") { auto embedded2 = AllTypesObjectEmbedded(); embedded2.str_col = "bar"; + auto managed_links = realm.objects(); + realm.write([&managed_obj, &link2, &embedded2] { managed_obj.map_int_col["b"] = 84; managed_obj.map_str_col["b"] = "bar"; @@ -92,10 +94,7 @@ TEST_CASE("map", "[map]") { CHECK(managed_obj.map_uuid_col["b"] == realm::uuid()); CHECK(managed_obj.map_binary_col["a"] == std::vector{0, 1, 2}); CHECK(managed_obj.map_binary_col["b"] == std::vector{3, 4, 5}); -// CHECK(managed_obj.map_link_col["a"] == link); -// CHECK(managed_obj.map_embedded_col["a"] == embedded); -// CHECK(managed_obj.map_link_col["b"] == link2); -// CHECK(managed_obj.map_embedded_col["b"] == embedded2); + CHECK(managed_obj.map_link_col["a"] == managed_links[0]); CHECK(managed_obj.map_link_col["b"]->str_col == "foo"); CHECK(managed_obj.map_embedded_col["b"]->str_col == "bar"); @@ -207,4 +206,26 @@ TEST_CASE("map", "[map]") { std::map as_values = managed_obj.map_str_col.detach(); CHECK(as_values == std::map({{"a", std::string("baz")}, {"b", std::string("foo")}})); } + + SECTION("object lifetime") { + managed ptr; + { + auto obj = AllTypesObject(); + auto link = AllTypesObjectLink(); + link.str_col = "foo"; + obj.map_link_col = { + {"a", &link} + }; + auto realm = db(std::move(config)); + auto managed_obj = realm.write([&realm, &obj] { + return realm.add(std::move(obj)); + }); + + auto opt_boxed_value = *managed_obj.map_link_col["a"]; + auto ref_type = *opt_boxed_value; + ptr = *ref_type; + } + // Check object copied correctly after scope exit. + CHECK(ptr.str_col == "foo"); + } } diff --git a/tests/db/mixed_tests.cpp b/tests/db/mixed_tests.cpp index 7fe75fb7..2a6e4818 100644 --- a/tests/db/mixed_tests.cpp +++ b/tests/db/mixed_tests.cpp @@ -44,4 +44,117 @@ TEST_CASE("mixed", "[mixed]") { }); CHECK(managed_obj.mixed_col == u); } + + SECTION("holds_alternative") { + auto obj = AllTypesObject(); + obj.mixed_col = (int64_t)42; + CHECK(obj.mixed_col == realm::mixed((int64_t)42)); + auto realm = db(std::move(config)); + auto managed_obj = realm.write([&realm, &obj] { + return realm.add(std::move(obj)); + }); + bool result = realm::holds_alternative(managed_obj.mixed_col); + CHECK(result); + result = realm::holds_alternative(managed_obj.mixed_col); + CHECK_FALSE(result); + + realm.write([&realm, &managed_obj] { + managed_obj.mixed_col = std::string("foo"); + }); + result = realm::holds_alternative(managed_obj.mixed_col); + CHECK(result); + + realm.write([&realm, &managed_obj] { + managed_obj.mixed_col = std::vector({1,1,1,1}); + }); + result = realm::holds_alternative>(managed_obj.mixed_col); + CHECK(result); + + realm.write([&realm, &managed_obj] { + managed_obj.mixed_col = realm::mixed((int64_t)1234); + }); + result = realm::holds_alternative(managed_obj.mixed_col); + CHECK(result); + + realm.write([&realm, &managed_obj] { + managed_obj.mixed_col = std::chrono::time_point(); + }); + result = realm::holds_alternative>(managed_obj.mixed_col); + CHECK(result); + + realm.write([&realm, &managed_obj] { + managed_obj.mixed_col = 123.456; + }); + result = realm::holds_alternative(managed_obj.mixed_col); + CHECK(result); + + realm.write([&realm, &managed_obj] { + managed_obj.mixed_col = realm::decimal128("123.456"); + }); + result = realm::holds_alternative(managed_obj.mixed_col); + CHECK(result); + + realm.write([&realm, &managed_obj] { + managed_obj.mixed_col = realm::object_id(); + }); + result = realm::holds_alternative(managed_obj.mixed_col); + CHECK(result); + + realm.write([&realm, &managed_obj] { + managed_obj.mixed_col = realm::uuid(); + }); + result = realm::holds_alternative(managed_obj.mixed_col); + CHECK(result); + + realm.write([&realm, &managed_obj] { + managed_obj.mixed_col = std::monostate(); + }); + result = realm::holds_alternative(managed_obj.mixed_col); + CHECK(result); + } + + SECTION("links") { + auto obj = AllTypesObject(); + obj.mixed_col = (int64_t)42; + CHECK(obj.mixed_col == realm::mixed((int64_t)42)); + auto realm = db(std::move(config)); + auto managed_obj = realm.write([&realm, &obj] { + return realm.add(std::move(obj)); + }); + CHECK_FALSE(managed_obj.mixed_col.has_link()); + + auto link = AllTypesObjectLink(); + link._id = 0; + link.str_col = "foo"; + + realm.write([&] { + managed_obj.mixed_col.set_link(std::move(link)); + }); + CHECK(managed_obj.mixed_col.has_link()); + + auto mixed_link = managed_obj.mixed_col.get_stored_link(); + CHECK(mixed_link->str_col == "foo"); + + bool result = realm::holds_alternative(managed_obj.mixed_col); + CHECK(result); + + result = realm::holds_alternative(managed_obj.mixed_col); + CHECK_FALSE(result); + + auto link2 = AllTypesObjectLink(); + link2._id = 0; + link2.str_col = "bar"; + + auto managed_link = realm.write([&] { + return realm.add(std::move(link2)); + }); + + realm.write([&] { + managed_obj.mixed_col.set_link(managed_link); + }); + mixed_link = managed_obj.mixed_col.get_stored_link(); + CHECK(mixed_link->str_col == "bar"); + + CHECK_THROWS(managed_obj.mixed_col.get_stored_link()); + } } \ No newline at end of file diff --git a/tests/db/object_tests.cpp b/tests/db/object_tests.cpp index e0533e86..ba09cf4b 100644 --- a/tests/db/object_tests.cpp +++ b/tests/db/object_tests.cpp @@ -988,7 +988,6 @@ namespace realm { auto obj1 = AllTypesObject(); obj1._id = 1; - auto o = AllTypesObjectLink(); o.str_col = "bar"; obj1.opt_obj_col = &o; @@ -1005,8 +1004,83 @@ namespace realm { }); CHECK(realm.objects().size() == 2); -// CHECK(realm.objects().size() == 2); - + CHECK(realm.objects().size() == 1); } + + static_assert(!std::is_constructible_v::ref_type>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + static_assert(!std::is_constructible_v>, "Default constructor is private."); + + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); + static_assert(!std::is_constructible_v>>, "Default constructor is private."); } } diff --git a/tests/db/results_tests.cpp b/tests/db/results_tests.cpp index 55492837..2a837ab8 100644 --- a/tests/db/results_tests.cpp +++ b/tests/db/results_tests.cpp @@ -223,7 +223,7 @@ namespace realm { CHECK(moved_o.double_col == 123.456); } - SECTION("results_iterator") { + SECTION("results_iterator_object") { auto realm = db(std::move(config)); AllTypesObject obj; @@ -241,50 +241,75 @@ namespace realm { }); auto results = realm.objects(); + managed obj_from_loop; size_t count = 0; - for (auto& o : results) { + for (auto o : results) { count++; if (count == 1) { CHECK(o._id == 1); CHECK(o.str_col == "foo"); + obj_from_loop = o; } else { CHECK(o._id == 2); CHECK(o.str_col == "bar"); } } + CHECK(obj_from_loop.str_col == "foo"); CHECK(count == 2); } - SECTION("results_iterator") { + SECTION("results_iterator_primitive") { auto realm = db(std::move(config)); AllTypesObject obj; - obj.str_col = "foo"; - obj._id = 1; + obj.list_int_col = {1, 2, 3}; - AllTypesObject obj2; - obj2.str_col = "bar"; - obj2._id = 2; + auto managed_obj = realm.write([&]() { + return realm.add(std::move(obj)); + }); + auto results = managed_obj.list_int_col.as_results(); - realm.write([&realm, &obj, &obj2]() { - realm.add(std::move(obj)); - realm.add(std::move(obj2)); + std::vector res; + for (auto o : results) { + res.push_back(o); + } + CHECK(res == std::vector({1, 2, 3})); + } + SECTION("results_iterator_mixed") { + auto realm = db(std::move(config)); + + AllTypesObject obj; + obj.list_mixed_col = {realm::mixed((int64_t)1), realm::mixed((int64_t)2), realm::mixed((int64_t)3)}; + + auto managed_obj = realm.write([&]() { + return realm.add(std::move(obj)); }); - auto results = realm.objects(); + auto results = managed_obj.list_mixed_col.as_results(); - size_t count = 0; - for (auto& o : results) { - count++; - if (count == 1) { - CHECK(o._id == 1); - CHECK(o.str_col == "foo"); - } else { - CHECK(o._id == 2); - CHECK(o.str_col == "bar"); - } + std::vector res; + for (auto o : results) { + res.push_back(o); } - CHECK(count == 2); + CHECK(res == std::vector({realm::mixed((int64_t)1), realm::mixed((int64_t)2), realm::mixed((int64_t)3)})); + } + + SECTION("results_iterator_enum") { + auto realm = db(std::move(config)); + + AllTypesObject obj; + obj.list_enum_col = {AllTypesObject::Enum::one, AllTypesObject::Enum::two}; + + auto managed_obj = realm.write([&]() { + return realm.add(std::move(obj)); + }); + auto results = managed_obj.list_enum_col.as_results(); + + std::vector res; + for (auto o : results) { + res.push_back(o); + } + CHECK(res == std::vector({AllTypesObject::Enum::one, AllTypesObject::Enum::two})); } SECTION("results_query") { diff --git a/tests/db/set_tests.cpp b/tests/db/set_tests.cpp index 83e00dec..80660156 100644 --- a/tests/db/set_tests.cpp +++ b/tests/db/set_tests.cpp @@ -459,7 +459,7 @@ TEST_CASE("set", "[set]") { auto it = managed_obj.set_obj_col.find(managed_link); CHECK(it != managed_obj.set_obj_col.end()); - auto x = (*it)._id.operator int64_t(); + CHECK((*it).str_col == "bar"); CHECK(*it == realm.objects().where([](auto& o) { return o._id == 3; })[0]); auto it2 = managed_obj.set_obj_col.find(managed_link_not_in_set); CHECK(it2 == managed_obj.set_obj_col.end()); @@ -643,5 +643,50 @@ TEST_CASE("set", "[set]") { } CHECK(res == std::set({1, 2})); } + + SECTION("object lifetime") { + std::unique_ptr> ptr; + { + auto realm = realm::db(std::move(config)); + auto obj = realm::AllTypesObject(); + auto managed_obj = realm.write([&]() { + return realm.add(std::move(obj)); + }); + + AllTypesObjectLink link; + link._id = 1; + link.str_col = "foo"; + + AllTypesObjectLink link2; + link2._id = 2; + link2.str_col = "bar"; + + AllTypesObjectLink link3; + link3._id = 3; + link3.str_col = "bar"; + + auto managed_link_not_in_set = realm.write([&]() { + return realm.add(std::move(link2)); + }); + + auto managed_link = realm.write([&]() { + realm.add(std::move(link2)); + return realm.add(std::move(link3)); + }); + + realm.write([&]() { + managed_obj.set_obj_col.insert(&link); + managed_obj.set_obj_col.insert(&link); + managed_obj.set_obj_col.insert(managed_link); + managed_obj.set_obj_col.insert(managed_link); + }); + CHECK(managed_obj.set_obj_col.size() == 2); + + auto it = managed_obj.set_obj_col.find(managed_link); + ptr = std::make_unique>(*it); + } + // Object should exist after scope exit + CHECK(ptr->str_col == "bar"); + } } diff --git a/tests/db/test_objects.hpp b/tests/db/test_objects.hpp index 45c997a5..102be902 100644 --- a/tests/db/test_objects.hpp +++ b/tests/db/test_objects.hpp @@ -71,8 +71,16 @@ namespace realm { primary_key _id; std::string str_col; StringObject* str_link_col = nullptr; + + std::vector list_str_col; + std::vector list_obj_col; + std::set set_str_col; + std::set set_obj_col; + std::map map_str_col; + std::map map_link_col; + }; - REALM_SCHEMA(AllTypesObjectLink, _id, str_col, str_link_col) + REALM_SCHEMA(AllTypesObjectLink, _id, str_col, str_link_col, list_str_col, list_obj_col, set_str_col, set_obj_col, map_str_col, map_link_col) struct SetParentObject { primary_key _id;