views:

507

answers:

12

One can define a static array at compile time as follows:

const std::size_t size = 5;    
unsigned int list[size] = { 1, 2, 3, 4, 5 };

Question 1 - Is it possible by using various kinds of metaprogramming techniques to assign these values "programmatically" at compile time?

Question 2 - Assuming all the values in the array are to be the same barr a few, is it possible to selectively assign values at compile time in a programmatic manner?

eg:

const std::size_t size = 7;        
unsigned int list[size] = { 0, 0, 2, 3, 0, 0, 0 };
  1. Solutions using C++0x are welcome
  2. The array may be quite large, few hundred elements long
  3. The array for now will only consist of POD types
  4. It can also be assumed the size of the array will be known beforehand, in a static compile-time compliant manner.
  5. Solutions must be in C++ (no script, no macros, no pp or code generator based solutions pls)

UPDATE: Georg Fritzsche's solution is amazing, needs a little work to get it compiling on msvc and intel compilers, but nonetheless a very interesting approach to the problem.

A: 

You can simply do this with some scripting language running from Makefile. This is optimal way to solve this problem for me.

Eg. You can generate some code depends on other (for example) header files by simple greping her content (for simplify greping you can mark some parameters with your specified comments).

Summary, you cant do this in C/C++ language on compiling time, but can do this with executing some other program before compiling process to generate source code.

Here this is a solve of Question 1 in BASH language:

#!/bin/bash

N = 5
echo "const std::size_t size = $N;"
echo -e "unsigned int list[size] = {"

for i in {1..N}
do
   echo -e "$i"
   if N <> 5
      echo ","
   end
done

echo "};"
Svisstack
+1  A: 

I think Hamish Grubijan's answer is a good one. Use a scripting language to generate the .h/.cpp files you need, then compile.

An input to the script would be whatever it is that effects the array contents. Very quick and easy.

+1 Isn't this acceptable practice, given that this is how autotools operates?
amphetamachine
why use metaprogramming at all? why not just get a script to generate all the various types you need, who would have thought policy oriented design all the other stuff people have been doing for the past 10 years is such a waste of time...
Hippicoder
@Hippicode: Hey look, a straw man. Where has *anyone* said "all metaprogramming is not to be used"? I certainly don't see it. All this answer says is "maybe metaprogramming isn't best *for your current situation*." Sheesh.
GMan
@Hippicoder, I already see a perl solution, but do not see a meta-programming solution. Declarative programming has its limitations, and this is where a general-purpose scripting language can come in and save the day.
Hamish Grubijan
A: 

Do you really need to do it at compiler time? It would be much easier to do at static initialization time. You could do something like this.

#include <cstddef>
#include <algorithm>

template<std::size_t n>
struct Sequence
{
    int list[n];

    Sequence()
    {
        for (std::size_t m = 0; m != n; ++m)
        {
            list[m] = m + 1;
        }
    }
};

const Sequence<5> seq1;

struct MostlyZero
{
    int list[5];

    MostlyZero()
    {
        std::fill_n(list, 5, 0); // Not actually necessary if our only
                                 // are static as static objects are
                                 // always zero-initialized before any
                                 // other initialization
        list[2] = 2;
        list[3] = 3;
    }
};

const MostlyZero mz1;

#include <iostream>
#include <ostream>

int main()
{
    for (std::size_t n = 0; n != 5; ++n)
    {
        std::cout << seq1.list[n] << ", " << mz1.list[n] << '\n';
    }
}

You could push the lists outside of the structs if you wanted but I thought it was a bit cleaner like this.

Charles Bailey
The values are not present at compile-time - I think if what i wanted was as simple as that I could just as easily written a function to populate a std::vector... thanks for the attempt though.
Hippicoder
@Hippicoder: If the values aren't present at compile-time then how are you going to programmatically assign them at compile-time as your question asks?
Charles Bailey
@Charles: I believe he's trying to say your code isn't generating them at compile time. Your code is creating the array at runtime, and thus doesn't fit his overly tight requirements...
Thanatos
A: 

There's a lot of things you can do with meta-programming. But first I'd like to ask: why would you want to do this in your case? I could understand if you needed to declare such an array in different places, so that it'd demand rewriting the same things multiple times. Is this your case?

By saying "define programmatically" I suggest the following:

#define MyArr(macro, sep) \
    macro(0) sep \
    macro(0) sep \
    macro(2) sep \
    macro(3) sep \
    macro(0) sep \
    macro(0) sep \
    macro(0)

By now we've defined all the values you wanted in the most abstract way. BTW if those values actually mean something for you - you could add it to the declaration:

#define MyArr(macro, sep) \
    macro(0, Something1) sep \
    macro(0, Something2) sep \
    // ...

Now let's breath life into the above declaration.

#define NOP
#define COMMA ,
#define Macro_Count(num, descr) 1
#define Macro_Value(num, descr) num

const std::size_t size = MyArr(Macro_Count, +); 
unsigned int list[size] = { MyArr(Macro_Value, COMMA) };

You can also handle the situation where most of your array entries are the same, with some perverted creativity :)

But you should always ask yourself: is this really worth it? Because, as you can see, you turn the code into a puzzle.

valdo
A: 

Something like Boost.Assignment could work for standard containers. If you really need to use arrays, you can use it along Boost.Array.

danielkza
A: 

from boost,

boost::mpl::range_c<int,1,5>

Will generate a list of sorted numbers from 1 to 5 at compile time. For the second, you mention no criteria for which values would be changed. I'm pretty sure you can't undef then redef a new var once a list is created.

Michael Dorgan
the with range_c and other mpl style arrays is that, they don't have a random access operator, or if they do it requires a compile-time index value. I'd like to be able to use the array as i would a static array at run-time with run-time generated index values.
Hippicoder
A: 

the 1't question. You can do it like that.

template <int num, int cur>
struct ConsequentListInternal {
    enum {value = cur};
    ConsequentListInternal<num-1,cur+1> next_elem;
};

template <int cur>
struct ConsequentListInternal<0, cur> {
    enum {value = cur};
};

template <int v>
struct ConsequentList {
    ConsequentListInternal<v, 0> list;
};

int main() {
    ConsequentList<15> list;
    return 0;
}
Max
Ok.... how would I get the ith value from the list, with a run-time generated "i" ? ps: please read the comment to Michael Dorgan's solution.
Hippicoder
A: 

How about building a nested struct using templates, and casting that as an array of the right type. The example below works for me, but I have a feeling I'm either treading in or walking very close to undefined behaviour.

#include <iostream>

template<int N>
struct NestedStruct
{
  NestedStruct<N-1> contained;
  int i;
  NestedStruct<N>() : i(N) {}
};

template<>
struct NestedStruct<0> 
{
  int i;
  NestedStruct<0>() : i(0) {}
};

int main()
{
  NestedStruct<10> f;
  int *array = reinterpret_cast<int*>(&f);
  for(unsigned int i=0;i<10;++i)
  {
    std::cout<<array[i]<<std::endl;
  }
}

And of course you could argue that the array is not initialised at compile time (which I think is impossible) but the values that will go into the array are calculated at compile time, and you can access them as you would a normal array... I think that's as close as you can get.

Michael Anderson
+2  A: 

Well your requirements are so vague it's difficult to do anything about them... The main issue is of course: where do those value come from ?

Anyway a build in C++ can be thought of as 4 steps:

  • Pre-build steps: script generation of header/source from other formats
  • Preprocessing
  • Template instantiations
  • Compilation proper

If you wish to rule out the script generation, then you're left with 2 alternatives: Preprocessing and Meta-template programming.

There is just no way I know of for meta-template programming to do the trick here, because as far as I know it's not possible to concatenate two arrays at compile time. Thus we are left with the savior of the day: Preprocessor Programming

I would suggest using a full-fledged library to help us out: Boost.Preprocessor.

Of particular interest here:

Now if only we knew where to pick the values from, we could give more meaningful examples.

Matthieu M.
Check out Georg Fritzsche's answer: using C++0x variadic templates and initialization of static arrays from varadic list he was able to come up with a metatemplate solution!
Matthieu M.
By the way, I obviously don't mind being downvoted (I would not propose answers otherwise), but after having taking some time to think over the problem, lay out a sound reasoning and write this answer with as few mistakes as possible (not that easy for a non-English native speaker) I would certainly appreciate if I had at least an idea as to WHY... care to comment ?
Matthieu M.
+12  A: 

The closest you can get is using C++0x features to initialize local or member arrays of templates from a variadic template argument list.
This is of course limited by the maximum template instantiation depth and wether that actually makes a notable difference in your case would have to be measured.

Example:

template<unsigned... args> struct ArrayHolder {
    static const unsigned data[sizeof...(args)];
};

template<unsigned... args> 
const unsigned ArrayHolder<args...>::data[sizeof...(args)] = { args... };

template<size_t N, template<size_t> class F, unsigned... args> 
struct generate_array_impl {
    typedef typename generate_array_impl<N-1, F, F<N>::value, args...>::result result;
};

template<template<size_t> class F, unsigned... args> 
struct generate_array_impl<0, F, args...> {
    typedef ArrayHolder<F<0>::value, args...> result;
};

template<size_t N, template<size_t> class F> 
struct generate_array {
    typedef typename generate_array_impl<N-1, F>::result result;
};

Usage for your 1..5 case:

template<size_t index> struct MetaFunc { 
    enum { value = index + 1 }; 
};

void test() {
    const size_t count = 5;
    typedef generate_array<count, MetaFunc>::result A;

    for (size_t i=0; i<count; ++i) 
        std::cout << A::data[i] << "\n";
}
Georg Fritzsche
EXCELLENT!!! AWESOME!!! - thank-you
Hippicoder
A note regarding template instantiation depth, msvc dies at around 1000, gcc has an option to set the recursive depth, I've been able to create a 512 element lut with this suggestion - compile times are obviously a little longer than having the lut hard-coded in source, but all-in-all it works fine!!! :D
Hippicoder
Amazing! It essentially allows the concatenation / extension of arrays I could not realize in C++03 with metatemplate. I think you should parameterize `ArrayHolder` with the MetaFunction though, in order to be able to define more than 1 array with a given arity.
Matthieu M.
@Matthieu: If the values differ its a different type, if they are the same i don't want a duplicate type here - am i missing something?
Georg Fritzsche
Nope, I was, thanks :)
Matthieu M.
very very nice!
Viktor Sehr
A: 

Just use a code generator. Build one or more templates that can generate the code you want, using a table or even math functions. Then include the file you generated in your app.

Seriously, a code generator would make your life much easier.

Rui Curado
@Rui: Two people have flagged this as spam. It doesn't appear to be spam to me, *except* your code generator isn't yet available, so mentioning it doesn't help to answer the question. (Editing the answer once your tool is available would be different.) – And I'm also a big fan of code generation, it really will make his life easier. ;)
Roger Pate
@Roger: I've edited my answer and removed all references to the product.
Rui Curado
@Rui: Now it's definitely worth an upvote! Self-promotion is a tricky business on SO.
Roger Pate