patterncppMinor
User defined literal for std::integral_constant
Viewed 0 times
stduserliteralintegral_constantfordefined
Problem
I created a user defined literal
With this literal, wirting
To generate other integral constants, I plan to add the user defined literals
Is there a way to improve this code and/or make it cleaner or more idiomatic? Have I missed some potential flaws?
_c to convert an "integer" literal into an std::integral_constant. Basically, the goal is to allow users to write std::integral_constant instances without the usual boilerplate. Here is the implementation:#include
template
constexpr auto pow_helper(T acc, T value, U times)
-> T
{
return (times > 1) ?
pow_helper(acc*value, value, times-1) :
acc;
}
// Compile-time pow function, only works with
// an unsigned integer exponent
template
constexpr auto pow(T value, U exponent)
-> T
{
return (exponent == 0) ? 1 :
(exponent > 0) ? pow_helper(value, value, exponent) :
1 / pow_helper(value, value, -exponent);
}
// Structure to parse an integer literal
template
struct parse
{
static constexpr Integral value =
parse::value * pow(10u, sizeof...(Digits))
+ parse::value;
};
// Specialization of parse to parse a single
// decimal digit
template
struct parse
{
static_assert(C >= '0' && C
constexpr auto operator"" _c()
-> std::integral_constant::value>
{
return {};
}With this literal, wirting
42_c generates an instance of std::integral_constant. Here is a small working example:int main()
{
std::cout >::value, "");
}To generate other integral constants, I plan to add the user defined literals
_cl, _cll, _cu, _cul and _cull whoe implementation is exactly the same, only the resulting type differs.Is there a way to improve this code and/or make it cleaner or more idiomatic? Have I missed some potential flaws?
Solution
Usage of "fat" templates
I would try to avoid using full-blown class templates when there's an elegant alternative solution with alias templates or
Algorithm
By changing the order of operations, you can get rid of the
Sketch:
Literals in other bases
Your parse function currently rejects hexadecimal, octal, and in C++1y binary literals, as well as C++1y's digit separators. Supporting the bases digit conversion. IIRC, it's not guaranteed that the letters are contiguous in the basic execution character set (as opposed to the digits), so this could be rather painful w/o a switch or lookup-table. Also, upper/lower case.
Automatically growing literal type
Normal C++ literals automatically adjust their types according to their value. If they don't fit into an
Operators
Unfortunately, the StdLib doesn't provide operators for
Consider using a custom type, possibly derived from / convertible to
Side remark:
I would try to avoid using full-blown class templates when there's an elegant alternative solution with alias templates or
constexpr functions. Note that I didn't measure this, so take it with a grain of salt: lightweight constexpr functions and alias template can be faster to instantiate. Using constexpr functions might reduce the number of instantiations to a minimum of 1 (the literal operator template) or two (the constexpr function template with a single template type parameter).Algorithm
By changing the order of operations, you can get rid of the
pow function (template) entirely: Pass the current result to the next step, then multiply and add. The shifting (in base 10) will be done on the fly.Sketch:
constexpr int combine(int p)
{
return p;
}
template
constexpr int combine(int val, int p0, TT... pp)
{
return combine(val*10 + p0, pp...);
}
constexpr int parse(char C)
{
return (C >= '0' && C
constexpr auto operator"" _c()
-> std::integral_constant
{
return {};
}Literals in other bases
Your parse function currently rejects hexadecimal, octal, and in C++1y binary literals, as well as C++1y's digit separators. Supporting the bases digit conversion. IIRC, it's not guaranteed that the letters are contiguous in the basic execution character set (as opposed to the digits), so this could be rather painful w/o a switch or lookup-table. Also, upper/lower case.
Automatically growing literal type
Normal C++ literals automatically adjust their types according to their value. If they don't fit into an
int, they'll try long, long long etc. Maybe one of the user-defined literals should mimic that behaviour for convenience.Operators
Unfortunately, the StdLib doesn't provide operators for
std::integral_constant. Therefore, -23_c will not be a std::integral_constant but rather an int (via the implicit conversion operator). I find that surprising.Consider using a custom type, possibly derived from / convertible to
std::integral_constant and provide (metaprogramming) operators for this type.Side remark:
lit is much easier to implement:template
using lit = std::integral_constant;Code Snippets
constexpr int combine(int p)
{
return p;
}
template<class... TT>
constexpr int combine(int val, int p0, TT... pp)
{
return combine(val*10 + p0, pp...);
}
constexpr int parse(char C)
{
return (C >= '0' && C <= '9')
? C - '0'
: throw std::out_of_range("only decimal digits are allowed");
}
template<char... Digits>
constexpr auto operator"" _c()
-> std::integral_constant<int, combine(0, parse(Digits)...)>
{
return {};
}template<std::uintmax_t N>
using lit = std::integral_constant<std::uintmax_t, N>;Context
StackExchange Code Review Q#50910, answer score: 9
Revisions (0)
No revisions yet.