13

I've been playing with user defined constants in gcc 4.7.2 and ran into some sort of size limiting factors which I do not quite understand.

The idea was to define a constexpr operator "" for fixed point decimal type. I want to avoid casting from double but rather parse mantissa and exponent at compilation time using variadic templates. The mantissa parsing proved a bit tricky.

When I enable any of the 3 disabled lines at the bottom of the code below, gcc falls into infinite loop and hangs there. I noticed the same max size for floating point literal and explicit instantiation of the variadic template but slightly larger size for integer literal.

I used command: g++ -std=c++11 -Wall -g -o literal_value literal_value.cpp

Using -ftemplate-depth-128 makes no difference.

#include <iostream>
#include <cstdint>

typedef std::uint64_t value_type;

template<value_type Temp, char... List> struct literal_parser;

template<value_type Temp, char Head, char... List>
struct literal_parser<Temp, Head, List...>
{
    static const value_type value = Head == '.' ?
        literal_parser<Temp, List...>::value :
        literal_parser<Temp * 10 + Head - '0', List...>::value;
};

template<value_type Temp, char Last>
struct literal_parser<Temp, Last>
{
    static const value_type value = Last == '.' ?
        Temp : Temp * 10 + Last - '0';
};

template<char... List>
inline constexpr value_type operator"" _value() noexcept
{
    return literal_parser<0U, List...>::value;
}

int main()
{
    std::cout << "value 1: " << 123456789012345678_value << std::endl;
    std::cout << "value 2: " << 1.23456789012345_value << std::endl;
    std::cout << "value 3: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'>::value << std::endl;

#if 0
    std::cout << "value 4: " << 1234567890123456789_value << std::endl;
    std::cout << "value 5: " << 1.234567890123456_value << std::endl;
    std::cout << "value 6: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6'>::value << std::endl;
#endif
}

Is that a bug in gcc or am I missing something?

4

4 回答 4

3

I must say that you found some nice corner case that makes compilers crazy :-) For me gcc 4.7.2 and 4.8 crashed during compilation. However clang (top version) compiled whole code fine but was using 2.4GB RAM. The problem seems to be connected to ternaty operator for '.' check. If you remove it and comment real number tests in main() everything compiles fine like a blast.

So answering your question you probably do not miss anything and gcc and clang need to probably revise their implementaion based on your case.

于 2012-11-14T17:21:22.537 回答
2

Based on Mateusz's answer I redefined the literal_parser template using constexpr function to parse single digit and everything is looking perfect now. Thanks a lot for your help!

#include <iostream>
#include <cstdint>

typedef std::uint64_t value_type;

template<value_type Temp, char... List> struct literal_parser;

inline constexpr value_type parse_digit(value_type value, char digit) noexcept
{
    return digit == '.' ? value : value * 10 + digit - '0';
}

template<value_type Temp, char Head, char... List>
struct literal_parser<Temp, Head, List...>
{
    static const value_type value =
        literal_parser<parse_digit(Temp, Head), List...>::value;
};

template<value_type Temp, char Last>
struct literal_parser<Temp, Last>
{
    static const value_type value = parse_digit(Temp, Last);
};

template<char... List>
inline constexpr value_type operator"" _value() noexcept
{
    return literal_parser<0U, List...>::value;
}

int main()
{
    std::cout << "value 1: " << 123456789012345678_value << std::endl;
    std::cout << "value 2: " << 1.23456789012345_value << std::endl;
    std::cout << "value 3: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'>::value << std::endl;

    std::cout << "value 4: " << 1234567890123456789_value << std::endl;
    std::cout << "value 5: " << 1.2345678901234567890_value << std::endl;
    std::cout << "value 6: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6'>::value << std::endl;
}
于 2012-11-14T17:47:14.300 回答
1
static const value_type value = Head == '.' ?
    literal_parser<Temp, List...>::value :
    literal_parser<Temp * 10 + Head - '0', List...>::value;

This is going to make the compilation time completely explode because the compiler has to evaluate both sides of the conditional (making the whole thing exponential over the number of digits). Try changing the expression to something like literal_parser<Head == '.' ? Temp : Temp * 10 + Head - '0', List...>::value.

于 2012-11-15T06:28:39.123 回答
-3

I think that the problem lies in how you are composing your command

g++ -std=c++11 -Wall -g -o literal_value literal_value.cpp

It's probably a good idea to never put the source files at the end, infact this compiles fine with g++ 4.7.2 under MinGW under Windows

g++ -std=c++11 -Wall -g literal_value.cpp -o literal_value

in general terms it's a good idea to reserve the last part of the line for linking specs and nothing else.

于 2012-11-14T17:05:06.063 回答