Sign in to follow this  
Opwiz

Compile-time array initialization

Recommended Posts

Is there a way to initialize an array of size N at compile time (where N is a constant integer specified as a template parameter)? E.g. if I wish the elements to be initialized to N^2/i (where i is the array index).

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
have a look at this: http://www.gamedev.net/community/forums/topic.asp?topic_id=252723&whichpage=1�

Share this post


Link to post
Share on other sites
Quote:
Original post by Opwiz
Is there a way to initialize an array of size N at compile time (where N is a constant integer specified as a template parameter)? E.g. if I wish the elements to be initialized to N^2/i (where i is the array index).


So you want the 0th element to hold infinity? ;)

Share this post


Link to post
Share on other sites
This is what I've managed to do:


template<int N>
class ClassWithStaticArray
{
public:
void PrintElems()
{
std::cout << dummy << std::endl;
for(int i = 0; i < N; ++i)
{
std::cout << i << ": " << elem[i] << std::endl;
}
}

private:
template<int D>
static int Init()
{
return elem[D] = Init<D - 1>() - 1;
}

template<>
static int Init<0>()
{
return elem[0] = N;
}

static int elem[N];
static const int dummy;
};

template<int N>
int ClassWithStaticArray<N>::elem[N];

template<int N>
const int ClassWithStaticArray<N>::dummy = Init<N>();


I have to use a static array but that happens to be what I need so that is not really a problem. The static array is initialized at run-time but only if the "dummy" variable is referenced. I want it to be generated at compile-time, or atleast without having to reference a dummy variable. Any ideas?

Share this post


Link to post
Share on other sites
Slightly better:


template<int N>
class ClassWithStaticArray
{
public:
void PrintElems()
{
for(int i = 0; i < N; ++i)
{
std::cout << i << ": " << ar[i] << std::endl;
}
}

private:
template<int D>
static int& Init()
{
return elem[D] = Init<D - 1>() - 1;
}

template<>
static int& Init<0>()
{
return elem[0] = N;
}

static int elem[N];
static int* ar;
};

template<int N>
int ClassWithStaticArray<N>::elem[N];

template<int N>
int* ClassWithStaticArray<N>::ar = &Init<N>() - N;


Whenever I need to reference the array I use the pointer "ar" that gets initialized when first used. Still generated at run-time though. A cleaner run-time solution would be to initialize the array at the constructor and use a flag to ensure that it only gets initialized once.

Share this post


Link to post
Share on other sites
this is both, horrible and beautiful ;) but works for me, to use it simply substitue the fib meta function template.


#include <cstdio>
#include <cstdlib>

template <template<unsigned> class F, unsigned N>
class value_array
{
template <template<unsigned> class F, unsigned N>
class value_holder
{
value_holder<F,N-1> base;
const unsigned val;
public:
value_holder(void): val( F<N>::val){}
};

template <template<unsigned> class F>
class value_holder<F,0>
{
const unsigned val;
public:
value_holder(void): val( F<0>::val){}
};
static const value_holder<F,N> values;
public:
static const unsigned size = N;
unsigned operator[](unsigned n){ return *(reinterpret_cast<const unsigned*>( &values)+n);}
};

template <template<unsigned> class F, unsigned N>
const typename value_array<F,N>::value_holder<F,N> value_array<F,N>::values;

//need a test "function"
template <unsigned N>
struct fib
{
enum {val = fib<N-1>::val + fib<N-2>::val};
};

template <>
struct fib<0>{ enum {val = 1};};

template <>
struct fib<1>{ enum {val = 1};};

int main(void)
{
static value_array<fib, 10> fibo;
for(unsigned n = 0; n != 10; ++n)
printf("%d ", fibo[n]);
puts("");
system("pause");
}


looking at the asm output for it you can find this gem

_DATA SEGMENT
?values@?$value_array@Ufib@@$09@@0V?$value_holder@Ufib@@$09@1@B DD 01H ; value_array<fib,10>::values
DD 01H
DD 02H
DD 03H
DD 05H
DD 08H
DD 0dH
DD 015H
DD 022H
DD 037H
DD 059H
_DATA ENDS


that's compile values =)

and yes, Im insane...

Share this post


Link to post
Share on other sites
Have you thought about code generation? It is probably the simplest solution...


// gen_array_cpp.cpp
#include <iostream>
#include <fstream>

int main(int argc, char* argv[])
{
if(argc<2)
{
std::cerr << "Usage: " << argv[0] << " <array size>" << std::endl;
return 1;
}

size_t array_size = atoi(argv[1]);

std::ofstream array_h("array.h");
if(!array_h)

array_h << "// AUTOMATICALLY GENERATED FILE, DO NOT MODIFY.\n"
<< "#ifndef INCLUDE_ARRAY_H\n"
<< "#define INCLUDE_ARRAY_H\n"
<< std::endl;;
array_h << "extern int array[" << array_size << "];" << std::endl;
array_h << "#endif // INCLUDE_ARRAY_H" << std::endl;
array_h.close();

std::ofstream array_cpp("array.cpp");
if(!array_cpp)
{
std::cerr << "Could not open file array.cpp" << std::endl;
return 1;
}

array_cpp << "// AUTOMATICALLY GENERATED FILE, DO NOT MODIFY.\n"
array_cpp << "int array[" << array_size << "] = {" << std::endl;
for(size_t i=0; i<array_size; ++i)
array_cpp << " " << array_size*array_size / (i+1) << "," << std::endl;
array_cpp << "};" << std::endl;
array_cpp.close();
}


or in python


import sys

try:
array_size = int(sys.argv[1])
except:
print >> sys.stderr, "Usage: %s <array size>" % sys.argv[0]
exit(1)

array_h = file("array.h","w")
print >> array_h, "// AUTOMATICALLY GENERATED FILE, DO NOT MODIFY."
print >> array_h, "#ifndef INCLUDE_ARRAY_H"
print >> array_h, "#define INCLUDE_ARRAY_H"
print >> array_h
print >> array_h, "extern int array[%d];" % array_size
print >> array_h
print >> array_h, "#endif // INCLUDE_ARRAY_H"
array_h.close()

array_cpp = file("array.cpp", "w")
print >> array_cpp, "// AUTOMATICALLY GENERATED FILE, DO NOT MODIFY."
print >> array_cpp
print >> array_cpp, "array[%d] = {" % array_size
for i in xrange(array_size):
print >> array_cpp, " %d," % (array_size**2 / (i+1))
print >> array_cpp, "};"
array_cpp.close()


You add those programs to your build process, and use the files they generate as source files in your project. Simple, maintainable, and not relying on any complex language tricks that may or may not be supported by your compiler.

Share this post


Link to post
Share on other sites
Quote:
this is both, horrible and beautiful ;) but works for me, to use it simply substitue the fib meta function template.

I'm amazed it compiles :) very nice. Your example worked fine. Got internal compiler error though when I tried to implement it inside my class (my table value algorithm needs to use a template parameter specified for the class so I can't put the algorithm outside the class).

Share this post


Link to post
Share on other sites
Quote:
Have you thought about code generation? It is probably the simplest solution...

It has crossed my mind.. But my first thought was that it would be complicated and hard to maintain. You would have to change the input parameters to the code generator each time you change the size of the static array. Also if you plan to use several instances of the static array (initialized differently depending on some template parameters) it becomes impossible to implement with code generation.

Share this post


Link to post
Share on other sites
Quote:
Original post by Opwiz
It has crossed my mind.. But my first thought was that it would be complicated and hard to maintain.


It's not that bad, really. :)


Quote:
You would have to change the input parameters to the code generator each time you change the size of the static array.


It can be done via a command-line parameter to the code generator. If it's part of your build process, it'll be transparent.

Quote:
Also if you plan to use several instances of the static array (initialized differently depending on some template parameters) it becomes impossible to implement with code generation.


Ok, it does get a bit more complicated, though still feasible ;)

Share this post


Link to post
Share on other sites
Quote:
Sounds odd, would you mind posting code for that Im curious.

This generates
fatal error C1001: INTERNAL COMPILER ERROR
(compiler file 'msc1.cpp', line 2701)

I'm using MSVC++ .NET 7.1


template<unsigned T>
class CompilerErrorExample
{
public:
CompilerErrorExample()
{
for(unsigned i = 0; i < T; ++i)
{
std::cout << i << ": " << fibo[i] << std::endl;
}
}

~CompilerErrorExample()
{
}

template<unsigned N>
struct fib
{
enum {val = fib<N-1>::val + fib<N-2>::val};
};

template <>
struct fib<0>{ enum {val = 1};};

template <>
struct fib<1>{ enum {val = 1};};

static value_array<fib, T> fibo;
};

template<unsigned T>
value_array<CompilerErrorExample<T>::fib, T> CompilerErrorExample<T>::fibo;

int main(void)
{
CompilerErrorExample<10> e;
return 0;
}


I'm not sure if the declaration of fibo is correct but the compiler generates internal error with or without.

[Edit: corrected code]

Share this post


Link to post
Share on other sites
Since msvc crapped out on you, here are the errors gcc gives me:

$ g++ er.cc -o er
er.cc:26: error: explicit specialization in non-namespace scope `class
CompilerErrorExample<T>'
er.cc:26: error: enclosing class templates are not explicitly specialized
er.cc:27: error: template parameters not used in partial specialization:
er.cc:27: error: `T'
er.cc:29: error: explicit specialization in non-namespace scope `class
CompilerErrorExample<T>'
er.cc:29: error: enclosing class templates are not explicitly specialized
er.cc:30: error: template parameters not used in partial specialization:
er.cc:30: error: `T'
er.cc:32: error: ISO C++ forbids declaration of `value_array' with no type
er.cc:32: error: template-id `value_array<template<unsigned int T>
template<unsigned int N> struct CompilerErrorExample<T>::fib, T>' used as a
declarator
er.cc:32: error: parse error before `;' token
er.cc:37: error: syntax error before `<' token
er.cc:37: error: `T' was not declared in this scope
er.cc:37: error: template argument 1 is invalid
er.cc:37: error: ISO C++ forbids declaration of `fibo' with no type
er.cc: In constructor `CompilerErrorExample<T>::CompilerErrorExample() [with
unsigned int T = 10]':
er.cc:41: instantiated from here
er.cc:12: error: invalid types `int[unsigned int]' for array subscript

This might help find out what's wrong.

Share this post


Link to post
Share on other sites
gcc says:

foo.cc:25: error: explicit specialization in non-namespace scope `class
CompilerErrorExample<T>'
foo.cc:25: error: enclosing class templates are not explicitly specialized
foo.cc:26: error: template parameters not used in partial specialization:
foo.cc:26: error: `T'


idem on 28/29.

It doesn't like your nested specializations. Take fib out of CompilerErrorExample.

Share this post


Link to post
Share on other sites
After going head to head with this for a while using both VC7.1 and GCC3.2 I've come to the conclusion that they simply don't like when you try to use a template inside another template, even got GCC to segfault a couple of times.

But I also realized what the simple solution to the problem is, create a new version of value_array, let's call it value_array2 that takes as parameter a templte taking two parameters, the first being what ever you need to pass from your class and the second being the usual N. That way you can move the generative template outside and still get the param you need from the class.

I got that working without much hassle and it works on both GCC and VC without problems.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this