There\'re cases when a library source is available, and it has to support variable parameters in general, but in practice these parameters are commonly constants.
Th
If you can pass in a template parameter then it is guaranteed to be a constexpr (the Standard's term for compile-time expressions). If it's not passed by template parameter, then it's not a constexpr. There is no way around this.
What would be much easier is to hand-roll a stack allocated variable length array class using alloca. This will guarantee stack allocation for arrays, regardless of whether or not they're static or not. In addition, you can get much of the same iteration functionality of a vector/boost::array.
#define MAKE_VLA(type, identifier, size) VLA< (type) > identifier ( alloca( (size) * sizeof ( type ) ), (size) );
template<typename T> class VLA {
int count;
T* memory;
VLA(const VLA& other);
public:
// Types
typedef T* pointer;
typedef T& reference;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef T value_type;
typedef std::size_t size_type;
class iterator {
mutable T* ptr;
iterator(T* newptr)
: ptr(newptr) {}
public:
iterator(const iterator& ref)
: ptr(ref.ptr) {}
operator pointer() { return ptr; }
operator const pointer() const { return ptr; }
reference operator*() { return *ptr; }
const reference operator*() const { return *ptr; }
pointer operator->() { return ptr; }
const pointer operator->() const { return ptr; }
iterator& operator=(const iterator& other) const {
ptr = iterator.ptr;
}
bool operator==(const iterator& other) {
return ptr == other.ptr;
}
bool operator!=(const iterator& other) {
return ptr != other.ptr;
}
iterator& operator++() const {
ptr++;
return *this;
}
iterator operator++(int) const {
iterator retval(ptr);
ptr++;
return retval;
}
iterator& operator--() const {
ptr--;
return *this;
}
iterator operator--(int) const {
iterator retval(ptr);
ptr--;
return retval;
}
iterator operator+(int x) const {
return iterator(&ptr[x]);
}
iterator operator-(int x) const {
return iterator(&ptr[-x]);
}
};
typedef const iterator const_iterator;
class reverse_iterator {
mutable T* ptr;
reverse_iterator(T* newptr)
: ptr(newptr) {}
public:
reverse_iterator(const reverse_iterator& ref)
: ptr(ref.ptr) {}
operator pointer() { return ptr; }
operator const pointer() const { return ptr; }
reference operator*() { return *ptr; }
const reference operator*() const { return *ptr; }
pointer operator->() { return ptr; }
const pointer operator->() const { return ptr; }
reverse_iterator& operator=(const reverse_iterator& other) const {
ptr = reverse_iterator.ptr;
}
bool operator==(const reverse_iterator& other) {
return ptr == other.ptr;
}
bool operator!=(const reverse_iterator& other) {
return ptr != other.ptr;
}
reverse_iterator& operator++() const {
ptr--;
return *this;
}
reverse_iterator operator++(int) const {
reverse_iterator retval(ptr);
ptr--;
return retval;
}
reverse_iterator& operator--() const {
ptr++;
return *this;
}
reverse_iterator operator--(int) const {
reverse_iterator retval(ptr);
ptr++;
return retval;
}
reverse_iterator operator+(int x) const {
return reverse_iterator(&ptr[-x]);
}
reverse_iterator operator-(int x) const {
return reverse_iterator(&ptr[x]);
}
};
typedef const reverse_iterator const_reverse_iterator;
typedef unsigned int difference_type;
// Functions
~VLA() {
for(int i = 0; i < count; i++)
memory[i].~T();
}
VLA(void* stackmemory, int size)
: memory((T*)stackmemory), count(size) {
for(int i = 0; i < count; i++)
new (&memory[i]) T();
}
reference at(size_type pos) {
return (reference)memory[pos];
}
const_reference at(size_type pos) {
return (const reference)memory[pos];
}
reference back() {
return (reference)memory[count - 1];
}
const_reference back() const {
return (const reference)memory[count - 1];
}
iterator begin() {
return iterator(memory);
}
const_iterator begin() const {
return iterator(memory);
}
const_iterator cbegin() const {
return begin();
}
const_iterator cend() const {
return end();
}
const_reverse_iterator crbegin() const {
return rbegin();
}
const_reverse_iterator crend() const {
return rend();
}
pointer data() {
return memory;
}
const_pointer data() const {
return memory;
}
iterator end() {
return iterator(&memory[count]);
}
const_iterator end() const {
return iterator(&memory[count]);
}
reference front() {
return memory[0];
}
const_reference front() const {
return memory[0];
}
reverse_iterator rbegin() {
return reverse_iterator(&memory[count - 1]);
}
const_reverse_iterator rbegin() const {
return const_reverse_iterator(&memory[count - 1]);
}
reverse_iterator rend() {
return reverse_iterator(memory[-1]);
}
const_reverse_iterator rend() const {
return reverse_iterator(memory[-1]);
}
size_type size() {
return count;
}
reference operator[](int index) {
return memory[index];
}
const reference operator[](int index) const {
return memory[index];
}
};
Note that I haven't actually tested this code, but it would be MUCH easier to grab, use, and maintain than to maintain that monstrosity in your OP.
If you're working with GCC, use __builtin_constant_p
to tell you whether something is a compile time constant. The documentation includes examples like
static const int table[] = {
__builtin_constant_p (EXPRESSION) ? (EXPRESSION) : -1,
/* ... */
};
is_const should be more reliable. On gcc-4.4 for example, the following:
int k=0;
printf("%d\n",is_const(k),is_const(k>0));
prints:
0,1
GCC is quite ambitious folding constant expressions which are not integral constant expressions by the words of the standard. A potentially better definition of is_const could be:
#define is_const(B)\
(sizeof(chkconst::chk2(0+!!(B))) != sizeof(chkconst::chk2(0+!(B))))
Aside from that, your technique is awesome, because I can finally write a SUPER_ASSERT macro which is checked during compilation if the assertion expression if compile-time and during runtime otherwise:
#define SUPER_ASSERT(X) {BOOST_STATIC_ASSERT(const_switch_uint(X,1));assert(X);}
I'll look into that const_switch_xxx() thing later. I have no idea how to implement another way, the deconstruct/reconstruct trick is brilliant.