c++: Is there something like “boost/std typetraits conditional” that generates a value (not a type) at compile time?

StackOverflow https://stackoverflow.com/questions/7472976

Вопрос

I am currently doing the following to generate a value at compile time, which works:

        //if B is true, m_value = TRUEVAL, else FALSEVAL, T is the value type
        template<bool B, class T, T TRUEVAL, T FALSEVAL>
        struct ConditionalValue
        {
            typedef T Type;

            Type m_value;

            ConditionalValue():
            m_value(TRUEVAL)
            {}
        };

        template<class T, T TRUEVAL, T FALSEVAL>
        struct ConditionalValue<false, T, TRUEVAL, FALSEVAL>
        {
            typedef T Type;

            Type m_value;

            ConditionalValue():
            m_value(FALSEVAL)
            {}
        };

Then you can simply do something like this:

template<class T>
void loadPixels(uint32 _w, uint32 _h, T * _pixels)
{
    PixelDataType::Type pixelType = PixelDataType::Auto; //enum I want to set

    ConditionalValue<boost::is_same<T, uint8>::value, PixelDataType::Type, PixelDataType::UInt8, PixelDataType::Auto> checker;
    pixelType = checker.m_value;

   ConditionalValue<boost::is_same<T, uint16>::value, PixelDataType::Type, PixelDataType::UInt16, PixelDataType::Auto> checker2;
   pixelType = checker2.m_value;

   ...

}

I know this example does not make much sense, but I use that code to set the value of an enum at compile time.- So here is my question: Is there something like that in std/boost type traits allready? When browsing through the reference I only found conditional which does almost what I want, but only generates a type, not a value.

EDIT:

Updated example.

Edit2:

I just realized that boost::is_same::value is all I need to solve my problem.- As for the answer to the question: There does not seem to be anything included in std/boost for good reason as pointed out by thiton

EDIT3: If you are still looking for a solution to create a value at compile time, you can either use my code wich works. If you are looking for something very close to boost/stl Kerrek's or Nawaz seem to be valid solutions too. If you are looking for a solution that assigns the correct enum at compile time Luc Touraille approach seems to be interesting even though i decided it's overkill for my situation!

Это было полезно?

Решение

I think it's the simple answer: Because the operator ?: can select values quite well. Types are harder to select, that's why boost constructs exist for that. For pathological cases, the boost::mpl magic Luc suggested is fine, but it should be quite rare.

Другие советы

A combination of std::conditional and std::integral_constant might work in some situations:

template <bool B, typename T, T trueval, T falseval>
struct conditional_val : std::conditional<B,
       std::integral_constant<T, trueval>,
       std::integral_constant<T, falseval>>::type
{ };

Now use:

const int q = conditional_val<B, int, 12, -8>::value;

Equivalently:

const int q = B ? 12 : -8;

Boost.MPL has a set of classes to manipulate data types at compile-time, along with some arithmetic operations. These classes wrap a value into a type, for instance the integer 4 can be represented by the type mpl::int_<4>.

You can use these in compile-time conditional:

typedef typename 
    mpl::if_< 
        boost::is_same< T, uint8 >, 
        mpl::int_< 42 >, 
        mpl::int_< 187 >
    >::type result;

int i = result::value;

MPL also provides a generic integral wrapper that you can use with your enums:

template<class T>
void loadPixels(uint32 _w, uint32 _h, T * _pixels)
{
    PixelDataType::Type pixelType = PixelDataType::Auto; //enum I want to set

    typedef typename mpl::if_<
        boost::is_same<T, uint8>, 
        mpl::integral_c<PixelDataType::Type, PixelDataType::UInt8>,
        mpl::integral_c<PixelDataType::Type, PixelDataType::Auto>
    >::type checker;

    pixelType = checker::value;

    typedef typename mpl::if_<
        boost::is_same<T, uint16>, 
        mpl::integral_c<PixelDataType::Type, PixelDataType::UInt16>,
        mpl::integral_c<PixelDataType::Type, PixelDataType::Auto>
    >::type checker2;

    pixelType = checker2::value;

    ...
}

If you have a lot of mapping like this to do, you could consider using a mixed compile-time/runtime data structure such as fusion::map, but that is probably a bit overkill :):

typedef fusion::map<
    fusion::pair<uint8, PixelDataType::Type>,
    fusion::pair<uint16, PixelDataType::Type> >
map_type;

map_type pixelTypesMap(
    make_pair<uint8>(PixelDataType::UInt8),
    make_pair<uint16>(PixelDataType::UInt16));

...

template<class T>
void loadPixels(uint32 _w, uint32 _h, T * _pixels)
{
    // need special handling if T is not in the map
    PixelDataType::Type pixelType = fusion::at_key<T>(pixelTypesMap);

    ...
}

You can write yourself:

namespace extend 
{
  template<bool B, class T, T X, T Y>  
  struct conditional
  {
       static const T value = X;
  };
  template<class T, T X, T Y>  
  struct conditional<false,T,X,Y>
  {
       static const T value = Y;
  };
}

//test
assert(conditional<std::is_same<int,int>::value, int, 10, 20>::value == 10);
assert(conditional<std::is_same<int,char>::value, int, 10, 20>::value == 20);

I came across a case where I needed to do exactly what ? does (compare values, return values) but using template specialization (why would you ever need that? simple: compile time evaluation of ? can lead to "unreachable code" warnings)

So the most standard solution which works for your original question as well as for mine imho is:

std::conditional<myval, 
                 std::integral_constant<T, val>, 
                 std::integral_constant<T, val>
                >::type::value;

Now simply replace "myval" with std::is_same and you have a solution for your case (compare types, return values), while above is a solution for my case (compare values, return values <=> ?)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top