1

I took an example I found on-line whereby a constexpr of the form _binary could be evaluated at compile time as an unsigned long long and then I tried to generalize it for any base from 2 to 36. For instance, 17b1234_baseChange would be evaluated from base 17 as ((1*17 +2)*17 + 3)*17 + 4 = 5624.

I realize this is kind of a contrived example but I was wondering what the limits are for operator"". Is seems like the compiler when parser is creating separate tokens for numbers and letters. I was curious if this was a bug or expected behavior. (In addition to 'b', I have tried other separators with no luck.) Thanks for your insight.

Command line within http://coliru.stacked-crooked.com/: g++-4.9 -std=c++14 -O3 -Wall -Wextra -pedantic -pthread -pedantic-errors main.cpp -lm && ./a.out

Code:

#include <iostream>

const char Delimiter = 'b';
typedef unsigned long long ULL;

/// Extract one "digit" from a digit string and then recurse.
template<unsigned short NumericBase, char Head, char... Rest> 
struct baseChange_helper
{
  constexpr ULL operator()(ULL result) const;
};

/// Teminate recursion when interpreting a numeric string.
template<unsigned short NumericBase, char Head> struct     
baseChange_helper<NumericBase, Head>
{
   constexpr ULL operator()(ULL result) const;
};

template<unsigned short NumericBase, char Head, char... Rest>
constexpr ULL baseChange_helper<NumericBase, Head, Rest...>::operator()
(ULL result) const
{
   static_assert(   (Head >= '0' && (Head <= '0' + std::min(NumericBase-1, 9)))
                 || (NumericBase > 10 && (Head >= 'A' && Head <= 'A' +     
                     std::min(NumericBase-10, 25)))
                 , "not a valid number in this base");
    return baseChange_helper<Rest...>{}(result = result * (NumericBase -1) 
                         + ((Head > 'A') ? (10 + Head - 'A') : (Head - '0')));
}

template<unsigned short NumericBase, char Head> 
constexpr ULL
baseChange_helper<NumericBase, Head>::operator()(ULL result) const
{
    static_assert(   (Head >= '0' && (Head <= '0' + std::min(NumericBase-1, 9)))
                 || (NumericBase > 10 && (Head >= 'A' && 
                     Head <= 'A' + std::min(NumericBase-10, 25)))
                 , "not a valid number in this base");
   return  result * (NumericBase -1)  + 
           ((Head > 'A') ? (10 + Head - 'A') : (Head - '0'));
}

template<unsigned short NumericBase, char Head, char... Rest> struct     
baseChange_parser
{
   constexpr ULL operator()() const;
};
template<unsigned short NumericBase, char Head, char... Rest>
constexpr ULL baseChange_parser<NumericBase, Head, Rest...>::operator()()
const
{
   static_assert( (Head == Delimiter && NumericBase > 1 && NumericBase < 36) ||
                  (Head >= '0' && Head <= '9'),
                  "not a valid base");    
   return ( Head == Delimiter ? baseChange_helper<NumericBase, Rest...>((ULL)0)
                    : baseChange_parser<NumericBase*10 + Head - '0', Rest...>()
          );
}
template<char... Chars> constexpr ULL operator"" _baseChange()
{
   return baseChange_parser<0, Chars...>{}();
}

int main() {
    const ULL v = 17b1234_baseChange;
    std::cout << v << std::endl;
}
1
  • I'd probably just define something such as _b14. Or do you need it more dynamic? Because, to be honest, having "17b" in front of all your numbers doesn't look really readable to me.
    – Mario
    Commented Feb 7, 2015 at 10:21

2 Answers 2

2

The input to your user-defined literal operator is one of two things: either it's a number, of it's a string.

For it to be a number, it must be something the normal C++ lexer/parser can recognize as a number. It reads the text of the program, converts that token to a number, and passes the number to your routine.

Otherwise, it can be a string. In this case, you need to enclose the string in quote marks, just like any other string literal. As far as the compiler cares, it's just a string literal so it can be formatted any way you choose as long as its contents are legitimate characters (e.g., if it contains a back-slash, the next character must form one of the standard escape sequences, unless you use a raw string literal).

Since 17b1234 isn't something the compiler can/will recognized as a number, it can't be passed to a user-defined literal operator as a number. If you really insist on this format, you're pretty much stuck with passing it as a string:

"17b1234"_baseChange

In this case, your operator will (of course) need to receive its argument as a string, not a number.

0

I don't think this is supposed to work. The system works on the string representations of individual tokens, so because in your example "17b12345_baseChange" the "b" is not an acceptable character for an integer literal (see note below) the token is split at that point, and the base change operation only sees "12345", leaving "17b" behind to generate a syntax error.

I do wonder how this interacts with C++14 digit grouping characters, however - could you get it to understand "17'12345" perhaps?

Note: except in the string "0b" which is only allowed at the start of the literal

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.