Library: Uninitialised-Storage array (see #1204)
Introduced as remedy for a long standing sloppiness: Using a `char[]` together with `reinterpret_cast` in storage management helpers bears danger of placing objects with wrong alignment; moreover, there are increasing risks that modern code optimisers miss the ''backdoor access'' and might apply too aggressive rewritings. With C++17, there is a standard conformant way to express such a usage scheme. * `lib::UninitialisedStorage` can now be used in a situation (e.g. as in `ExtentFamily`) where a complete block of storage is allocated once and then subsequently used to plant objects one by one * moreover, I went over the code base and adapted the most relevant usages of ''placement-new into buffer'' to also include the `std::launder()` marker
This commit is contained in:
parent
6707962bca
commit
c5679b0fd0
16 changed files with 247 additions and 48 deletions
|
|
@ -57,6 +57,7 @@
|
|||
|
||||
#include "lib/error.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <list>
|
||||
|
||||
|
|
@ -81,7 +82,8 @@ namespace lib {
|
|||
{
|
||||
struct Allocation
|
||||
{
|
||||
char buf_[sizeof(TY)];
|
||||
alignas(TY)
|
||||
std::byte buf_[sizeof(TY)];
|
||||
|
||||
template<typename...ARGS>
|
||||
TY&
|
||||
|
|
@ -93,7 +95,7 @@ namespace lib {
|
|||
TY&
|
||||
access()
|
||||
{
|
||||
return reinterpret_cast<TY&> (buf_);
|
||||
return * std::launder (reinterpret_cast<TY*> (&buf_));
|
||||
}
|
||||
void
|
||||
discard() /// @warning strong assumption made here: Payload was created
|
||||
|
|
|
|||
|
|
@ -209,9 +209,6 @@ namespace diff{
|
|||
{
|
||||
scopes_.emplace();
|
||||
TreeMutator::Handle placementHandle (scopes_.top());
|
||||
|
||||
static_assert (buffSiz >= sizeof(typename MutatorStack::value_type)
|
||||
,"insufficient working buffer for TreeMutator");
|
||||
return placementHandle;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@
|
|||
#include <boost/static_assert.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <iostream>
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
|
||||
|
||||
|
||||
|
|
@ -67,14 +69,14 @@ namespace util {
|
|||
namespace { // implementation details...
|
||||
|
||||
inline boost::format&
|
||||
accessImpl (char* buffer)
|
||||
accessImpl (std::byte* buffer)
|
||||
{
|
||||
return reinterpret_cast<boost::format&> (*buffer);
|
||||
return * std::launder (reinterpret_cast<boost::format*> (buffer));
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
destroyImpl (char* buffer)
|
||||
destroyImpl (std::byte* buffer)
|
||||
{
|
||||
accessImpl(buffer).~format();
|
||||
}
|
||||
|
|
@ -83,7 +85,7 @@ namespace util {
|
|||
/** in case the formatting of a (primitive) value fails,
|
||||
* we try to supply an error indicator instead */
|
||||
void
|
||||
pushFailsafeReplacement (char* formatter, const char* errorMsg =NULL)
|
||||
pushFailsafeReplacement (std::byte* formatter, const char* errorMsg =NULL)
|
||||
try {
|
||||
string placeholder("<Error");
|
||||
if (errorMsg){
|
||||
|
|
@ -98,7 +100,7 @@ namespace util {
|
|||
|
||||
|
||||
inline void
|
||||
suppressInsufficientArgumentErrors (char* formatter)
|
||||
suppressInsufficientArgumentErrors (std::byte* formatter)
|
||||
{
|
||||
using namespace boost::io;
|
||||
accessImpl(formatter).exceptions (all_error_bits ^ too_few_args_bit);
|
||||
|
|
|
|||
|
|
@ -159,11 +159,11 @@ namespace util {
|
|||
/** size of an opaque implementation Buffer */
|
||||
enum{ FORMATTER_SIZE = lib::meta::SizeTrait::BOOST_FORMAT };
|
||||
|
||||
typedef char Implementation[FORMATTER_SIZE];
|
||||
typedef std::byte Implementation[FORMATTER_SIZE];
|
||||
|
||||
|
||||
/** @internal buffer to hold a boost::format */
|
||||
mutable Implementation formatter_;
|
||||
alignas(size_t) mutable Implementation formatter_;
|
||||
|
||||
|
||||
/** call into the opaque implementation */
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@
|
|||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/uninitialised-storage.hpp"
|
||||
#include "lib/meta/duck-detector.hpp"
|
||||
#include "lib/meta/function.hpp"
|
||||
#include "lib/meta/trait.hpp"
|
||||
|
|
@ -867,13 +868,11 @@ namespace lib {
|
|||
using Group = std::array<RES, grp>;
|
||||
using Iter = typename Group::iterator;
|
||||
struct Buffer
|
||||
: lib::UninitialisedStorage<RES,grp>
|
||||
{
|
||||
char storage[sizeof(Group)];
|
||||
|
||||
Group& group() { return reinterpret_cast<Group&> (storage); }
|
||||
|
||||
Iter begin() { return group().begin();}
|
||||
Iter end() { return group().end(); }
|
||||
Group& group(){ return *this; }
|
||||
Iter begin() { return group().begin();}
|
||||
Iter end() { return group().end(); }
|
||||
};
|
||||
Buffer buff_;
|
||||
uint pos_{0};
|
||||
|
|
|
|||
|
|
@ -90,6 +90,25 @@ namespace meta {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Metafunction " max( alignof(T) ) for T in TYPES "
|
||||
*/
|
||||
template<class TYPES>
|
||||
struct maxAlign;
|
||||
template<>
|
||||
struct maxAlign<NullType>
|
||||
{
|
||||
static constexpr int value = 0;
|
||||
};
|
||||
template<class TY, class TYPES>
|
||||
struct maxAlign<Node<TY,TYPES>>
|
||||
{
|
||||
static constexpr size_t thisval = alignof(TY);
|
||||
static constexpr size_t nextval = maxAlign<TYPES>::value;
|
||||
static constexpr size_t value = nextval > thisval? nextval:thisval;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Metafunction to check if a specific type is contained
|
||||
* in a given typelist. Only exact match is detected.
|
||||
|
|
|
|||
|
|
@ -86,8 +86,9 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
|
@ -213,7 +214,8 @@ namespace lib {
|
|||
/** Inner capsule managing the contained object (interface) */
|
||||
struct Buffer
|
||||
{
|
||||
char content_[siz];
|
||||
alignas(size_t) std::byte content_[siz];
|
||||
|
||||
void* ptr() { return &content_; }
|
||||
|
||||
virtual ~Buffer() {} ///< this is an ABC with VTable
|
||||
|
|
@ -255,7 +257,7 @@ namespace lib {
|
|||
SUB&
|
||||
get() const ///< core operation: target is contained within the inline buffer
|
||||
{
|
||||
return *reinterpret_cast<SUB*> (unConst(this)->ptr());
|
||||
return * std::launder (reinterpret_cast<SUB*> (unConst(this)->ptr()));
|
||||
}
|
||||
|
||||
~Buff()
|
||||
|
|
@ -328,12 +330,12 @@ namespace lib {
|
|||
Buffer&
|
||||
buff()
|
||||
{
|
||||
return *reinterpret_cast<Buffer*> (&storage_);
|
||||
return * std::launder (reinterpret_cast<Buffer*> (&storage_));
|
||||
}
|
||||
const Buffer&
|
||||
buff() const
|
||||
{
|
||||
return *reinterpret_cast<const Buffer *> (&storage_);
|
||||
return * std::launder (reinterpret_cast<const Buffer *> (&storage_));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -614,13 +616,14 @@ namespace lib {
|
|||
: util::NonCopyable
|
||||
{
|
||||
|
||||
mutable char buf_[siz];
|
||||
alignas(BA) mutable
|
||||
std::byte buf_[siz];
|
||||
|
||||
|
||||
BA&
|
||||
getObj() const
|
||||
{
|
||||
return reinterpret_cast<BA&> (buf_);
|
||||
return * std::launder (reinterpret_cast<BA*> (&buf_));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -166,6 +166,9 @@
|
|||
#include "lib/meta/duck-detector.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
|
|
@ -383,12 +386,13 @@ namespace lib {
|
|||
/* === embedded object in buffer === */
|
||||
|
||||
/** Storage for embedded objects */
|
||||
mutable char buf_[siz];
|
||||
alignas(IFA) mutable
|
||||
std::byte buf_[siz];
|
||||
|
||||
IFA&
|
||||
accessEmbedded() const
|
||||
{
|
||||
return reinterpret_cast<IFA&> (buf_);
|
||||
return * std::launder (reinterpret_cast<IFA*> (&buf_));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
|
||||
|
||||
|
||||
|
|
@ -86,7 +87,8 @@ namespace wrapper {
|
|||
template<typename X, typename COND =void>
|
||||
class ReplaceableItem
|
||||
{
|
||||
char content_[sizeof(X)];
|
||||
alignas(X)
|
||||
std::byte content_[sizeof(X)];
|
||||
|
||||
public:
|
||||
ReplaceableItem()
|
||||
|
|
@ -131,7 +133,7 @@ namespace wrapper {
|
|||
X&
|
||||
access()
|
||||
{
|
||||
return reinterpret_cast<X&>(content_);
|
||||
return * std::launder (reinterpret_cast<X*> (&content_));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
#include "lib/meta/trait.hpp"
|
||||
#include "lib/iter-adapter.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
|
|
@ -113,15 +114,15 @@ namespace lib {
|
|||
class ElementHolder
|
||||
: util::NonCopyable
|
||||
{
|
||||
|
||||
mutable char buf_[siz];
|
||||
alignas(I) mutable
|
||||
std::byte buf_[siz];
|
||||
|
||||
public:
|
||||
|
||||
I&
|
||||
accessObj() const
|
||||
{
|
||||
return reinterpret_cast<I&> (buf_);
|
||||
return * std::launder (reinterpret_cast<I*> (&buf_));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
130
src/lib/uninitialised-storage.hpp
Normal file
130
src/lib/uninitialised-storage.hpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
UNINITIALISED-STORAGE.hpp - array-like container with raw storage
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2023, 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 uninitialised-storage.hpp
|
||||
** A raw memory block with proper alignment and array access.
|
||||
** This is a building block for custom containers and memory management schemes.
|
||||
** Regular containers in C++ always ensure invocation of any object's constructors
|
||||
** and destructors -- which is a boon and prevents a lot of consistency problems.
|
||||
** When constructing custom management schemes however, this automatic initialisation
|
||||
** can be problematic; some objects require constructor arguments, preventing mass
|
||||
** initialisation; and initialising a large memory block has considerable cost,
|
||||
** which may be wasted it the intended usage is to plant objects into that space
|
||||
** later, on demand. The std::vector::reserve(size) function can often be used
|
||||
** to circumvent those problems — yet unfortunately std::vector has the hard
|
||||
** requirement on the content objects to be at least _movable_ to support
|
||||
** automatic storage growth.
|
||||
**
|
||||
** A traditional workaround is to exploit a programming trick relying on a
|
||||
** _forced cast:_ Storage is declared as `char[]` without initialiser; since
|
||||
** `char` is a primitive type, C++ will not default-initialise the storage then
|
||||
** for sake of plain-old-C compatibility. A special accessor function will then
|
||||
** force-cast into the desired target type. However well established, this
|
||||
** practice is riddled with various problems
|
||||
** - it requires very precise setup and any mistake leads to memory corruption
|
||||
** - according to the C++ standard, such an access involves _undefined behaviour_
|
||||
** - compilers are getting better and increasingly aggressive with performing global
|
||||
** optimisation and may miss the secondary hidden access under some circumstances.
|
||||
** - when objects with `const` data fields are placed into such a buffer, the compiler
|
||||
** may perform constant folding based on the current/initial object values and thus
|
||||
** fail to propagate changes due to other object instances being placed into storage.
|
||||
**
|
||||
** Attempts were made to codify this kind of usage properly into the standard; these
|
||||
** turned out to be problematic however and were [deprecated again]. Starting with
|
||||
** C++17, fortunately there is now a way to express this kind of raw unprotected
|
||||
** access through standard conformant ways and marked clearly to prevent overly
|
||||
** zealous optimisation. Since the typical use is for allocating a series of
|
||||
** storage slots with a well defined target type, this implementation
|
||||
** relies on std::array as »front-end« for access.
|
||||
** - target type `T` and size are given as template parameters
|
||||
** - the storage is defined as `std::byte` to express its very purpose
|
||||
** - the secondary access is marked by `std::launder` to prevent optimisation
|
||||
** - an implicit conversion path to the corresponding array type is provided
|
||||
** - subscript operators enable direct access to the raw storage
|
||||
** - helper functions allow for placement new and delete.
|
||||
** [deprecated again]: https://stackoverflow.com/a/71828512
|
||||
** @see extent-family.hpp
|
||||
** @see vault::gear::TestChainLoad::Rule where this setup matters
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIB_UNINITIALISED_STORAGE_H
|
||||
#define LIB_UNINITIALISED_STORAGE_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <array>
|
||||
#include <new>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
/**
|
||||
* Block of raw uninitialised storage with array like access.
|
||||
* @tparam T the nominal type assumed to sit in each »slot«
|
||||
* @tparam cnt number of »slots« in the array
|
||||
*/
|
||||
template<typename T, size_t cnt>
|
||||
class UninitialisedStorage
|
||||
{
|
||||
using _Arr = std::array<T,cnt>;
|
||||
alignas(T) std::byte buffer_[sizeof(_Arr)];
|
||||
|
||||
public:
|
||||
_Arr&
|
||||
array()
|
||||
{
|
||||
return * std::launder (reinterpret_cast<_Arr* > (&buffer_));
|
||||
}
|
||||
|
||||
_Arr const&
|
||||
array() const
|
||||
{
|
||||
return * std::launder (reinterpret_cast<_Arr const*> (&buffer_));
|
||||
}
|
||||
|
||||
|
||||
operator _Arr&() { return array(); }
|
||||
operator _Arr const&() const { return array(); }
|
||||
|
||||
T & operator[] (size_t idx) { return array()[idx]; }
|
||||
T const& operator[] (size_t idx) const { return array()[idx]; }
|
||||
|
||||
|
||||
template<typename...Args>
|
||||
T&
|
||||
createAt (size_t idx, Args&& ...args)
|
||||
{
|
||||
return *new(&operator[](idx)) T{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
void
|
||||
destroyAt (size_t idx)
|
||||
{
|
||||
operator[](idx).~T();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif /*LIB_UNINITIALISED_STORAGE_H*/
|
||||
|
|
@ -88,6 +88,7 @@
|
|||
#include "lib/util.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -222,10 +223,11 @@ namespace lib {
|
|||
template<typename TYPES>
|
||||
class Variant
|
||||
{
|
||||
// WARNING: never add any member field before the storage_ array /////////////////////////TICKET #1204
|
||||
|
||||
public:
|
||||
enum { SIZ = meta::maxSize<typename TYPES::List>::value };
|
||||
enum { SIZ = meta::maxSize<typename TYPES::List>::value
|
||||
, ALIGN = meta::maxAlign<typename TYPES::List>::value
|
||||
};
|
||||
|
||||
template<typename RET>
|
||||
using VisitorFunc = typename variant::VFunc<RET>::template VisitorInterface<TYPES>;
|
||||
|
|
@ -264,7 +266,8 @@ namespace lib {
|
|||
struct Buffer
|
||||
: meta::VirtualCopySupportInterface<Buffer>
|
||||
{
|
||||
char content_[SIZ];
|
||||
alignas(ALIGN)
|
||||
std::byte content_[SIZ];
|
||||
|
||||
void* ptr() { return &content_; }
|
||||
|
||||
|
|
@ -287,7 +290,7 @@ namespace lib {
|
|||
TY&
|
||||
access() const ///< core operation: target is contained within the inline buffer
|
||||
{
|
||||
return *reinterpret_cast<TY*> (unConst(this)->ptr());
|
||||
return * std::launder (reinterpret_cast<TY*> (unConst(this)->ptr()));
|
||||
}
|
||||
|
||||
~Buff()
|
||||
|
|
@ -396,12 +399,12 @@ namespace lib {
|
|||
Buffer&
|
||||
buffer()
|
||||
{
|
||||
return *reinterpret_cast<Buffer*> (&storage_);
|
||||
return * std::launder (reinterpret_cast<Buffer*> (&storage_));
|
||||
}
|
||||
Buffer const&
|
||||
buffer() const
|
||||
{
|
||||
return *reinterpret_cast<const Buffer*> (&storage_);
|
||||
return * std::launder (reinterpret_cast<const Buffer*> (&storage_));
|
||||
}
|
||||
|
||||
template<typename X>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include "lib/util.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <cstddef>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
|
@ -113,8 +114,9 @@ namespace wrapper {
|
|||
using TY_unconst = std::remove_const_t<TY>;
|
||||
|
||||
|
||||
mutable
|
||||
char content_[sizeof(TY)];
|
||||
alignas(TY) mutable
|
||||
std::byte content_[sizeof(TY)];
|
||||
|
||||
bool created_;
|
||||
|
||||
template<typename REF>
|
||||
|
|
@ -135,7 +137,7 @@ namespace wrapper {
|
|||
TY&
|
||||
access () const
|
||||
{
|
||||
return reinterpret_cast<TY&> (content_);
|
||||
return * std::launder (reinterpret_cast<TY*> (&content_));
|
||||
}
|
||||
|
||||
TY_unconst&
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ namespace engine {
|
|||
throw error::Invalid ("Buffer with the given ID not yet emitted");
|
||||
|
||||
diagn::Block& memoryBlock = access_emitted (bufferID);
|
||||
TY* converted = reinterpret_cast<TY*> (memoryBlock.accessMemory());
|
||||
TY* converted = std::launder (reinterpret_cast<TY*> (memoryBlock.accessMemory()));
|
||||
|
||||
REQUIRE (converted);
|
||||
return *converted;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
|
||||
#include "vault/common.hpp"
|
||||
#include "lib/uninitialised-storage.hpp"
|
||||
#include "lib/iter-adapter.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
|
@ -90,16 +91,18 @@ namespace mem {
|
|||
|
||||
|
||||
private:
|
||||
using _UniqueStoragePtr = std::unique_ptr<lib::UninitialisedStorage<T,siz>>;
|
||||
|
||||
/** Entry in the Extents management datastructure */
|
||||
struct Storage
|
||||
: std::unique_ptr<char[]>
|
||||
: _UniqueStoragePtr
|
||||
{
|
||||
/**
|
||||
* @note default ctor immediately allocates the full storage,
|
||||
* but without initialisation since payload is `char`
|
||||
* but without any initialisation of memory content
|
||||
*/
|
||||
Storage()
|
||||
: unique_ptr{new char[sizeof(Extent)]}
|
||||
: _UniqueStoragePtr{new lib::UninitialisedStorage<T,siz>}
|
||||
{ }
|
||||
|
||||
/** access projected Extent storage type
|
||||
|
|
@ -108,8 +111,9 @@ namespace mem {
|
|||
Extent&
|
||||
access()
|
||||
{
|
||||
ENSURE (get() != nullptr);
|
||||
return reinterpret_cast<Extent&> (*get());
|
||||
auto* rawStorage = this->get();
|
||||
ENSURE (rawStorage != nullptr);
|
||||
return static_cast<Extent&> (rawStorage->array());
|
||||
}
|
||||
};
|
||||
using Extents = std::vector<Storage>;
|
||||
|
|
|
|||
|
|
@ -110692,7 +110692,7 @@ class Something
|
|||
<node CREATED="1570817565257" ID="ID_872374381" MODIFIED="1570817571504" TEXT="auf x86 harmlos"/>
|
||||
<node CREATED="1570817572097" ID="ID_942342414" MODIFIED="1570817577917" TEXT="aber nicht portabel"/>
|
||||
</node>
|
||||
<node CREATED="1570812031118" FOLDED="true" ID="ID_1018321072" MODIFIED="1570812037369" TEXT="Alignment bei placement-new">
|
||||
<node CREATED="1570812031118" FOLDED="true" ID="ID_1018321072" MODIFIED="1701555783331" TEXT="Alignment bei placement-new">
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1570812038974" ID="ID_716754107" MODIFIED="1570812068335" TEXT="der Buffer muß passendes Alignment für das Target aufweisen">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1570812070369" ID="ID_490799733" MODIFIED="1570812089910" TEXT="der Compiler kann das (meist/immer) nicht verifizieren">
|
||||
|
|
@ -110750,6 +110750,37 @@ class Something
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1701555943105" ID="ID_55328417" MODIFIED="1701559664023" TEXT="char[] durch aligned raw storage ablösen">
|
||||
<icon BUILTIN="bell"/>
|
||||
<node CREATED="1701555969695" ID="ID_1535597438" MODIFIED="1701556221036" TEXT="„char is calling for trouble“">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Da alignof(char) ≡ 1, ist es gradezu eine »Steilvorlage« für Probleme, wenn man einen Allocation-Buffer per char[] deklariert. GCC macht das nicht (er fängt Allokationen immer an 64bit-Grenzen an), aber grundsätzlich dürfte ein Compiler ein char[] anfangen, wo er grad' lustig ist. Besonders gefährlich, wenn das Array <i>in ein anderes Objekt eingebettet</i> wird. Nur den zuletzt genannten Fall habe ich 2019 abgeklärt; gegen alle sonstigen <i>Schnaps-Ideen </i>gibt es keinen Schutz — es sei denn, man hält sich an die Sprachmittel
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1701556228394" ID="ID_1444144444" LINK="https://en.cppreference.com/w/cpp/types/aligned_storage" MODIFIED="1701559705550" TEXT="es gäbe dafür ein Sprachmittel: std::aligned_storage">
|
||||
<node CREATED="1701556795662" ID="ID_1263844060" LINK="https://stackoverflow.com/a/71828512" MODIFIED="1701556817727" TEXT="wenngleich auch deprecated für C++23"/>
|
||||
<node CREATED="1701559709278" ID="ID_78471984" MODIFIED="1701559737569" TEXT="direkt durch ein std::byte[] ersetzen mit alignas(T)">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1701556464842" ID="ID_1873202439" MODIFIED="1701559743611" TEXT="versuchsweise: vault::mem::ExtentFamily umgestellt">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1701559745027" ID="ID_1331044905" MODIFIED="1701574720900" TEXT="lib::UninitialisedStorage bereitstellen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1701561920506" ID="ID_889775258" MODIFIED="1701561928182" TEXT="⟶ und für ExtentFamilty verwenden">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1690740456817" FOLDED="true" ID="ID_1080126518" MODIFIED="1690740564054" TEXT="#853 Portabilität">
|
||||
|
|
|
|||
Loading…
Reference in a new issue