From 6f9e15aff82a3596177c3fe26cd64a2e028e5aac Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Fri, 7 Feb 2014 14:49:21 +0100 Subject: [PATCH] base: Add 'construct_at' utility This utility allows for the manual placement of objects without the need to have a global placement new operation nor the need for type-specific new operators. Issue #989 --- base/include/util/construct_at.h | 84 +++++++++++++++++++++++++++++ os/include/util/volatile_object.h | 18 +------ os/run/volatile_object.run | 5 ++ os/src/test/volatile_object/main.cc | 40 ++++++++++++++ 4 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 base/include/util/construct_at.h diff --git a/base/include/util/construct_at.h b/base/include/util/construct_at.h new file mode 100644 index 0000000000..57e83d2b42 --- /dev/null +++ b/base/include/util/construct_at.h @@ -0,0 +1,84 @@ +/* + * \brief Utility for the manual placement of objects + * \author Norman Feske + * \date 2014-02-07 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__CONSTRUCT_AT_H_ +#define _INCLUDE__UTIL__CONSTRUCT_AT_H_ + +#include +#include + +namespace Genode { + template + static inline T *construct_at(void *, ARGS &&...); +} + + +/** + * Construct object of given type at a specific location + * + * \param T object type + * \param at desired object location + * \param args list of arguments for the object constructor + * + * \return typed object pointer + * + * We use move semantics (ARGS &&) because otherwise the compiler would create + * a temporary copy of all arguments that have a reference type and use a + * reference to this copy instead of the original within this function. + * + * There is a slight difference between the object that is constructed by this + * function and a common object of the given type. If the destructor of the + * given type or of any base of the given type is virtual, the vtable of the + * returned object references an empty delete(void *) operator for that + * destructor. However, this shouldn't be a problem as an object constructed by + * this function should never get destructed implicitely or through a delete + * expression. + */ +template +static inline T * Genode::construct_at(void *at, ARGS &&... args) +{ + /** + * Utility to equip an existing type 'T' with a placement new operator + */ + struct Placeable : T + { + Placeable(ARGS &&... args) : T(args...) { } + + void * operator new (size_t, void *ptr) { return ptr; } + void operator delete (void *, void *) { } + + /** + * Standard delete operator + * + * As we explicitely define one version of the delete operator, the + * compiler won't implicitely define any delete version for this class. + * But if type T has a virtual destructor, the compiler implicitely + * defines a 'virtual ~Placeable()' which needs the following operator. + */ + void operator delete (void *) + { + PERR("cxx: Placeable::operator delete (void *) not supported."); + } + }; + + /* + * If the args input to this function contains rvalues, the compiler would + * use the according rvalue references as lvalues at the following call if + * we don't cast them back to rvalue references explicitely. We can not use + * lvalues here because the compiler can not bind them to rvalue references + * as expected by Placeable. + */ + return new (at) Placeable(static_cast(args)...); +} + +#endif /* _INCLUDE__UTIL__CONSTRUCT_AT_H_ */ diff --git a/os/include/util/volatile_object.h b/os/include/util/volatile_object.h index f80ae84a5b..50213380bb 100644 --- a/os/include/util/volatile_object.h +++ b/os/include/util/volatile_object.h @@ -14,6 +14,7 @@ #ifndef _INCLUDE__UTIL__VOLATILE_OBJECT_H_ #define _INCLUDE__UTIL__VOLATILE_OBJECT_H_ +#include #include #include @@ -39,21 +40,6 @@ class Genode::Volatile_object { private: - /** - * Utility to equip an existing type 'T' with a placement new operator - */ - template - struct Placeable : T - { - template - Placeable(ARGS &&... args) - : - T(args...) - { } - - void *operator new (size_t, void *ptr) { return ptr; } - }; - /** * Static reservation of memory for the embedded object */ @@ -66,7 +52,7 @@ class Genode::Volatile_object template void _do_construct(ARGS &&... args) { - new (_space) Placeable(args...); + construct_at(_space, args...); _constructed = true; } diff --git a/os/run/volatile_object.run b/os/run/volatile_object.run index 8bec6c38c5..c5e06aa712 100644 --- a/os/run/volatile_object.run +++ b/os/run/volatile_object.run @@ -59,6 +59,11 @@ compare_output_to { [init -> test-volatile_object] destruct Member_with_reference [init -> test-volatile_object] destruct Object 2 [init -> test-volatile_object] destruct Object 1 +[init -> test-volatile_object] -- construct Throwing object +[init -> test-volatile_object] construct Throwing -> don't throw +[init -> test-volatile_object] destruct Throwing +[init -> test-volatile_object] construct Throwing -> throw exception +[init -> test-volatile_object] -- catched exception as expected [init -> test-volatile_object] --- test-volatile_object finished --- } diff --git a/os/src/test/volatile_object/main.cc b/os/src/test/volatile_object/main.cc index 72a7119398..3e658bdc68 100644 --- a/os/src/test/volatile_object/main.cc +++ b/os/src/test/volatile_object/main.cc @@ -75,6 +75,35 @@ struct Compound }; +struct Bool +{ + bool b; + + Bool(Bool const &) = delete; + + Bool(bool const &b) : b(b) { } +}; + + +struct Throwing +{ + Throwing(Bool const &throws) + { + if (throws.b) { + PLOG("construct Throwing -> throw exception"); + throw -1; + } else { + PLOG("construct Throwing -> don't throw"); + } + } + + virtual ~Throwing() + { + PLOG("destruct Throwing"); + } +}; + + static void call_const_method(Compound const &compound) { compound.member->reference.const_method(); @@ -125,6 +154,17 @@ int main(int, char **) printf("-- destruct Compound and Objects 1 and 2 --\n"); } + try { + printf("-- construct Throwing object\n"); + Bool const b_false(false), b_true(true); + + Volatile_object inst(b_false); + inst.construct(b_true); + PERR("expected contructor to throw"); + } catch (int i) { + printf("-- catched exception as expected\n"); + } + printf("--- test-volatile_object finished ---\n"); return 0;