I am looking for a gcc-supported C++ language extension to enable the allocation of non-zero-based array pointers. Ideally I could simply write:
#include
Assuming you're using gcc on linux x86-64, it supports the intptr_t
and uintptr_t
types which can hold any pointer value (valid or not) and also support integer arithmetic. uintptr_t
is more suitable in this application because it supports mod 2^64 semantics while intptr_t
has UB cases.
As suggested in comments, we can use this to build a class that overloads operator[]
and performs range checking:
#include
#include
#include // for ostringstream
#include // out_of_range
#include // uintptr_t
using namespace std;
// Safe non-zero-based array. Includes bounds checking.
template
class Array {
uintptr_t array; // base value for non-zero-based access
int lo; // lowest valid index
int hi; // highest valid index plus 1
public:
Array(int lo, int hi)
: array(), lo(lo), hi(hi)
{
if (lo > hi)
{
ostringstream msg; msg<<"Array(): lo("< hi("<= hi)
{
ostringstream msg;
msg << "Array.at(): " << i << " is not in range ["
<< lo << ", " << hi << "]";
throw out_of_range(msg.str());
}
return _at(i);
}
int get_lo() const {return lo;}
int get_hi() const {return hi;}
int size() const {return hi - lo;}
Elem& operator[](ptrdiff_t i){return at(i);}
// std::vector is wrong; operator[] is the typical use and should be safe.
// It's good practice to fix mistakes as we go along.
};
// Test
int main()
{
const int LO = 1000000000;
const int HI = LO + 10;
Array array(LO, HI);
for (int i=LO; i
Note that it is still not possible to cast the invalid "pointer" calculated by intptr_t
to a pointer type, due to GCC 4.7 Arrays and Pointers:
When casting from pointer to integer and back again, the resulting pointer must reference the same object as the original pointer, otherwise the behavior is undefined. That is, one may not use integer arithmetic to avoid the undefined behavior of pointer arithmetic as proscribed in C99 and C11 6.5.6/8.
This is why the array
field must be of type intptr_t
and not Elem*
. In other words, behavior is defined so long as the intptr_t
is adjusted to point back to the original object before converting back to Elem*
.