lumiera_/tests/library/opaque-unchecked-buffer-test.cpp
Ichthyostega 5a37bce855 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.
2021-05-02 19:40:11 +02:00

184 lines
4.8 KiB
C++

/*
OpaqueUncheckedBuffer(Test) - passive inline buffer test
Copyright (C) Lumiera.org
2009, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
/** @file opaque-unchecked-buffer-test.cpp
** unit test \ref OpaqueUncheckedBuffer_test
*/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/nocopy.hpp"
#include "lib/util.hpp"
#include "lib/opaque-holder.hpp"
#include <iostream>
#include <cstring>
namespace lib {
namespace test{
using ::Test;
using util::min;
using lumiera::error::LUMIERA_ERROR_FATAL;
using util::NonCopyable;
using std::strlen;
using std::cout;
using std::endl;
namespace { // test dummy hierarchy
// Note: vtable (and virtual dtor), but varying storage requirements
long _checksum = 0;
uint _create_count = 0;
struct Base
: NonCopyable
{
uint id_;
Base(uint i) : id_(i) { ++_create_count; _checksum += id_; }
virtual ~Base() { }
virtual void confess() =0;
};
template<uint ii>
struct DD : Base
{
char buff_[ii+1];
~DD() { _checksum -= ii; } // verify the correct dtor is called...
DD(Symbol sym = 0)
: Base(ii)
{
memset (&buff_, '*', ii);
if (sym)
memcpy (&buff_, sym, min (ii, strlen (sym)));
buff_[ii] = 0;
}
void
confess ()
{
cout << "DD<" << ii << ">: " << buff_ << endl;
}
};
struct D42Sub
: DD<42>
{
D42Sub (string s1, string s2)
: DD<42> ((s1+' '+s2).c_str())
{ }
void
confess ()
{
cout << "I'm special, " << buff_ << endl;
}
};
struct Killer
: DD<23>
{
Killer () { throw lumiera::error::Fatal ("crisscross"); }
};
/** maximum additional storage maybe wasted
* due to alignment of the contained object
* within OpaqueHolder's buffer
*/
const size_t _ALIGNMENT_OVERHEAD_ = sizeof(size_t);
}
/******************************************************************************//**
* @test use an inline buffer to place objects of a subclass, without any checks.
* InPlaceBuffer only provides minimal service, to be covered here,
* including automatic dtor invocation and smart-ptr style access.
*/
class OpaqueUncheckedBuffer_test : public Test
{
virtual void
run (Arg)
{
_checksum = 0;
_create_count = 0;
{
using Buffer = InPlaceBuffer<Base, sizeof(DD<42>), DD<0>>;
Buffer buff;
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
buff.create<DD<5>>();
buff->confess();
buff.create<DD<9>> ("I'm fine");
buff->confess();
VERIFY_ERROR( FATAL, buff.create<Killer> () );
CHECK(0 == buff->id_); // default object was created, due to exception...
// 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);
CHECK (42 == _checksum); // No.42 is alive
}
CHECK (0 == _checksum); // all dead
}
};
LAUNCHER (OpaqueUncheckedBuffer_test, "unit common");
}} // namespace lib::test