Proof Of Concept
Ok, inspired by UncleBens challenge here, I came up with a Proof-Of-Concept (see below) that let's you actually do:
srand(123);
for (int i=0; i<10; i++)
{
size_t N = rand() % DEMO_MAX; // capped for demo purposes
std::auto_ptr<iarray> dyn(make_dynamic_array(N));
exercise(*dyn);
}
It revolves around a template trick in factory<>::instantiate
that actually uses a compile-time meta-binary-search to match the specified (runtime) dimension to a range of explicit static_array
class template instantiations.
I feel the need to repeat that this is not good design, I provide the code sample only to show what the limits are of what can be done - with reasonable effor, to achieve the actual goal of the question. You can see the drawbacks:
- the compiler is crippled with a boatload of useless statical types and create classes that are so big that they become a performance liability or a reliability hazard (stack allocation anyone? -> we're on 'stack overflow' already :))
- at
DEMO_MAX = 256
, g++ -Os
will actually emit 258 instantiations of factory<>
; g++ -O4
will keep 74 of those, inlining the rest[2]
- compilation doesn't scale well: at
DEMO_MAX = MAX_RAND
compilation takes about 2m9s to... run out of memory on a 64-bit 8GB machine; at MAX_RAND>>16
it takes over 25 minutes to possibly compile (?) while nearly running out of memory. It would really require some amounts of ugly manual optimization to remove these limits - I haven't gone so insane as to actually do that work, if you'll excuse me.
- on the upside, this sample demonstrates the arguably sane range for this class (0..256) and compiles in only 4 seconds and 800Kb on my 64-bit linux. See also a down-scaled, ANSI-proof version at codepad.org
[2] established that with objdump -Ct test | grep instantiate | cut -c62- | sort -k1.10n
Show me the CODE already!
#include <iostream>
#include <memory>
#include <algorithm>
#include <iterator>
#include <stdexcept>
struct iarray
{
typedef int value_type;
typedef value_type* iterator;
typedef value_type const* const_iterator;
typedef value_type& reference;
typedef value_type const& const_reference;
virtual size_t size() const = 0;
virtual iterator begin() = 0;
virtual const_iterator begin() const = 0;
// completely unoptimized plumbing just for demonstration purps here
inline iterator end() { return begin()+size(); }
inline const_iterator end() const { return begin()+size(); }
// boundary checking would be 'gratis' here... for compile-time constant values of 'index'
inline const_reference operator[](size_t index) const { return *(begin()+index); }
inline reference operator[](size_t index) { return *(begin()+index); }
//
virtual ~iarray() {}
};
template <size_t N> struct static_array : iarray
{
static const size_t _size = N;
value_type data[N];
virtual size_t size() const { return _size; }
virtual iterator begin() { return data; }
virtual const_iterator begin() const { return data; }
};
#define DEMO_MAX 256
template <size_t PIVOT=DEMO_MAX/2, size_t MIN=0, size_t MAX=DEMO_MAX>
struct factory
/* this does a binary search in a range of static types
*
* due to the binary search, this will require at most 2log(MAX) levels of
* recursions.
*
* If the parameter (size_t n) is a compile time constant expression,
* together with automatic inlining, the compiler will be able to optimize
* this all the way to simply returning
*
* new static_array<n>()
*
* TODO static assert MIN<=PIVOT<=MAX
*/
{
inline static iarray* instantiate(size_t n)
{
if (n>MAX || n<MIN)
throw std::range_error("unsupported size");
if (n==PIVOT)
return new static_array<PIVOT>();
if (n>PIVOT)
return factory<(PIVOT + (MAX-PIVOT+1)/2), PIVOT+1, MAX>::instantiate(n);
else
return factory<(PIVOT - (PIVOT-MIN+1)/2), MIN, PIVOT-1>::instantiate(n);
}
};
iarray* make_dynamic_array(size_t n)
{
return factory<>::instantiate(n);
}
void exercise(iarray& arr)
{
int gen = 0;
for (iarray::iterator it=arr.begin(); it!=arr.end(); ++it)
*it = (gen+=arr.size());
std::cout << "size " << arr.size() << ":\t";
std::copy(arr.begin(), arr.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << std::endl;
}
int main()
{
{ // boring, oldfashioned method
static_array<5> i5;
static_array<17> i17;
exercise(i5);
exercise(i17);
}
{ // exciting, newfangled, useless method
for (int n=0; n<=DEMO_MAX; ++n)
{
std::auto_ptr<iarray> dyn(make_dynamic_array(n));
exercise(*dyn);
}
try { make_dynamic_array(-1); } catch (std::range_error e) { std::cout << "range error OK" << std::endl; }
try { make_dynamic_array(DEMO_MAX + 1); } catch (std::range_error e) { std::cout << "range error OK" << std::endl; }
return 0;
srand(123);
for (int i=0; i<10; i++)
{
size_t N = rand() % DEMO_MAX; // capped for demo purposes
std::auto_ptr<iarray> dyn(make_dynamic_array(N));
exercise(*dyn);
}
}
return 0;
}