Last active
September 16, 2016 11:46
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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