gcc c++11 limits for user defined constants and template parameter packs
Asked Answered
P

4

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?

Penicillin answered 14/11, 2012 at 15:21 Comment(4)
As far as I know, custom non-string literals are not yet functional in gcc 4.7. Custom string literals seem to work fine though.Madore
For what it's worth, I get the same problem with GCC 4.7.2.Carlie
It's good to know that I'm not alone fighting with this issue. Thanks. I've checked the status of these features in the table. gcc.gnu.org/gcc-4.7/cxx0x_status.html Variadic templates N2242 Yes User-defined literals N2765 Yes Now I found that compiler when struggling with these templates consumes all the memory on my linux box. The box has 4G and 2.5G would be available for gcc. That seems quite excessive memory consumption for the task.Retorsion
It compiles fine with G++ 4.7 for me ... but does need nearly 9GB of memory. You probably ran out of memory and started swapping. It "only" uses 8.1GB with G++ from trunk (i.e. what will be 4.8), which is a small improvement :-\Unexperienced
W
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.

Washington answered 14/11, 2012 at 17:21 Comment(0)
P
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;
}
Penicillin answered 14/11, 2012 at 17:47 Comment(0)
B
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.

Boz answered 15/11, 2012 at 6:28 Comment(0)
M
-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.

Manciple answered 14/11, 2012 at 17:5 Comment(3)
I think you are mistaken here. All non-option command line arguments should follow the optional ones according to POSIX. GNU programs generally allow for reordering, but that's an extension. Personally, I always compile with sources and link with object files at the end because this is a portable way that works with gcc, Sun CC and other compilers.Pollock
@MaximYegorushkin all i can say is that the first command do not works and second one does, also I have always seen people arranging gcc commands this way and I have always found this a really good and clean idea.Manciple
You should list objects/libraries in the order they are needed to resolve symbols, but for other options (including all the options in this case) it makes no difference, and is not related to the OP's problem.Unexperienced

© 2022 - 2024 — McMap. All rights reserved.