1

I'm new to metaprogramming, and I am having a const-related issue when using a tab with it.

Let's say we have several "types". Each of the types have different version, and we shall be able to handle all vesrion for each type. For that, we use a struct that contains standard info about the type, and an array containing info for each version.

The thing is, each type does not have the same number of versions. Also, the versions numbers are not very high, so I prefer not to use dynamic allocation of the said tables. But if I do static allocation, I need to have a table with the same size for each instance of the structure. That means, I have to get the highest version value and use it as the size of the array.

Here I come : I want to create a small metaprogramming template which gives the highest version value @ compile time, so I can have a fixed size of the array that will surely contains the necerrasy info for each type. But I get a compile error.

Here is a simplified sample code that reproduce the issue (the error follows it)

#include <stdio.h>

// change values here
#define VERSION_ALPHA 3
#define VERSION_BETA 5
#define VERSION_GAMMA 2

// different available types
enum TYPES
{
    T_ALPHA = 0,
    T_BETA,
    T_GAMMA,

    T_COUNT, // number of types
};

// to access versions more easily from code
static const int typeVersions[T_COUNT] =
{
    VERSION_ALPHA,
    VERSION_BETA,
    VERSION_GAMMA
};

// this meta is used to get the highest version values between all types
template<int i>
class HighestVersion
{
private:
    // version of type -1
    enum
    {
        PREVIOUS = HighestVersion<i-1>::VALUE
    };

public:
    // current max value
    enum
    {
        VALUE = (typeVersions[i] > PREVIOUS ? typeVersions[i] : PREVIOUS)
    };
};

// first version
template<>
class HighestVersion<0>
{
public:
    // current max value
    enum
    {
        VALUE = typeVersions[0]
    };
};

// highest version macro
#define HIGHEST_VERSION HighestVersion<T_COUNT>::VALUE

// holds info about a single type
struct TypeInfo
{
    char * s_pName; // name of the type as string
    unsigned int s_Flags[HIGHEST_VERSION]; // flags for each available version of this type
};


int main()
{
    // instanciate
    TypeInfo infos[T_COUNT];

    // do stuff, set name, load flags....
    /*...*/

    // for test purpose, print max version value (should print 5 in this situation)
    printf("%d\n", HIGHEST_VERSION);
}

The compiler says :

error C2057: expected constant expression

@ the lines

VALUE = (typeVersions[i] > PREVIOUS ? typeVersions[i] : PREVIOUS)

and

VALUE = typeVersions[0]

It seems that the compiler tells me that the table's contents are not constant. I assume it's because the table is interpreted as a pointer which is no constant in that case (so if the pointer changes the contents are not the same). Is there a way to correct that so I can use the script ? It will make the user to not need to manually set the size of that table...

Thanks in advance :)

5
  • What happens if you change VALUE to static const int, instead of enum? Commented Jul 24, 2013 at 13:04
  • 1
    I think you need to use constexpr feature somewhere but my compiler doesn't support it. Sorry. Commented Jul 24, 2013 at 13:13
  • Same issue. the IDE underlines the part typeVersions[i], which probably means that the error comes from here. I'll have a look at the "constexpr" (dunno what it is) Commented Jul 24, 2013 at 13:18
  • I don't think the compiler considers the array elements are a const expression, even though the array is const and known at compile-time. Commented Jul 24, 2013 at 13:19
  • constexpr does not seem to be supported by the visual studio 2012 compiler... Commented Jul 24, 2013 at 13:22

3 Answers 3

1

I'm sure not it's even possible to make this work with a static array.
A possible alternative is a trait class:

template<TYPES>
struct typeVersions;

// specializations for each type

template<>
struct typeVersions<T_ALPHA> { static const int version = VERSION_ALPHA; };
template<>
struct typeVersions<T_BETA> { static const int version = VERSION_BETA; };
// etc...

You'd use it like this:

enum {
    VALUE = typeVersions<i>::version
};
Sign up to request clarification or add additional context in comments.

1 Comment

Got it, it compiled. I was affraid of having to do something like this (got the idea, but didn't know it was called "trait") but it seems it is the only solution. Thanks.
1

As jrok said, it is likely not possible to do this with a static array. But neither is it necessary to create a trait-specialization for each type-version if you have an adequately conforming C++11 compiler.

I see that you are using VC++ and you are probably committed to it, which regrettably means the only adequately conforming C++11 compiler you might have or lay your hands right now on is VC++ 2013 Preview. If you can use that then the simple variadic template solution illustrated by the following modification of your program will work for you:

#include <stdio.h>

// change values here
#define VERSION_ALPHA 3
#define VERSION_BETA 5
#define VERSION_GAMMA 2

// different available types
enum TYPES
{
    T_ALPHA = 0,
    T_BETA,
    T_GAMMA,
    T_COUNT, // number of types
};

template<int ...Versions>
struct versions_list
{
    static_assert(sizeof ...(Versions),
        "Cannot have 0 versions");
};

template<int Only>
struct versions_list<Only>
{
    static const int max = Only;
};

template<int First, int Last>
struct versions_list<First,Last>
{
    static const int max = First > Last ? First : Last;
};

template<int First, int Second, int ...Rest>
struct versions_list<First,Second,Rest...>
{
    static const int tail_max = versions_list<Second,Rest...>::max;
    static const int max = First > tail_max ? First : tail_max;
};

// Update your version list here:
typedef versions_list<VERSION_ALPHA, VERSION_BETA, VERSION_GAMMA> typeVersions;

#define HIGHEST_VERSION typeVersions::max


// holds info about a single type
struct TypeInfo
{
    char * s_pName; // name of the type as string
    unsigned int s_Flags[HIGHEST_VERSION]; // flags for each available version of this type
};


int main()
{
    // instanciate
    TypeInfo infos[T_COUNT];

    // do stuff, set name, load flags....
    /*...*/

    // for test purpose, print max version value (should print 5 in this situation)
    printf("%d\n", HIGHEST_VERSION);
}

The HIGHEST_VERSION macro is really pointless here: you could just delete its definition and replace all occurrences with typeVersions::max.

By the way, if you really want to use C's stdio API in a C++ program rather than the C++ iostream API, strictly you ought to use #include <cstdio>, not #include <stdio.h>

Comments

0

I am not sure if this can be done with C-style arrays, but if you have compiler support for C++11 then please check my solution:

#include <array>
#include <iostream>

template <int Size, int Indice>
struct HighestValue
{
    static int get(std::array<int, Size> checkedArray) {
        return std::max(HighestValue<Size, Indice - 1>::get(checkedArray), checkedArray[Indice]);
    }
};

template <int Size>
struct HighestValue<Size, 0>
{
    static int get(std::array<int, Size> checkedArray) {
        return checkedArray[0];   
    }
};

template<size_t Size>
int checkMax(std::array<int, Size> checkedArray)
{
    return HighestValue<Size, Size - 1>::get(checkedArray);   
}

int main()
{
    std::array<int, 7> test {1, 5, 2, 3, 123, 5, 2};
    std::cout << checkMax(test);
}

Currently I do not have spare time to play with this, but I am sure it could be improved further.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.