The fundamental decision is that we want to have a single generic front-end, meaning that we must jump dynamically into a configured deleter function. And on top of that comes the additional requirement that ''some allocators'' are in fact tied to a specific instance, while other allocators are monostate. However, we can distinguish both by probing if the allocator can be default constructed, and if a default constructed allocator is equivalent to the currently used alloctor instance. If this test fails, we must indeed maintain a single allocator instance, and (to avoid overengineering for this rather special use case) we will place this allocator instance into heap memory then, with a self-cleanup mechanism On the other hand, all monostate allocators can be handled through static trapolines.
160 lines
4.4 KiB
C++
160 lines
4.4 KiB
C++
/*
|
|
SEVERAL.hpp - abstraction providing a limited fixed number of elements
|
|
|
|
Copyright (C) Lumiera.org
|
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
|
2024, 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 several.hpp
|
|
** Abstraction interface: array-like access by subscript
|
|
** @todo as of 2016, this concept seems very questionable: do we _really_ want
|
|
** to abstract over random access, or do we _actually_ want for-iteration??
|
|
** @warning WIP-WIP-WIP in rework 5/2025
|
|
** @see several-builder.hpp
|
|
*/
|
|
|
|
|
|
#ifndef LIB_SEVERAL_H
|
|
#define LIB_SEVERAL_H
|
|
|
|
|
|
#include "lib/nocopy.hpp"
|
|
#include "lib/iter-adapter.hpp"
|
|
|
|
#include <cstddef>
|
|
|
|
|
|
namespace lib {
|
|
|
|
namespace {// Storage implementation details
|
|
|
|
template<class I>
|
|
struct ArrayBucket
|
|
{
|
|
union Manager
|
|
{
|
|
typedef void (*Deleter) (void*, size_t, size_t);
|
|
|
|
bool unmanaged :1;
|
|
Deleter deleter;
|
|
};
|
|
|
|
Manager manager;
|
|
size_t spread;
|
|
|
|
/** mark start of the storage area */
|
|
alignas(I)
|
|
std::byte storage[sizeof(I)];
|
|
|
|
/** perform unchecked access into the storage area
|
|
* @note typically reaching behind the nominal end of this object
|
|
*/
|
|
I&
|
|
subscript (size_t idx)
|
|
{
|
|
std::byte* elm = storage;
|
|
size_t off = idx * spread;
|
|
elm += off;
|
|
return * std::launder (reinterpret_cast<I*> (elm));
|
|
}
|
|
};
|
|
|
|
}//(End)implementation details
|
|
|
|
|
|
|
|
/************************************************//**
|
|
* Abstraction: Fixed array of elements.
|
|
* Typically the return type is an interface,
|
|
* and the Implementation wraps some datastructure
|
|
* holding subclasses.
|
|
* @warning in rework 5/2025
|
|
*/
|
|
template<class I>
|
|
class Several
|
|
// : util::MoveAssign ////////////////////////////////////////OOO fundamental design mismatch with intended builder usage!
|
|
{
|
|
protected:
|
|
using Bucket = ArrayBucket<I>*;
|
|
|
|
size_t size_{0};
|
|
Bucket data_{nullptr};
|
|
|
|
public:
|
|
~Several() noexcept
|
|
try { discardData(); }
|
|
ERROR_LOG_AND_IGNORE (progress, "clean-up Several data")
|
|
|
|
size_t
|
|
size() const
|
|
{
|
|
return size_;
|
|
}
|
|
|
|
bool
|
|
empty() const
|
|
{
|
|
return not (size_ and data_);
|
|
}
|
|
|
|
I&
|
|
operator[] (size_t idx)
|
|
{
|
|
REQUIRE (data_);
|
|
return data_->subscript (idx);
|
|
}
|
|
|
|
I& front() { return operator[] (size_-1); }
|
|
I& back() { return operator[] (0); }
|
|
|
|
using iterator = I*;
|
|
using const_iterator = I const*;
|
|
|
|
iterator begin() { UNIMPLEMENTED ("iteration"); }
|
|
iterator end() { UNIMPLEMENTED ("iteration"); }
|
|
const_iterator begin() const { UNIMPLEMENTED ("iteration"); }
|
|
const_iterator end() const { UNIMPLEMENTED ("iteration"); }
|
|
|
|
friend auto begin (Several const& svl) { return svl.begin();}
|
|
friend auto end (Several const& svl) { return svl.end(); }
|
|
|
|
private:
|
|
void
|
|
discardData()
|
|
{
|
|
if (data_ and not data_->manager.unmanaged)
|
|
{
|
|
if (data_->manager.deleter)
|
|
{
|
|
(*data_->manager.deleter) (data_, size_, data_->spread);
|
|
}
|
|
else
|
|
{
|
|
for (size_t i=0; i<size_; ++i)
|
|
operator [](i).~I();
|
|
////////////////////////////////////////////OOO really a good idea to invoke the std::allocator_traits directly here??
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
} // namespace lib
|
|
#endif
|