unique_ptr's of zero size - c++

I often work with multi-dimensional arrays, and I am not a big fan of std::vector, since it is not possible to instantiate a std::vector or a std::vector of std::vector's using a reference without copying the underlying data.
For one-dimensional arrays, I use the following
template<typename T>
using deleted_aligned_array = std::unique_ptr<T[], std::function<void(T*)> >;
template<typename T>
deleted_aligned_array<T> deleted_aligned_array_create(size_t n) {
return deleted_aligned_array<T>((T*)_mm_malloc(n*sizeof(T),16), [](T* f)->void { _mm_free(f);});
}
This is very convenient and allows me to instantiate a dynamically sized array, which also works for a size of zero. Further, I can use std::forward to pass on the data without copying.
For a two-dimensional array, I would like to do something like
template<typename T>
using deleted_aligned_array2 = std::unique_ptr<T*,std::function<void(T**)>>;
template<typename T>
deleted_aligned_array2<T> deleted_aligned_array_create(size_t m, size_t n) {
auto arr = deleted_aligned_array2(new T*[m](), [&](T** x) {
if (malloc_usable_size(x) > 0) {
_mm_free(&(x[0][0]));
}
delete[] x;});
if (m*n > 0) {
arr.get()[0] = (T*) _mm_malloc(m*n*sizeof(T),16);
// Row pointers
for (size_t iRow = 1; iRow < m; iRow++) {
(m_data.get())[iRow] = &(m_data.get()[0][iRow*n]);
}
}
return arr;
}
It works for zero-size arrays, but I get an error from valgrind for obvious reasons, invalid read of size 8.
Is it possible to solve this in an elegant way, without creating an entire class keeping a std::unique_ptr member, where I implement move-constructor, move-assignment etc. Ultimately, I would like to generalize this to be used for any dimension
template<typename T, size_t Dim>
deleted_aligned_array<T,D> deleted_aligned_array_create(...);
The returned array should be a unique pointer with row pointer recursively initialized and it should support zero-size arrays, e.g.
auto arr = deleted_aligned_array_create<float,3>(4,5,10);
should return a 3-dimensional array with row and column pointers.
Issues:
1) Avoid reading invalid data in a simple way.
2) Use a template parameter D for generating the types: T*, T** and simply passing on D to code recursively generating row pointers (this I already have).
3) Preferably in a portable way. malloc_usable_size is a GNU extension and calling it on x results in an invalid read, when the size is 0.
Thanks in advance

I sort of found a solution but it is not very elegant. If you have a more elegant solution, please post your answer. The solution here is pretty ugly, once we get to higher dimensions.
template <class T, size_t D>
class deleted_aligned_multi_array {
};
template <class T>
class deleted_aligned_multi_array<T,1> : public std::unique_ptr<T[], std::function<void(T*)> > {
deleted_aligned_multi_array(size_t n) :
std::unique_ptr<T[], std::function<void(T*)> >((T*)_mm_malloc(n*sizeof(T),16),
[](T* f)->void { _mm_free(f);}) {}
};
template <class T>
class deleted_aligned_multi_array<T,2> {
public:
typedef T** pointer;
typedef std::unique_ptr<T*, std::function<void(T**)>> deleted_unique_array;
deleted_aligned_multi_array() : m(0), n(0), data() {}
deleted_aligned_multi_array(size_t m, size_t n) : m(m), n(n) {
if (m*n > 0) {
data = deleted_unique_array(new T*[m](),
[&](T** x) {
if (sps::msize(x) > 0) {
_mm_free(&(x[0][0]));
}
delete[] x;});
data.get()[0] = (T*) _mm_malloc(m*n*sizeof(T),16);
for (size_t iRow = 1; iRow < m; iRow++) {
(data.get())[iRow] = &(data.get()[0][iRow*n]);
}
}
else {
data.reset();
}
}
deleted_aligned_multi_array(deleted_aligned_multi_array&& other) : m(other.m), n(other.n),
data(std::move(other.data)) {}
deleted_aligned_multi_array& operator=( deleted_aligned_multi_array&& other ) {
if (this != &other) {
data = std::move( other.data );
m = other.m;
m = other.n;
}
return *this;
}
T& operator()(size_t i, size_t j) {
return this->data.get()[0][i*n + j];
}
T* operator[](size_t m) {
return &(data.get()[m][0]);
}
const T* operator[](size_t m) const {
return data.get()[m];
}
pointer get() const {
return data.get();
}
void reset(pointer __p = pointer()) {
data.reset(__p);
}
template<typename _Up>
void reset(_Up) = delete;
private:
deleted_aligned_multi_array(const deleted_aligned_multi_array& other) = delete;
deleted_aligned_multi_array& operator=( const deleted_aligned_multi_array& a ) = delete;
public:
size_t m; ///<Number of rows
size_t n; ///<Number of columns
deleted_unique_array data; ///<Data
};
A utility function for accessing a sub array, can now easily be made
template <class T>
std::unique_ptr<T*, std::function<void(T*)> sub_array(size_t m, size_t n, size_t i, size_t j) {
// Establish row pointers with reference i and j and dimension mxn.
}

Related

Variable dimension dynamic array in C++

I would like to implement a STL-like tensor template in C++, which needs overload operator[] to access its elements, which may looks like this:
Tensor<double,2> tensor2
{
{1,2,3},
{4,5,6}
};
std::cout<< tensor2[1][2]<<'\n';
For low dimensions, it may be easy to use some thing like
std::vector<std::vector<int>>
But this would not be easy to use for higher dimensions.
Also I would like to implement product between tensors and contraction between dimensions in one tensor.
I implemented the multi-dimensional array with some very basic operations. Since Boost.MultiArray offers a far better solution, my implementation is just for demonstration.
namespace DataStructures
{
struct ExtentList //Discribe extents of dimensions for MultiArray
{
std::vector<size_t> dimensions;
ExtentList() = default;
template<typename Iterator, typename SFINAE = std::enable_if_t<std::_Is_iterator_v<Iterator>>>
ExtentList(Iterator begin, Iterator end) : dimensions(begin, end) {}
//operator[] used to initialize the extents
ExtentList& operator[](size_t n) { dimensions.push_back(n); return *this; }
ExtentList after_front() const { return ExtentList(++dimensions.cbegin(), dimensions.cend()); }
//at() used to access extents
size_t at(size_t n) const { return dimensions.at(n); }
};
static ExtentList Extents;
template<
typename ElemType, //Underlying Type
size_t Dimension, //Dimension of MultiArray
typename ElementAllocator = std::allocator<ElemType>, //Allocator for elements
template<typename, typename> typename ContainerType = std::vector, //Underlying container type
template<typename> typename ContainerAllocator = std::allocator> //Allocator for container
class MultiArray
{
//Necessary for contructor with ExtentList
friend class MultiArray<ElemType, Dimension + 1U, ElementAllocator, ContainerType, ContainerAllocator>;
using value_type = typename
std::conditional_t<
Dimension == 1U,
ElemType,
MultiArray<ElemType, Dimension - 1U, ElementAllocator, ContainerType, ContainerAllocator>>;
using allocator_type = typename
std::conditional_t<
Dimension == 1U,
ElementAllocator,
ContainerAllocator<value_type>>;
ContainerType<value_type, allocator_type> data;
public:
MultiArray() = default;
MultiArray(size_t n, const value_type& val) : data(n, val) {}
template<typename SFINAE = std::enable_if_t<(Dimension == 1U)>>
MultiArray(ExtentList extents, const ElemType& elem) : data(extents.at(0), elem) {}
template<typename SFINAE = std::enable_if_t<(Dimension >= 2U)>, typename SFINAE2 = SFINAE>
MultiArray(ExtentList extents, const ElemType& elem) : data(extents.at(0), value_type(extents.after_front(), elem)) {}
MultiArray(std::initializer_list<value_type> ilist) : data(ilist) {}
template<typename ... SizeType>
MultiArray(size_t N, SizeType... args) : data(N, value_type(args...)) {}
value_type& operator[](size_t n) { return data[n]; }
void push_back(const value_type& elem) { data.push_back(elem); }
};
}
The idea is to implement the multi-dimensional array with recursive template, so subscription and list initialization operations are allowed.
namespace DS = DataStructures;
DS::MultiArray<int, 2> matrix_2d
{
{ 1,2,3 },
{ 4,5,6 },
{ 7,8,9 }
};
for (size_t i = 0; i != 3; ++i)
for (size_t j = 0; j != 3; ++j)
std::cout << matrix_2d[i][j] << ' ';
DS::MultiArray<int, 3> matrix_3d(DS::Extents[10][10][10], 0);
size_t sum = 0;
for (size_t i = 0; i != DS::Extents.at(0); ++i)
for (size_t j = 0; j != DS::Extents.at(1); ++j)
for (size_t k = 0; k != DS::Extents.at(2); ++k)
sum += (matrix_3d[i][j][k] = i * 100 + j * 10 + k);
std::cout << sum << '\n' << matrix_3d[9][9][9] << '\n';
The idea of ExtentList comes from Boost. It is better than variable function parameter list or variable template.

Is it Possible to Use Casting as "array slicing" in C++11

I have some shared memory populated by specialized hardware. It's declared as an array of structs, like:
struct port {
int data[10];
char port_id[8];
}
struct bus {
port ports[5];
char bus_id[8];
}
struct bus busses[10];
I'm (re)learning C++, and wanted to use C++11's ranged for loops to iterate over the data.
HOWEVER: That last dimension of the array (data[10]), I only care about the first 4 elements. Is there a way to take a slice of the data and use it in the for() statement?
Like
for (auto & b : busses) {
for (auto & p : bus.ports) {
for (auto & d : port.data[0 through 3]) {
store_the_address_of_d_for_use_elsewhere(d);
}
}
}
Is there a way to use a cast in the innermost for loop, so that it appears like there's only 4 elements? The address of the data is important because I'm going to refer directly to it later using pointers.
This is probably one of those times when a good old-fashioned for (int i = 0; i < 4; i++) is your best bet.
Don't overthink it, and don't try to use "new" features just for the sake of it, creating more complexity and more work in the process.
template<class T>
struct array_view {
T* b = 0;
T* e = 0;
T* begin() const { return b; }
T* end() const { return e; }
std::size_t size() const { return end()-begin(); }
T& front() const { return *begin(); }
T& back() const { return *(end()-1); }
// basic constructors:
array_view(T* s, T* f):b(s), e(f) {}
array_view(T* s, std::size_t N):array_view(s, s+N) {}
// default ctors: (no need for move)
array_view()=default;
array_view(array_view const&)=default;
array_view& operator=(array_view const&)=default;
// advanced constructors:
template<class U>
using is_compatible = std::integral_constant<bool,
std::is_same<U, T*>{} || std::is_same<U, T const*>{} ||
std::is_same<U, T volatile*>{} || std::is_same<U, T volatile const*>{}
>;
// this one consumes containers with a compatible .data():
template<class C,
typename std::enable_if<is_compatible< decltype(std::declval<C&>().data()) >{}, int>::type = 0
>
array_view( C&& c ): array_view( c.data(), c.size() ) {}
// this one consumes compatible arrays:
template<class U, std::size_t N,
typename std::enable_if<is_compatible< U* >{}, int>::type = 0
>
array_view( U(&arr)[N] ):
array_view( arr, N )
{}
// create a modified view:
array_view without_front( std::size_t N = 1 ) const {
return {begin()+(std::min)(size(), N), end()};
}
array_view without_back( std::size_t N = 1 ) const {
return {begin(), end()-(std::min)(size(), N)};
}
array_view only_front( std::size_t N = 1 ) const {
return {begin(), begin()+(std::min)(size(), N)};
}
array_view only_back( std::size_t N = 1 ) const {
return {end()-(std::min)(size(), N), end()};
}
};
Now some functions that let you easily create it:
template<class T, std::size_t N>
array_view<T> array_view_of( T(&arr)[N] ) {
return arr;
}
template<class C,
class Data = decltype( std::declval<C&>().data() ),
class T = typename std::remove_pointer<Data>::type
>
array_view<T> array_view_of( C&& c ) {
return std::forward<C>(c);
}
template<class T>
array_view<T> array_view_of( T* s, std::size_t N ) {
return {s, N};
}
template<class T>
array_view<T> array_view_of( T* s, T* e ) {
return {s, e};
}
and we are done the boilerplate part.
for (auto & b : bus) {
for (auto & p : bus.port) {
for (auto & d : array_view_of(bus.data).only_front(4)) {
store_the_address_of_d_for_use_elsewhere(d);
}
}
}
live example
Now I would only advocate this approach because array_view is shockingly useful in many different applications. Writing it just for this case is silly.
Note that the above array_view is a multiply-iterated class; I've written it here before. This one is, in my opinion, better than the previous ones, other than the annoying c++11-isms I had to use.
for (auto & d : reinterpret_cast<int (&)[4]>(p))

Overloading operator [] for N-dimensional structure

I made an N-dimensional structure with vectors and templates:
//----------------N-dimensional vector--------------------------------
template<int dim,typename T> class n_dim_vector {
public:
typedef std::vector<typename n_dim_vector<dim - 1, T>::vector> vector;
};
template<typename T> class n_dim_vector <0, T> {
public:
typedef T vector;
};
It can be instatiaated with different dimnsion-counts and is prt of a class that represent a search space.
template<int dim, typename T> class n_dim_ssc {
private:
typename n_dim_vector<dim, T>::vector searchspace;
};
My problem: I cannot get operator[] right to access searchspace properly, specifically the return type.
I tried:
template<typename V> std::vector<V>& operator[](unsigned i) {
return searchspace[i];
}
T& operator[](unsigned i) {
return searchspace[i];
}
at first, thinking the compiler would derive typename V as whatever type searchspace contained at all but the last level. Thats what T& operator[](unsigned i) was for.
But alas, doen't work this way. And I cannot work out how it would
EDIT Don't fear, I do not access empty memory, the structure is initialized and filled, I just didn't include the code for clarity's sake.
Also, I don't intend to access it with a single integer, I wanted to use searchspace[i][j]..[k]
The way to let compiler deduces the return type is auto:
In C++14:
auto operator[](unsigned i) { return searchspace[i]; }
In C++11:
auto operator[](unsigned i) -> decltype(searchspace[i]) { return searchspace[i]; }
I'm answering to your comment
Feel free to recommend something better, I'd appreciate it.
The following code shows one way to handle the multidimensional vector at once, i.e. non-recursively. It could be improved in several ways which I didn't consider for now (for instance, I wouldn't want to use and pass that many arrays but rather use variadic parameter lists. This however requires much more and more diffcult code, so I'll let it be.)
#include <numeric>
template<size_t Dim, typename T>
struct MultiDimVector
{
std::array<size_t, Dim> Ndim;
std::array<size_t, Dim> stride;
std::vector<T> container;
MultiDimVector(std::array<size_t, Dim> const& _Ndim) : Ndim(_Ndim), container(size())
{
stride[0] = 1;
for (size_t i = 1; i<Dim; ++i)
{
stride[i] = stride[i - 1] * Ndim[i - 1];
}
}
size_t size() const
{
return std::accumulate(Ndim.begin(), Ndim.end(), 1, std::multiplies<size_t>());
}
size_t get_index(std::array<size_t, Dim> const& indices) const
{
//here one could also use some STL algorithm ...
size_t ret = 0;
for (size_t i = 0; i<Dim; ++i)
{
ret += stride[i] * indices[i];
}
return ret;
}
T const& operator()(std::array<size_t, Dim> const& indices) const
{
return container[get_index(indices)];
}
};
You can use it like
MultiDimVector<3, double> v({ 3, 2, 5 }); //initialize vector of dimension 3x2x5
auto a = v({0,1,0}); //get element 0,1,0
But as I wrote, the curly brackets suck, so I'd rewrite the whole thing using variadic templates.
The problem with your approach is that you're not initializing any memory inside the vector and just trying to return non-existent memory spots. Something on the line of the following (WARNING: uncleaned and unrefactored code ahead):
#include <iostream>
#include <vector>
template<int dim,typename T> class n_dim_vector {
public:
typedef std::vector<typename n_dim_vector<dim - 1, T>::vector> vector;
};
template<typename T> class n_dim_vector <0, T> {
public:
typedef T vector;
};
template<int dim, typename T> class n_dim_ssc {
public:
typename n_dim_vector<dim, T>::vector searchspace;
n_dim_ssc() {}
n_dim_ssc(typename n_dim_vector<dim, T>::vector space) : searchspace(space) {}
n_dim_ssc<dim-1, T> operator[](std::size_t i) {
if(searchspace.size() < ++i)
searchspace.resize(i);
return n_dim_ssc<dim-1, T>(searchspace[--i]);
}
typename n_dim_vector<dim, T>::vector get() {
return searchspace;
}
};
template<typename T> class n_dim_ssc<0,T> {
public:
typename n_dim_vector<0, T>::vector searchspace;
n_dim_ssc() {}
n_dim_ssc(typename n_dim_vector<0, T>::vector space) : searchspace(space) {}
typename n_dim_vector<0, T>::vector get() {
return searchspace;
}
};
int main(int argc, char** argv) {
n_dim_ssc<0, int> ea;
int a = ea.get();
n_dim_ssc<1, int> ea2;
auto dd2 = ea2[0].get();
n_dim_ssc<2, int> ea3;
auto dd3 = ea3[0][0].get();
}
Try it out
will work with an accessor method (you can modify this as you want).
Anyway I strongly have to agree with Kerrek: a contiguous memory space accessed in a multi-dimensional array fashion will both prove to be faster and definitely more maintainable/easier to use and read.

Appropriate data structure for 8 dimensional object [duplicate]

I'd like to write an n-dimensional histogram class. It should be in the form of bins that contains other bins etc. where each bin contains min and max range, and a pointer to the next dimension bins
a bin is defined like
template<typename T>
class Bin {
float minRange, maxRange;
vector<Bin<either Bin or ObjectType>> bins;
}
This definition is recursive. So in run time the user defines the dimension of the histogram
so if its just 1-dimension, then
Bin<Obj>
while 3-dimensions
Bin<Bin<Bin<Obj>>>
Is this possible?
Regards
Certainly, C++11 has variable length parameter lists for templates. Even without C++11 you can use specialisation, if all your dimensions have the same type:
template <typename T, unsigned nest>
struct Bin {
std::vector<Bin<T, (nest-1)> > bins;
};
template <typename T>
struct Bin<T,0> {
T content;
};
You can only specify the dimension at runtime to a certain degree. If it is bound by a fixed value you can select the appropriate type even dynamically. However, consider using a one-dimensional vector instead of a multi-dimensional jagged vector!
To get the exact syntax you proposed, do:
template <typename T>
class Bin
{
float minRange, maxRange;
std::vector<T> bins;
};
And it should do exactly what you put in your question:
Bin< Bin< Bin<Obj> > > bins;
To do it dynamically (at runtime), I employed some polymorphism. The example is a bit complex. First, there is a base type.
template <typename T>
class BinNode {
public:
virtual ~BinNode () {}
typedef std::shared_ptr< BinNode<T> > Ptr;
virtual T * is_object () { return 0; }
virtual const T * is_object () const { return 0; }
virtual Bin<T> * is_vector() { return 0; }
const T & operator = (const T &t);
BinNode<T> & operator[] (unsigned i);
};
BinNode figures out if the node is actually another vector, or the object.
template <typename T>
class BinObj : public BinNode<T> {
T obj;
public:
T * is_object () { return &obj; }
const T * is_object () const { return &obj; }
};
BinObj inherits from BinNode, and represents the object itself.
template <typename T>
class Bin : public BinNode<T> {
typedef typename BinNode<T>::Ptr Ptr;
typedef std::map<unsigned, std::shared_ptr<BinNode<T> > > Vec;
const unsigned dimension;
Vec vec;
public:
Bin (unsigned d) : dimension(d) {}
Bin<T> * is_vector() { return this; }
BinNode<T> & operator[] (unsigned i);
};
Bin is the vector of BinNode's.
template <typename T>
inline const T & BinNode<T>::operator = (const T &t) {
if (!is_object()) throw 0;
return *is_object() = t;
}
Allows assignment to a BinNode if it is actually the object;
template <typename T>
BinNode<T> & BinNode<T>::operator[] (unsigned i) {
if (!is_vector()) throw 0;
return (*is_vector())[i];
}
Allows the BinNode to be indexed if it is a vector.
template <typename T>
inline BinNode<T> & Bin<T>::operator[] (unsigned i)
{
if (vec.find(i) != vec.end()) return *vec[i];
if (dimension > 1) vec[i] = Ptr(new Bin<T>(dimension-1));
else vec[i] = Ptr(new BinObj<T>);
return *vec[i];
}
Returns the indexed item if it is present, otherwise creates the appropriate entry, depending on the current dimension depth. Adding a redirection operator for pretty printing:
template <typename T>
std::ostream &
operator << (std::ostream &os, const BinNode<T> &n) {
if (n.is_object()) return os << *(n.is_object());
return os << "node:" << &n;
}
Then you can use Bin like this:
int dim = 3;
Bin<float> v(dim);
v[0][1][2] = 3.14;
std::cout << v[0][1][2] << std::endl;
It doesn't currently support 0 dimension, but I invite you to try to do it yourself.

Boost Pool maximum size

I am using boost pool as a static memory provider,
void func()
{
std::vector<int, boost::pool_allocator<int> > v;
for (int i = 0; i < 10000; ++i)
v.push_back(13);
}
In above code, how we can fix the size of pool, i mean as we know boost::pool provide as a static memory allocator, but i am not able to fix the size of this pool, its keep growing, there should be way to restrict its size.
for example i want a pool of 200 chunks only so i can take 200 chunks after that it should though NULL
please let me now how to do this
I don't think boost pool provides what you want. Actually there are 4 other template parameters for boost::pool_allocator except the type of object:
UserAllocator: Defines the method that the underlying Pool will use to allocate memory from the system(default = boost::default_user_allocator_new_delete).
Mutex: Allows the user to determine the type of synchronization to be used on the underlying singleton_pool(default = boost::details::pool::default_mutex).
NextSize: The value of this parameter is passed to the underlying Pool when it is created and specifies the number of chunks to allocate in the first allocation request (default = 32).
MaxSize: The value of this parameter is passed to the underlying Pool when it is created and specifies the maximum number of chunks to allocate in any single allocation request (default = 0).
You may think MaxSize is exactly what you want, but unfortunately it's not.
boost::pool_allocator uses a underlying boost::singleton_pool which is based on an boost::pool, the MaxSize will eventually pass to the data member of boost::pool<>: max_size, so what role does the max_size play in the boost::pool? let's have a look at boost::pool::malloc():
void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
{ //! Allocates a chunk of memory. Searches in the list of memory blocks
//! for a block that has a free chunk, and returns that free chunk if found.
//! Otherwise, creates a new memory block, adds its free list to pool's free list,
//! \returns a free chunk from that block.
//! If a new memory block cannot be allocated, returns 0. Amortized O(1).
// Look for a non-empty storage
if (!store().empty())
return (store().malloc)();
return malloc_need_resize();
}
Obviously, boost::pool immediately allocates a new memory block if no free chunk available in the memory block. Let's continue to dig into the malloc_need_resize():
template <typename UserAllocator>
void * pool<UserAllocator>::malloc_need_resize()
{ //! No memory in any of our storages; make a new storage,
//! Allocates chunk in newly malloc aftert resize.
//! \returns pointer to chunk.
size_type partition_size = alloc_size();
size_type POD_size = static_cast<size_type>(next_size * partition_size +
math::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
char * ptr = (UserAllocator::malloc)(POD_size);
if (ptr == 0)
{
if(next_size > 4)
{
next_size >>= 1;
partition_size = alloc_size();
POD_size = static_cast<size_type>(next_size * partition_size +
math::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
ptr = (UserAllocator::malloc)(POD_size);
}
if(ptr == 0)
return 0;
}
const details::PODptr<size_type> node(ptr, POD_size);
BOOST_USING_STD_MIN();
if(!max_size)
next_size <<= 1;
else if( next_size*partition_size/requested_size < max_size)
next_size = min BOOST_PREVENT_MACRO_SUBSTITUTION(next_size << 1, max_size*requested_size/ partition_size);
// initialize it,
store().add_block(node.begin(), node.element_size(), partition_size);
// insert it into the list,
node.next(list);
list = node;
// and return a chunk from it.
return (store().malloc)();
}
As we can see from the source code, max_size is just related to the number of chunks to request from the system next time, we can only slow down the speed of increasing via this parameter.
But notice that we can defines the method that the underlying pool will use to allocate memory from the system, if we restrict the size of memory allocated from system, the pool's size wouldn't keep growing. In this way, boost::pool seems superfluous, you can pass the custom allocator to STL container directly. Here is a example of custom allocator(based on this link) which allocates memory from stack up to a given size:
#include <cassert>
#include <iostream>
#include <vector>
#include <new>
template <std::size_t N>
class arena
{
static const std::size_t alignment = 8;
alignas(alignment) char buf_[N];
char* ptr_;
bool
pointer_in_buffer(char* p) noexcept
{ return buf_ <= p && p <= buf_ + N; }
public:
arena() noexcept : ptr_(buf_) {}
~arena() { ptr_ = nullptr; }
arena(const arena&) = delete;
arena& operator=(const arena&) = delete;
char* allocate(std::size_t n);
void deallocate(char* p, std::size_t n) noexcept;
static constexpr std::size_t size() { return N; }
std::size_t used() const { return static_cast<std::size_t>(ptr_ - buf_); }
void reset() { ptr_ = buf_; }
};
template <std::size_t N>
char*
arena<N>::allocate(std::size_t n)
{
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
if (buf_ + N - ptr_ >= n)
{
char* r = ptr_;
ptr_ += n;
return r;
}
std::cout << "no memory available!\n";
return NULL;
}
template <std::size_t N>
void
arena<N>::deallocate(char* p, std::size_t n) noexcept
{
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
if (pointer_in_buffer(p))
{
if (p + n == ptr_)
ptr_ = p;
}
}
template <class T, std::size_t N>
class short_alloc
{
arena<N>& a_;
public:
typedef T value_type;
public:
template <class _Up> struct rebind { typedef short_alloc<_Up, N> other; };
short_alloc(arena<N>& a) noexcept : a_(a) {}
template <class U>
short_alloc(const short_alloc<U, N>& a) noexcept
: a_(a.a_) {}
short_alloc(const short_alloc&) = default;
short_alloc& operator=(const short_alloc&) = delete;
T* allocate(std::size_t n)
{
return reinterpret_cast<T*>(a_.allocate(n*sizeof(T)));
}
void deallocate(T* p, std::size_t n) noexcept
{
a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T));
}
template <class T1, std::size_t N1, class U, std::size_t M>
friend
bool
operator==(const short_alloc<T1, N1>& x, const short_alloc<U, M>& y) noexcept;
template <class U, std::size_t M> friend class short_alloc;
};
template <class T, std::size_t N, class U, std::size_t M>
inline
bool
operator==(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept
{
return N == M && &x.a_ == &y.a_;
}
template <class T, std::size_t N, class U, std::size_t M>
inline
bool
operator!=(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept
{
return !(x == y);
}
int main()
{
const unsigned N = 1024;
typedef short_alloc<int, N> Alloc;
typedef std::vector<int, Alloc> SmallVector;
arena<N> a;
SmallVector v{ Alloc(a) };
for (int i = 0; i < 400; ++i)
{
v.push_back(10);
}
}