0

In my C++ program I've got these three user-defined literal operators:

constexpr Duration operator""_h(unsigned long long int count) { return {count / 1.f, true}; }
constexpr Duration operator""_m(unsigned long long int count) { return {count / 60.f, true}; }
constexpr Duration operator""_s(unsigned long long int count) { return {count / 3600.f, true}; }

Duration holds a number of hours (as a float) and a validity flag.

So I can say: Duration duration = 17_m;

And I can say: int m = 17; Duration duration = operator""_m(m);

But I can't say:

const char* m = "17_m"; Duration duration1 = operator""_(m);
const char* h = "17_h"; Duration duration2 = operator""_(h);

What I'm aiming for is something like that operator""_() that I just invented up there, with the compiler picking at run-time the appropriate operator to call. I know I could write something like this myself (in fact I've already done it in this case), but I don't think anything like it is already in the language. I'm asking here to confirm that: Is it in the language?

mjwach
  • 1,174
  • 2
  • 9
  • 25
  • could you please describe a use case when you have constant value but variable units? – Andriy Tylychko Aug 16 '18 at 21:51
  • It seems like you actually need a `Duration` parser rather than a runtime-switching literal. I think a literal whose meaning can change at runtime would be very annoying to maintain. What is it you're really trying to do? This sounds like an [X/Y problem](https://en.wikipedia.org/wiki/XY_problem) to me. – alter_igel Aug 16 '18 at 21:55
  • I'm trying to get an answer to the question I asked. – mjwach Aug 16 '18 at 21:59
  • No, you can't. The compiler isn't present at run-time, and neither are names (including the names of user-defined literal operators). – rici Aug 17 '18 at 03:08

1 Answers1

1

Do you wish to implement your own parser? Here is a sketch that can be extended to the constexpr world:

#include <cassert>
#include <cstdlib>

#include <iostream>

constexpr Duration parse_duration(const char* input) {// input: \A\d*_[hms]\z\0
  int numeric_value = 0;

// TODO: handle negative values, decimal, whitespace...

  std::size_t pos = 0;
  while(input[pos] != '_') {
    unsigned digit = unsigned(input[pos++]) - unsigned('0');
    assert(digit <= 9);
    numeric_value *= 10;
    numeric_value += digit;
  }

  char unit = input[pos+1];
  assert(input[pos+2] == '\0' && "must end with '\0' after one-letter unit");
  switch(unit) {
  case 'h': return operator""_h(numeric_value);
  case 'm': return operator""_m(numeric_value);
  case 's': return operator""_s(numeric_value);
  default: std::cerr << unit << std::endl;
  }
  assert(false && "unknown unit");

  return {};
}

If you do not care about constexpr then you should use one of the higher-level approaches suggested in @RemyLebeau's comment to this answer.

Julius
  • 1,816
  • 10
  • 14
  • Not now, because I already did (and it looks basically like what you've got there). I want to know whether I can just use whatever internal parser the compiler uses (but at run-time), instead of writing all that stuff. – mjwach Aug 16 '18 at 22:28
  • No, I do not know a simpler solution. Though I can imagine that a library could be written to provide this kind of functionality. The tricky part there is probably the lookup of the user-defined operators if they are in a namespace. Macros for the rescue? – Julius Aug 16 '18 at 22:38
  • 1
    Consider using `std::sscanf()` or `std::istringstream` to parse the input string. Or, change the input parameter to `std::string` and use its `find()` and `substr()` methods to split the input into its constituent pieces, and then use [`std::strtoull()`](https://en.cppreference.com/w/cpp/string/byte/strtoul) or [`std::strtold()`](https://en.cppreference.com/w/cpp/string/byte/strtof) to parse the numeric substring into a `unsigned long long` or `long double` as needed. – Remy Lebeau Aug 16 '18 at 23:18
  • @RemyLebeau: Indeed, you made a valid point. I will keep my answer as the `constexpr`-able algorithm. – Julius Aug 17 '18 at 00:07