9

I've been looking into some of the new features of C++11 and am very impressed with some of them, particularly the user defined literals.

These allow you to define literals of the form 999_something where something controls what is done to the 999 to generate the literal. So no more having to use:

#define MEG * 1024 * 1024
int ten_meg = 10 M;

I was thinking this would be nice to implement underscores in large numbers, like 1_000_000_blah which would match the readability of Perl, though the idea that Perl is somehow readable seems quite humorous to me :-)

It would also be handy for binary values like 1101_1110_b and 0011_0011_1100_1111_b.

Obviously because of the _ characters, these will need to be raw mode type, processing a C string, and I'm okay with that.

What I can't figure out is how to deliver a different type based on the operand size. For example:

1101_1110_b

should give a char (assuming char is 8-bit of course) while:

0011_0011_1100_1111_b

would deliver a 16-bit type.

I can get the length of the operand from within the literal operator function operator"" itself (by counting digit chars) but the return type seems to be fixed to the function so I can't return a different type based on this.

Can this be done with a single suffix _b within the user defined types framework, or do I need to resort to splitting the types apart manually (_b8, _b16 and so on) and provide mostly duplicate functions?

Morwenn
  • 21,684
  • 12
  • 93
  • 152
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • I'm not sure if this works, but you might try to overload several literal operator templates for various bit counts. – celtschk Jun 21 '13 at 06:34
  • 1
    It apparently does work: http://akrzemi1.wordpress.com/2012/10/29/user-defined-literals-part-iii/ — I think the template given there should be quite easy to extend to support more types. – celtschk Jun 21 '13 at 06:38
  • 2
    Thinking again of it, what will likely *not* work is using underscores as part of the number: IIUC `1101_1110_b` will *not* call `operator""_b` with (template) argument `1101_1110`, but `operator""_1110_b` with (template) argument `1101`. – celtschk Jun 21 '13 at 06:42
  • Right, user-defined literals can't change the syntax of a literal, just the type and initialization of the resulting expression. Though you could get `"1101_1110"_b` working. – aschepler Jun 21 '13 at 11:37

2 Answers2

6

You need to know the size oh your string, and the only way to achieve that is to have a parameter pack to use sizeof... on. You should be able to achieve what you want with a variadic template operator"":

#include <cstdint>
#include <type_traits>

template<char... String>
auto operator "" _b()
    -> typename std::conditional<sizeof...(String) <= 8,
        uint8_t,
        typename std::conditional<sizeof...(String) <= 16,
            uint16_t,
            uint32_t
        >::type
    >::type
{
    // Do whatever you want here
}

And here is a test case:

int main()
{
    auto a = 10000001_b;
    auto b = 100000001_b;

    std::cout << std::boolalpha;
    std::cout << std::is_same<decltype(a), uint8_t>::value << "\n"; // true
    std::cout << std::is_same<decltype(b), uint16_t>::value << "\n"; // true
}

Unfortunately, that solution can't handle the digit separator. Moreover, the std::conditional machinery is quite ugly. You could probably work something better with boost::mpl::vector, boost::mpl::at and some arithmetic operations.

Morwenn
  • 21,684
  • 12
  • 93
  • 152
  • And now ignore leading 0s in the type deduction ;) No seriously, good answer. – Christian Rau Jun 21 '13 at 11:46
  • 2
    @ChristianRau It's easier to ignore people writing 0s. Moreover, that would be... octal binary! – Morwenn Jun 21 '13 at 11:47
  • @Morwenn: I wish C and C++ would deprecate constants which contain a leading zero and nothing other than decimal digits, *but* define a standard notation for octal constants in such a fashion that code could define a macro (e.g. __OCT(x)) macro which would interpret x as an octal constant in either style of compiler [my personal preferred notation, but for the fact that it would have conflicted with user-defined literal syntax, would have been do say that 8x1234 would be an octal constant, 0x1234 and 16x1234 would be hex, 10x00123 would be decimal, etc. That could have generalized nicely. – supercat May 02 '15 at 16:30
0

Thanks to Morwenn's answer I came up with a full solution to user-defined binary literals for those of us stuck with C++11:

#include <cstdint>
#include <type_traits>

/// User-defined binary literal for C++11
/// @see https://stackoverflow.com/a/538101 / https://gist.github.com/lichray/4153963
/// @see https://stackoverflow.com/a/17229703
namespace detail {

template<class tValueType, char... digits>
struct binary_literal;

template<char... digits>
struct unsigned_binary_literal
{
    using Type = typename std::conditional<sizeof...(digits) <= 8, uint8_t,
                    typename std::conditional<sizeof...(digits) <= 16, uint16_t,
                        typename std::conditional<sizeof...(digits) <= 32, uint32_t, uint64_t>::type
                    >::type
                >::type;
};

template<char... digits>
struct signed_binary_literal
{
    using Type = typename std::conditional<sizeof...(digits) <= 8, int8_t,
                    typename std::conditional<sizeof...(digits) <= 16, int16_t,
                        typename std::conditional<sizeof...(digits) <= 32, int32_t, int64_t>::type
                    >::type
                >::type;
};

template<class tValueType, char high, char... digits>
struct binary_literal<tValueType, high, digits...>
{
    static constexpr tValueType value = (static_cast<tValueType>(high == '1') << (sizeof...(digits))) ^ binary_literal<tValueType, digits...>::value;
};

template<class tValueType, char high>
struct binary_literal<tValueType, high>
{
    static constexpr tValueType value = (high == '1');
};
} // namespace detail

/// C++11 support for binary literal
/// @tparam digits to transform to an unsigned integer
template<char... digits>
constexpr auto operator "" _bin() -> typename detail::unsigned_binary_literal<digits...>::Type
{
    return detail::binary_literal<typename detail::unsigned_binary_literal<digits...>::Type, digits...>::value;
}

/// C++11 support for binary literal
/// @tparam digits to transform to a signed integer
template<char... digits>
constexpr auto operator "" _sbin() -> typename detail::signed_binary_literal<digits...>::Type
{
    return static_cast<typename detail::signed_binary_literal<digits...>::Type>(detail::binary_literal<typename detail::unsigned_binary_literal<digits...>::Type, digits...>::value);
}