سؤال

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?

هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

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);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top