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.
This commit is contained in:
parent
5aa41accfc
commit
5a37bce855
4 changed files with 94 additions and 26 deletions
|
|
@ -109,7 +109,7 @@
|
|||
|
||||
namespace lib {
|
||||
|
||||
template<class BA>
|
||||
template<class BA, class DEFAULT>
|
||||
class PlantingHandle;
|
||||
|
||||
namespace idi {
|
||||
|
|
@ -511,7 +511,7 @@ namespace diff{
|
|||
|
||||
/* === low-level access (for diff application) === */
|
||||
|
||||
using BufferHandle = PlantingHandle<TreeMutator>;
|
||||
using BufferHandle = PlantingHandle<TreeMutator, TreeMutator>;
|
||||
|
||||
/** attachment point to receive and apply tree-diff changes.
|
||||
* The actual implementation needs to be provided for concrete Record payload types;
|
||||
|
|
|
|||
|
|
@ -571,6 +571,9 @@ namespace lib {
|
|||
|
||||
|
||||
|
||||
template<class BA, class DEFAULT>
|
||||
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<XYZ>, arg1, arg2, arg3)` */
|
||||
template<typename SUB>
|
||||
static auto embedType() { return (SUB*) nullptr; }
|
||||
|
||||
|
||||
/** a "planting handle" can be used to expose an opaque InPlaceBuffer through an API */
|
||||
using Handle = PlantingHandle<BA, DEFAULT>;
|
||||
|
||||
|
||||
/** Abbreviation for placement new */
|
||||
template<class TY, typename...ARGS>
|
||||
|
|
@ -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<class BA>
|
||||
template<class BA, class DEFAULT = BA>
|
||||
class PlantingHandle
|
||||
{
|
||||
void* buffer_;
|
||||
|
|
@ -723,31 +729,18 @@ namespace lib {
|
|||
"target interface BA must provide virtual dtor, "
|
||||
"since InPlaceBuffer needs to take ownership.");
|
||||
|
||||
template<class SUB>
|
||||
void __ensure_can_create();
|
||||
|
||||
|
||||
public:
|
||||
template<size_t maxSiz>
|
||||
PlantingHandle (InPlaceBuffer<BA, maxSiz>& targetBuffer)
|
||||
PlantingHandle (InPlaceBuffer<BA, maxSiz, DEFAULT>& targetBuffer)
|
||||
: buffer_(&targetBuffer)
|
||||
, maxSiz_(maxSiz)
|
||||
{ }
|
||||
|
||||
|
||||
template<class SUB>
|
||||
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<std::string>(sizeof(SUB)) + " > "
|
||||
+boost::lexical_cast<std::string>(maxSiz_)
|
||||
,error::LUMIERA_ERROR_CAPACITY);
|
||||
|
||||
using Holder = InPlaceBuffer<BA, sizeof(SUB)>;
|
||||
Holder& holder = *static_cast<Holder*> (buffer_);
|
||||
|
||||
return holder.template create<SUB> (std::forward<SUB> (implementation));
|
||||
}
|
||||
|
||||
template<class SUB>
|
||||
bool
|
||||
canCreate() const
|
||||
|
|
@ -755,6 +748,33 @@ namespace lib {
|
|||
return sizeof(SUB) <= maxSiz_;
|
||||
}
|
||||
|
||||
/** move-construct an instance of subclass into the opaque buffer */
|
||||
template<class SUB>
|
||||
SUB&
|
||||
emplace (SUB&& implementation)
|
||||
{
|
||||
__ensure_can_create<SUB>();
|
||||
|
||||
using Holder = InPlaceBuffer<BA, sizeof(SUB), DEFAULT>;
|
||||
Holder& holder = *static_cast<Holder*> (buffer_);
|
||||
|
||||
return holder.template create<SUB> (std::forward<SUB> (implementation));
|
||||
}
|
||||
|
||||
/** Abbreviation for placement new of a subclass SUB into the opaque buffer*/
|
||||
template<class SUB, typename...ARGS>
|
||||
SUB&
|
||||
create (ARGS&& ...args)
|
||||
{
|
||||
__ensure_can_create<SUB>();
|
||||
|
||||
using Holder = InPlaceBuffer<BA, sizeof(SUB), DEFAULT>;
|
||||
Holder& holder = *static_cast<Holder*> (buffer_);
|
||||
|
||||
return holder.template create<SUB> (std::forward<ARGS> (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<class BA, class B0>
|
||||
template<class SUB>
|
||||
inline void
|
||||
PlantingHandle<BA,B0>::__ensure_can_create()
|
||||
{
|
||||
if (not this->canCreate<SUB>())
|
||||
throw error::Fatal("Unable to implant implementation object of size "
|
||||
"exceeding the pre-established storage buffer capacity. "
|
||||
+boost::lexical_cast<std::string>(sizeof(SUB)) + " > "
|
||||
+boost::lexical_cast<std::string>(maxSiz_)
|
||||
,error::LUMIERA_ERROR_CAPACITY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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<Base, sizeof(DD<42>), DD<0>> Buffer;
|
||||
using Buffer = InPlaceBuffer<Base, sizeof(DD<42>), 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<D42Sub> ("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<D42Sub> ("what the f**","is going on here?");
|
||||
|
||||
// subclass instance was indeed implanted into the opaque buffer
|
||||
buff->confess();
|
||||
|
||||
CHECK (6 == _create_count);
|
||||
|
|
|
|||
|
|
@ -33477,6 +33477,30 @@
|
|||
<node COLOR="#338800" CREATED="1619972797494" ID="ID_1468885512" MODIFIED="1619972821488" TEXT="nebenbei: Operation "create" → "emplace"">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1619976082135" ID="ID_416166359" MODIFIED="1619976677236" TEXT="Variante für placement-new einbauen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1619976099788" ID="ID_1451882006" MODIFIED="1619976684789" TEXT="Problem: DEFAULT-Typ und SIZE-Parameter in das Handle einbetten">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1619976624404" ID="ID_1394264630" MODIFIED="1619976674356" TEXT="DEFAULT-Typ kann als Typ-Parameter weiteregeben werden">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1619976641856" ID="ID_960415515" MODIFIED="1619976671474" TEXT="dann wird eben auch der Typ vom Handle komplexer">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1619976655262" ID="ID_411012847" MODIFIED="1619976666398" TEXT="dafür in InPlaceBuffer einen Typ-Konstruktor anbieten">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1619976688954" ID="ID_1451984496" MODIFIED="1619976700633" TEXT="nochmal über den Size-Parameter nachdenken...">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1619976702067" ID="ID_1433875229" MODIFIED="1619976734718" TEXT="kann man den dann auch zur Compile-Zeit weitergeben?">
|
||||
<icon BUILTIN="help"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1619976718142" ID="ID_210847961" MODIFIED="1619976734718" TEXT="wie sieht's mit Assignments aus?">
|
||||
<icon BUILTIN="help"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue