I wanted to implement utility literals for evaluating roots of numbers. I implemented suffix literals that return root functions. Here's my code:
#include <cmath>
#include <stdexcept>
class RootFunc {
double exp;
public:
constexpr RootFunc(auto root): exp(1.0/root) {}
constexpr auto operator()(auto number) {
return std::pow(number, exp);
}
};
#define ERROR_STRING(alt) \
"Please use the `" alt "` suffix literal"
consteval RootFunc operator"" th_root(unsigned long long int n) {
int last_digit = n % 10;
// Ensure users use the right suffix literal
if (n < 11 || n > 13) {
if (last_digit == 1) {
throw std::invalid_argument(ERROR_STRING("st_root"));
} else if (last_digit == 2) {
throw std::invalid_argument(ERROR_STRING("nd_root"));
} else if (last_digit == 3) {
throw std::invalid_argument(ERROR_STRING("rd_root"));
}
}
return RootFunc(n);
}
consteval RootFunc operator"" st_root(unsigned long long int n) {
int last_digit = n % 10;
// Ensure users use the right suffix literal
if ((n >= 11 && n <= 13) || last_digit >= 4) {
throw std::invalid_argument(ERROR_STRING("th_root"));
} else if (last_digit == 2) {
throw std::invalid_argument(ERROR_STRING("nd_root"));
} else if (last_digit == 3) {
throw std::invalid_argument(ERROR_STRING("rd_root"));
}
return RootFunc(n);
}
consteval RootFunc operator"" nd_root(unsigned long long int n) {
int last_digit = n % 10;
// Ensure users use the right suffix literal
if ((n >= 11 && n <= 13) || last_digit >= 4) {
throw std::invalid_argument(ERROR_STRING("th_root"));
} else if (last_digit == 1) {
throw std::invalid_argument(ERROR_STRING("st_root"));
} else if (last_digit == 3) {
throw std::invalid_argument(ERROR_STRING("rd_root"));
}
return RootFunc(n);
}
consteval RootFunc operator"" rd_root(unsigned long long int n) {
int last_digit = n % 10;
// Ensure users use the right suffix literal
if ((n >= 11 && n <= 13) || last_digit >= 4) {
throw std::invalid_argument(ERROR_STRING("th_root"));
} else if (last_digit == 2) {
throw std::invalid_argument(ERROR_STRING("nd_root"));
} else if (last_digit == 1) {
throw std::invalid_argument(ERROR_STRING("st_root"));
}
return RootFunc(n);
}
#undef ERROR_STRING
#include <iostream>
#define SHOW(expr) \
(std::cout << #expr << " = " << (expr) << std::endl)
int main() {
SHOW(4th_root(81));
SHOW(21st_root(200));
SHOW(2nd_root(4));
SHOW(3rd_root(27));
SHOW(11th_root(1001));
// This should fail:
// SHOW(1th_root(3));
// SHOW(11st_root(91));
// SHOW(23nd_root(1009));
}
#undef SHOW
I want to issue a compiler error when a user calls the wrong suffix literal. In my implementation, I raised an exception. While this does cause the compilation to fail, it doesn't communicate what really happened. The compilation fails because the throw clause is not a constant expression. I'm wondering if there's a better way to do this. I tried using static_assert but I get an error that n is not a constant expression, which I don't understand why, given that the function is consteval.
Please can someone tell me if there's a better way to issue a compiler error for an illegal argument in a consteval function?