11
\$\begingroup\$

I created a user defined literal _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 <type_traits>

template<typename T, typename U>
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<typename T, typename U>
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<typename Integral, char C, char... Digits>
struct parse
{
    static constexpr Integral value =
        parse<Integral, C>::value * pow(10u, sizeof...(Digits))
        + parse<Integral, Digits...>::value;
};

// Specialization of parse to parse a single
// decimal digit
template<typename Integral, char C>
struct parse<Integral, C>
{
    static_assert(C >= '0' && C <= '9',
                  "only characters in range 0..9 are accepted");

    static constexpr Integral value = C - '0';
};

// User defined literal for std::integral_constant
template<char... Digits>
constexpr auto operator"" _c()
    -> std::integral_constant<int, parse<int, Digits...>::value>
{
    return {};
}

With this literal, wirting 42_c generates an instance of std::integral_constant<int, 42>. Here is a small working example:

int main()
{
    std::cout << 45_c << '\n';   // prints 45
    std::cout << -23_c << '\n';  // prints -23

    static_assert(std::is_same<decltype(58_c), std::integral_constant<int, 58>>::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?

\$\endgroup\$
  • 2
    \$\begingroup\$ Maybe it would be more idiomatic if the type of the literal adjusted to the size of the number, just like for ordinary integer literals. The whole idea can also be simplified by using lit<45> with alias templates, or by multiplying on the fly (sketch: coliru.stacked-crooked.com/a/3cca450bf7c1e866) \$\endgroup\$ – dyp May 17 '14 at 17:19
  • 1
    \$\begingroup\$ Jeremy W. Murphy once suggested exponentiation by squaring: stackoverflow.com/a/16443849/420683 Not sure why you require exponent to be unsigned but then treat the case exponent < 0.. \$\endgroup\$ – dyp May 17 '14 at 17:25
  • \$\begingroup\$ @dyp Exponentiation by squaring indeed sounds good. Concerning the strange check, you are totally right: I actually pulled the pow function from an old project and the template parameter names were irrelevant. I thought that the function couldn't handle negative values and din't read the code again. You're totally right. \$\endgroup\$ – Morwenn May 17 '14 at 18:09
  • \$\begingroup\$ @dyp your first "on the fly" sketch is cool! Exponentiation by squaring is not really needed to speed up anything, because parse still needs a loop though all values. In a simpler loop, your value gets the whole job done, no exponentiation needed. \$\endgroup\$ – iavr May 17 '14 at 18:35
  • \$\begingroup\$ @dyp And the way it handles negative exponents is broken anyway, I forgot a minus sign. I will a least add that since there are still no reviews (working code they said). \$\endgroup\$ – Morwenn May 17 '14 at 18:35
8
\$\begingroup\$

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 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<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 {};
}

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 < 10 is fairly simple because you only need to change the "shifting" factor. I.e. multiply by 8 or 2 instead of 10. For base 16, you also need to add some code to the character -> 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<int, -23> 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<45> is much easier to implement:

template<std::uintmax_t N>
using lit = std::integral_constant<std::uintmax_t, N>;
\$\endgroup\$
  • \$\begingroup\$ The class I wrote with this idea in mind has indeed custom operators. I agree that lit<N> is easier to implement, but I wanted the UDL syntax to work. I think that I will use some (most) of your ideas to improve the overall design. Thanks a lot :) \$\endgroup\$ – Morwenn May 24 '14 at 14:08
  • \$\begingroup\$ @Morwenn Oh, btw. I think it'll currently do the wrong thing for octal literals. So maybe you want at least to detect those. \$\endgroup\$ – dyp May 24 '14 at 14:49
  • \$\begingroup\$ You're right. I don't intend to implement the literals thing (too much work, and my class is math-related, so I expect people to use decimal literals), but warning about the octals literals is definitely a good idea :) \$\endgroup\$ – Morwenn May 24 '14 at 15:32
  • 2
    \$\begingroup\$ Thank you for this clear explanation. For anyone who comes across this looking for a slightly more complete solution, here's an implementation that supports hex, octal, and binary literals, along with the digit separator: gist.github.com/mattbierner/5c698972de0cdd9de86a \$\endgroup\$ – Matt Bierner Sep 19 '15 at 23:32
  • \$\begingroup\$ @MattBierner Your code does assume that 'A' through 'F' are represented by consecutive numbers, though.. \$\endgroup\$ – dyp Sep 19 '15 at 23:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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