From 5a37bce855d53421ca563bac96abf2c3b8a9cd69 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 2 May 2021 19:40:11 +0200 Subject: [PATCH] Lib/Diff: extend PlantingHandle to allow for placment-new ...this extension was spurred by the previeous refactoring. Since 'emplace' now clearly denotes an operation to move-embed an existing object, we could as well offer a separate 'create' API, which would take forwarding arguments as usual and just delegates to the placement-new operation 'create' already available in the InPlaceBuffer class. Such would be a convenience shortcut and is not strictly necessary, since move-construction is typically optimised away; yet it would also allow to support strictly non-copyable payload types. This refactoring also highlights a fuzziness in the existing design, where we just passed the interface type, while being sloppy about the DEFAULT type. In fact this *is* relevant, since any kind of construction might fail, necessitating to default-construct a placeholder, since InPlaceBuffer was intended for zero-overhead usage and thus has in itself no means to know about the state of its buffer's contents. Thus the only sane contract is that there is always a valid object emplaced into the buffer, which in turn forces us to provide a loophole for class hierarchies with an abstract base class -- in such a case the user has to provide a fallback type explicitly. --- src/lib/diff/record.hpp | 4 +- src/lib/opaque-holder.hpp | 77 ++++++++++++++----- .../library/opaque-unchecked-buffer-test.cpp | 15 +++- wiki/thinkPad.ichthyo.mm | 24 ++++++ 4 files changed, 94 insertions(+), 26 deletions(-) diff --git a/src/lib/diff/record.hpp b/src/lib/diff/record.hpp index 0b1f8465d..b51a4bd66 100644 --- a/src/lib/diff/record.hpp +++ b/src/lib/diff/record.hpp @@ -109,7 +109,7 @@ namespace lib { - template + template class PlantingHandle; namespace idi { @@ -511,7 +511,7 @@ namespace diff{ /* === low-level access (for diff application) === */ - using BufferHandle = PlantingHandle; + using BufferHandle = PlantingHandle; /** attachment point to receive and apply tree-diff changes. * The actual implementation needs to be provided for concrete Record payload types; diff --git a/src/lib/opaque-holder.hpp b/src/lib/opaque-holder.hpp index 4e4c315a9..9e79b180a 100644 --- a/src/lib/opaque-holder.hpp +++ b/src/lib/opaque-holder.hpp @@ -571,6 +571,9 @@ namespace lib { + template + class PlantingHandle; + /** * Buffer to place and maintain an object instance privately within another object. * Variation of a similar concept as with OpaqueHolder, but implemented here @@ -652,7 +655,10 @@ namespace lib { * Use as `InPlaceBuffer(embedType, arg1, arg2, arg3)` */ template static auto embedType() { return (SUB*) nullptr; } - + + /** a "planting handle" can be used to expose an opaque InPlaceBuffer through an API */ + using Handle = PlantingHandle; + /** Abbreviation for placement new */ template @@ -713,7 +719,7 @@ namespace lib { * @warning the type BA must expose a virtual dtor, since the targeted * InPlaceBuffer has to take ownership of the implanted object. */ - template + template class PlantingHandle { void* buffer_; @@ -723,31 +729,18 @@ namespace lib { "target interface BA must provide virtual dtor, " "since InPlaceBuffer needs to take ownership."); + template + void __ensure_can_create(); + + public: template - PlantingHandle (InPlaceBuffer& targetBuffer) + PlantingHandle (InPlaceBuffer& targetBuffer) : buffer_(&targetBuffer) , maxSiz_(maxSiz) { } - template - BA& - emplace (SUB&& implementation) - { - if (sizeof(SUB) > maxSiz_) - throw error::Fatal("Unable to implant implementation object of size " - "exceeding the pre-established storage buffer capacity. " - +boost::lexical_cast(sizeof(SUB)) + " > " - +boost::lexical_cast(maxSiz_) - ,error::LUMIERA_ERROR_CAPACITY); - - using Holder = InPlaceBuffer; - Holder& holder = *static_cast (buffer_); - - return holder.template create (std::forward (implementation)); - } - template bool canCreate() const @@ -755,6 +748,33 @@ namespace lib { return sizeof(SUB) <= maxSiz_; } + /** move-construct an instance of subclass into the opaque buffer */ + template + SUB& + emplace (SUB&& implementation) + { + __ensure_can_create(); + + using Holder = InPlaceBuffer; + Holder& holder = *static_cast (buffer_); + + return holder.template create (std::forward (implementation)); + } + + /** Abbreviation for placement new of a subclass SUB into the opaque buffer*/ + template + SUB& + create (ARGS&& ...args) + { + __ensure_can_create(); + + using Holder = InPlaceBuffer; + Holder& holder = *static_cast (buffer_); + + return holder.template create (std::forward (args)...); + } + + BA* get() const { @@ -766,6 +786,23 @@ namespace lib { + /** @internal Helper to ensure the opaque buffer provides sufficient storage + * @tparam SUB actual subclass type to be implanted into the opaque buffer + */ + template + template + inline void + PlantingHandle::__ensure_can_create() + { + if (not this->canCreate()) + throw error::Fatal("Unable to implant implementation object of size " + "exceeding the pre-established storage buffer capacity. " + +boost::lexical_cast(sizeof(SUB)) + " > " + +boost::lexical_cast(maxSiz_) + ,error::LUMIERA_ERROR_CAPACITY); + } + + } // namespace lib #endif diff --git a/tests/library/opaque-unchecked-buffer-test.cpp b/tests/library/opaque-unchecked-buffer-test.cpp index c6d721f38..27c032775 100644 --- a/tests/library/opaque-unchecked-buffer-test.cpp +++ b/tests/library/opaque-unchecked-buffer-test.cpp @@ -121,7 +121,7 @@ namespace test{ * due to alignment of the contained object * within OpaqueHolder's buffer */ - const size_t _ALIGN_ = sizeof(size_t); + const size_t _ALIGNMENT_OVERHEAD_ = sizeof(size_t); } @@ -141,10 +141,10 @@ namespace test{ _checksum = 0; _create_count = 0; { - typedef InPlaceBuffer), DD<0>> Buffer; + using Buffer = InPlaceBuffer), DD<0>>; Buffer buff; - CHECK (sizeof(buff) <= sizeof(DD<42>) + _ALIGN_); + CHECK (sizeof(buff) <= sizeof(DD<42>) + _ALIGNMENT_OVERHEAD_); CHECK (1 == _create_count); CHECK (0 == _checksum); buff->confess(); // one default object of type DD<0> has been created @@ -159,7 +159,14 @@ namespace test{ CHECK(0 == buff->id_); // default object was created, due to exception... - buff.create ("what the f**","is going on here?"); + // as a variation: use a "planting handle" to implant yet another subtype + // into the opaque buffer. This setup helps to expose such a buffer via API + using Handle = Buffer::Handle; + + Handle plantingHandle{buff}; + plantingHandle.create ("what the f**","is going on here?"); + + // subclass instance was indeed implanted into the opaque buffer buff->confess(); CHECK (6 == _create_count); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8149d6b93..29c610e24 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -33477,6 +33477,30 @@ + + + + + + + + + + + + + + + + + + + + + + + +