Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile time type conversion check (constexpr and user defined literals)

Update: I posted my own answer below And there's a longer version of this matter here: http://scrupulousabstractions.tumblr.com/post/38460349771/c-11-type-safe-use-of-integer-user-defined-literals

Question:

I've made a simple constexpr user defined literal _X that gets the value as an unsigned long long (that's how numeral user defined literals work: http://en.cppreference.com/w/cpp/language/user_literal), and then I make sure that the value fits in a signed long long.

It all works well (too big values cause compilation error), but only when I explicitly create a variable such as

constexpr auto a= 150_X;

If instead I write something typical like

cout << 150_X << endl;;

the tests are not performed at compile time.

  • Are constexpr functions only executed at compile time if they are assigned to a constexpr variable? (I could not find that in the standard)

  • Is it possible to achieve the safe behaviour of _X that I'm looking for?

Full example:

#include<iostream>
#include<stdexcept>

inline constexpr long long testConv(unsigned long long v) {
  return (v > 100 ) ? throw std::exception() : v; 
} // will eventually use actual limit from numeric_limits

inline constexpr long long operator "" _X(unsigned long long f) { 
  return testConv(f) ;
}

int main(){
  constexpr auto a= 5_X;
  std::cout << a << std::endl;
  std::cout << 200_X << std::endl;  // This bad literal is accepted at compile time
  constexpr auto c=250_X;           // This bad literal is not accepted at compile time
  std::cout << c << std::endl;
}

oh, for reference: I used gcc4.7.2.

like image 299
Johan Lundberg Avatar asked Dec 13 '12 21:12

Johan Lundberg


1 Answers

Self answer: I found a complete solution, inspired by the comments and other answers to my question, and other questions such as https://stackoverflow.com/a/13384317/1149664.

The solution is to use the template form of user defined literals and sum up the number manually, multiplying the sum based on the already parsed digits by 10.

I wrote a detailed version of this self-answer here: http://scrupulousabstractions.tumblr.com/post/38460349771/c-11-type-safe-use-of-integer-user-defined-literals

template<char... Chars>
int operator"" _steps(){
  return {litparser<0,Chars...>::value};
}

Litparser is a little template meta-program which takes a list of characters as arguments expanded from the input characters held by the Chars parameter pack.

typedef unsigned long long ULL;

// Delcare the litparser 
template<ULL Sum, char... Chars> struct litparser;

// Specialize on the case where there's at least one character left:
template<ULL Sum, char Head, char... Rest>
struct litparser<Sum, Head, Rest...> {
// parse a digit. recurse with new sum and ramaining digits
  static const ULL value = litparser<
    (Head <'0' || Head >'9') ? throw std::exception() : 
    Sum*10 + Head-'0' , Rest...>::value;
};

// When 'Rest' finally is empty, we reach this terminating case
template<ULL Sum>  struct litparser<Sum> {
  static const ULL value = Sum;
};
like image 197
Johan Lundberg Avatar answered Nov 14 '22 22:11

Johan Lundberg