1

Suppose that I want to declare a local static constant array, but I don't know its initializer values in compile time, and those have to be calculated first. For example, I have an array int p[100]. I write a loop to fill it with first 100 prime numbers. After those are calculated, I define a static const int primes[100] which has to be initialized by the values of p. How can I do this?

P.S. The question of "why I want to declare a local static const object?" may not have a satisfactory answer, but I think this is not the case here.

P.S.S. I mentioned the prime numbers just as an example. That is not the point.

P.S.S.S. Let's say p has 1000000 members. Then the solution suggested by user2079303 surely deserves more upvotes.

polfosol ఠ_ఠ
  • 1,840
  • 26
  • 41
  • 1
    @EddeAlmeida - that's simply not true. Constants can be initialised at runtime, just not changed. Consider constants local to a function – Smeeheey May 10 '16 at 08:13
  • Ok @Smeeheey. I deleted the stupid comment I made before. – Ed de Almeida May 10 '16 at 08:16
  • I should have said, then, that constants are initialised when they are created and can't be changed after that moment. Thus, one can't just create a constant to give it a value later. Is this correct? – Ed de Almeida May 10 '16 at 08:18
  • 1
    If you really need a static const array that isn't really const, I'd use a combination of `memcpy` and `const_cast`. – Karsten Koop May 10 '16 at 08:21
  • Could you provide more details? @KarstenKoop – polfosol ఠ_ఠ May 10 '16 at 08:39
  • `memcpy(const_cast(primes), p, sizeof(primes));` – Karsten Koop May 10 '16 at 08:41
  • If I declare `static const int primes[100]` your solution throws _access violation exception_ and if I remove the `const` word, it does not make the array constant. So `const_cast` seems useless here. Am I wrong? @KarstenKoop – polfosol ఠ_ఠ May 10 '16 at 08:50
  • The static const variables might get stored in a memory page that is strictly read-only, so even though your code compiles, this still generates an access violation on OS level. Might be compiler and OS dependend. – Karsten Koop May 10 '16 at 08:56
  • Question seems unclear. Why would you want to assign to a static array instead of just using `p` ? `static` means that it will be only initialized once, but you should only be computing the primes once anyway , so `static` would be redundant. – M.M May 10 '16 at 11:25
  • @KarstenKoop it looks like you're suggesting writing to a const object, which causes undefined behaviour and is therefore a terrible idea – M.M May 10 '16 at 11:25
  • @M.M There could be various reasons. For example, let's say I calculate the values of `p` and then write `someptr=&p[0]` where `someptr` is a global `int*`. If I exit the local scope, the calculate values would be destroyed. But if I first assign them to a static constant array, and then write `someptr=&primes[0]` they will remain unchanged outside the local scope. – polfosol ఠ_ఠ May 10 '16 at 11:35
  • @polfosol OK, fair enough. For that purposes you could use a pointer-to-const pointing to a static non-const array though. – M.M May 10 '16 at 11:37

4 Answers4

2

It's possible to initialize a static const array at run time, but a bit tedious:

int* init = get_run_time_array();
static const int primes[100] {
        init[0], init[1], init[1], init[2], init[3], init[4], init[5], // ...
};
eerorika
  • 232,697
  • 12
  • 197
  • 326
1

To sidestep your question, it is actually perfectly feasible to have your primes calculated at compile time using template meta-programming. The below code shows a possible way to do this. The main function creates instantiates the PrimeGen type to generate a compile-time std::array of the 1st 100 primes. Apologies that the code is somewhat hard to read as there is a lot of template boiler-plate here. Basically most of the logic occurs in the first PrimeGen specialisation, which uses the Sieve of Eratosthenes to generate primes, using the following basic formula:

  1. Start with N=100 (required number yet to generate), Next = 2, an empty list of primes and an empty list of filters

  2. Check if Next is prime (that is, passes all existing filters). If so, add it to the list of primes, decrement N and add a new filter which will fail to pass any number divisible by Next.

  3. Recurse to next specialisation of PrimeGen with the above modified parameters as well as with Next incremented to Next+1
  4. Terminate when N=0

Obviously the compilation time of this program will be relatively long as the compiler computes the primes.

    #include <iostream>
    #include <array>

    template <size_t Mod>
    struct ModFilter
    {
        template<size_t N>
        static constexpr bool apply()
        {
            return N % Mod != 0;
        }
    };

    template <size_t N, typename ... Filters>
    struct CheckFilters;

    template <size_t N>
    struct CheckFilters<N>
    {
        static const bool pass = true;
    };

    template <size_t N, typename Filter, typename ... Filters>
    struct CheckFilters<N, Filter, Filters...>
    {
        static const bool pass = Filter::template apply<N>() && CheckFilters<N, Filters...>::pass;
    };

    template<typename ... Filters>
    struct FilterPack{};

    template<size_t ... Numbers>
    struct NumberList{};

    template <size_t N, bool doAppend, typename Numbers>
    struct ConditionalAppend
    {
        typedef Numbers Output;
    };

    template <size_t N, size_t ... Numbers>
    struct ConditionalAppend<N, true, NumberList<Numbers...>>
    {
        typedef NumberList<Numbers..., N> Output;
    };

    template <size_t N, bool doAppend, typename Filters>
    struct ConditionalAppendFilter
    {
        typedef Filters Output;
    };

    template <size_t N, typename ... Filters>
    struct ConditionalAppendFilter<N, true, FilterPack<Filters...>>
    {
        typedef FilterPack<Filters..., ModFilter<N>> Output;
    };

    template<size_t N, size_t Next, typename Numbers, typename Filters>
    struct PrimeGen;

    template<size_t N, size_t Next, size_t ... Numbers, typename ... Filters>
    struct PrimeGen<N, Next, NumberList<Numbers...>, FilterPack<Filters...>>
    {
        static constexpr std::array<size_t, N + sizeof...(Numbers)> numbers
            = PrimeGen<CheckFilters<Next, Filters...>::pass ? N-1 : N,
                        Next+1,
                        typename ConditionalAppend<Next, CheckFilters<Next, Filters...>::pass, NumberList<Numbers...>>::Output,
                        typename ConditionalAppendFilter<Next, CheckFilters<Next, Filters...>::pass, FilterPack<Filters...>>::Output>
              ::numbers;

        static const int size = N + sizeof...(Numbers);
    };

    template<size_t Next, size_t ... Numbers, typename ... Filters>
    struct PrimeGen<0, Next, NumberList<Numbers...>, FilterPack<Filters...>>
    {
        static constexpr std::array<size_t, sizeof...(Numbers)> numbers = {Numbers...};
        static const int size = sizeof...(Numbers);
    };

    template<size_t N, size_t Next, size_t ... Numbers, typename ... Filters>
    constexpr std::array<size_t,N + sizeof...(Numbers)> PrimeGen<N, Next, NumberList<Numbers...>, FilterPack<Filters...>>::numbers;

    template<size_t Next, size_t ... Numbers, typename ... Filters>
    constexpr std::array<size_t,sizeof...(Numbers)> PrimeGen<0,Next,NumberList<Numbers...>,FilterPack<Filters...>>::numbers;

    int main()
    {
        using MyGen = PrimeGen<100, 2, NumberList<>, FilterPack<> >;

        for(int i=0; i<MyGen::size; ++i)
            std::cout << MyGen::numbers[i] << std::endl;

        return 0;
    }
Smeeheey
  • 9,906
  • 23
  • 39
0

The most efficient solution will be:

static const int primes[100] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541 };

You can even do away with your whole p calculation now.

M.M
  • 138,810
  • 21
  • 208
  • 365
0

I just read this great answer to another question in SO and figured it out. There is no need to declare a local variable as static const. It seems that only static would suffice to keep the calculated values intact outside the scope.

Community
  • 1
  • 1
polfosol ఠ_ఠ
  • 1,840
  • 26
  • 41
  • Why? @LightnessRacesinOrbit – polfosol ఠ_ఠ May 11 '16 at 11:03
  • There isn't enough detail here to say, but from what little you've told us, this sounds like a misconception. – Lightness Races in Orbit May 11 '16 at 11:15
  • In a comment, I stated that: for example, let's say I calculate the values of `p` and then write `someptr=&p[0]` where `someptr` is a global `int*`. If I exit the local scope, the calculated values would be destroyed. But if I first assign them to a static constant array, and then write `someptr=&primes[0]` they will remain unchanged outside the local scope. I hoped this would clarify the reason for this question, and I really can't see a misconception. @LightnessRacesinOrbit – polfosol ఠ_ఠ May 11 '16 at 11:24
  • Hmm, okay. In _this_ answer, you seemed to suggest that `static` grants immutability. It doesn't. I guess what I don't understand is what `const` has to do with anything. – Lightness Races in Orbit May 11 '16 at 11:25
  • I agree. The word `const` may arise some misconceptions. At a first glance, I wanted them to be constant in local scope and accessible outside the local scope. It seems we generally can't have both. @LightnessRacesinOrbit – polfosol ఠ_ఠ May 11 '16 at 11:32
  • Yeah I think you're confusing mutability with accessibility. – Lightness Races in Orbit May 11 '16 at 12:02