Skip to content

Instantly share code, notes, and snippets.

@eruffaldi
Last active September 16, 2016 11:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eruffaldi/9d4433459a81562a09603edc29264a9e to your computer and use it in GitHub Desktop.
Save eruffaldi/9d4433459a81562a09603edc29264a9e to your computer and use it in GitHub Desktop.
stack_array and hstack_array for examples of stack arrays based respectively on alloca and pre-allocated size
/**
* A little exercise on stack_array
*
* FOUND (too late) https://tlzprgmr.wordpress.com/2008/04/02/c-how-to-create-variable-length-arrays-on-the-stack/
* - desctruction removal
* - note about parameter issue
* - alignment management
* - generic construction
* - hybrid version using a fixed buffer Q and then heap
*
* ALTERNATIVE: array with pre-allocated size (template given) when exceeding uses vector: somewhere
*/
#include <type_traits>
#include <algorithm>
#include <iostream>
#include <memory>
#include <iterator>
#ifndef MSVC
#include <alloca.h>
#define xalloca alloca
#else
#include <malloc.h>
#define xalloca _alloca
#endif
// ignore if trivial
template<class T,
typename std::enable_if<std::is_trivially_destructible<T>::value,int>::type = 0>
void deletespan(T* t,int size)
{
}
template<class T,
typename std::enable_if<
!std::is_trivially_destructible<T>{} &&
(std::is_class<T>{} || std::is_union<T>{}),
int>::type = 0>
void deletespan(T* data, int size)
{
for(int i = size-1; i >= 0; i--)
data[i].~T();
}
/// array allocated on the stack
template <class T, int Q = 10>
class hstack_array
{
public:
using iterator = T*;
using const_iterator = const T*;
explicit hstack_array(int n);
//explicit hstack_array(int n, const T & value);
~hstack_array();
T & operator [] (int i) { return data_[i]; }
T * data() { return data_; }
std::size_t size() const { return size_; }
std::size_t capacity() const { return size_; }
iterator begin() { return data_; }
iterator end() { return data_+size_; }
const_iterator cbegin() const { return data_; }
const_iterator cend() const { return data_+size_; }
private:
hstack_array & operator=(const hstack_array & x) = delete;
hstack_array(const hstack_array & x) = delete;
alignas(T) unsigned char xdata_[sizeof(T)*Q]; // buffer aligned to hold Q T entities
// std::vector<T> instead of this
std::size_t size_;
T * data_ = 0;
};
template <class T, int Q>
hstack_array<T,Q>::hstack_array(int n) : size_(n)
{
if(n > Q)
{
data_ = new T[n];
}
else
{
data_ = new (xdata_) T[n];
}
}
#if 0
template <class T, int Q>
hstack_array<T,Q>::hstack_array(int n, const T & value) : size_(n)
{
if(n > Q)
{
data_ = new T[n](value);
}
else
{
data_ = new (xdata_) T[n](value);
}
}
#endif
template <class T,int Q>
hstack_array<T,Q>::~hstack_array()
{
if(size_ > Q)
{
delete[] data_;
}
else
{
deletespan(data_,size_);
}
}
/// array allocated on the stack
template <class T>
class stack_array
{
public:
using iterator = T*;
using const_iterator = const T*;
explicit stack_array(void * p, int n);
~stack_array();
T & operator [] (int i) { return data_[i]; }
T * data() { return data_; }
std::size_t size() const { return size_; }
std::size_t capacity() const { return size_; }
iterator begin() { return data_; }
iterator end() { return data_+size_; }
const_iterator cbegin() const { return data_; }
const_iterator cend() const { return data_+size_; }
static std::size_t needed(int n) { return sizeof(T)*n+alignof(T)-sizeof(T); }
private:
stack_array & operator=(const stack_array & x) = delete;
stack_array(const stack_array & x) = delete;
std::size_t size_;
T * data_;
};
/*
ATTENTION - possible issue with this
On many systems alloca() cannot be used inside the list of arguments
of a function call, because the stack space reserved by alloca()
would appear on the stack in the middle of the space for the function
arguments.
The use of C99 variable-length arrays and alloca() in the same function will cause the lifetime of alloca's
storage to be limited to the block containing the alloca()!!!
*/
#define STACK_NEW(T,size) xalloca(stack_array<T>::needed(size)),size
template <class T>
stack_array<T>::stack_array(void * p, int n) : size_(n)
{
if(sizeof(T) != alignof(T))
{
std::size_t q = needed(n);
std::align(sizeof(T),alignof(T),p,q);
}
data_ = new (p) T[size_];
}
template <class T>
stack_array<T>::~stack_array()
{
deletespan(data_,size_);
}
//---- TESTING ----
#include <iostream>
struct X
{
X()
{
std::cout << "me " <<this << std::endl;
}
~X()
{
std::cout << "free " <<this << std::endl;
}
};
void fx(int w)
{
stack_array<X> q(STACK_NEW(X,w));
stack_array<double> q2(STACK_NEW(double,w));
std::fill(q2.begin(),q2.end(),10);
std::cout << "filled: ";
std::copy(q2.begin(),q2.end(),std::ostream_iterator<double>(std::cout," "));
std::cout << "\n";
q2[w-2] = 20;
auto p = std::find(q2.begin(),q2.end(),20);
std::cout << "created " << q2.size() << " at " << q2.data() << " stack elements " << " " << *p << std::endl;
}
void hfx(int w)
{
hstack_array<X> q(w);
hstack_array<double> q2(w);
std::fill(q2.begin(),q2.end(),10);
std::cout << "filled: ";
std::copy(q2.begin(),q2.end(),std::ostream_iterator<double>(std::cout," "));
std::cout << "\n";
q2[w-2] = 20;
auto p = std::find(q2.begin(),q2.end(),20);
std::cout << "created " << q2.size() << " at " << q2.data() << " stack elements " << " " << *p << std::endl;
}
int main(int argc, char const *argv[])
{
fx(10);
hfx(10);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment