Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User-defined literal to MPL sequence: is this legal?

Being able to convert the string passed to a literal operator into an MPL sequence would be useful, since we would then be able to control code generation based on the contents of the string. Previously, I thought this to be impossible, since the arguments to a constexpr function are not regarded as constant expressions within the function's body. However, I came up with the following workaround that compiles under Clang 3.4.2 and GCC 4.8.2:

#include <cstdint>
#include <iostream>
#include <typeinfo>

struct string
{
    const uintmax_t m_str[64];
    const size_t m_length;

    template <class... Ts>
    constexpr string(const Ts... ts) :
    m_str{(uintmax_t)ts...}, m_length{sizeof...(Ts)} {}

    constexpr size_t size() const { return m_length; }
    constexpr size_t length() const { return m_length; }
    constexpr uintmax_t operator[](size_t n) const { return m_str[n]; }
};

template <uintmax_t... Ts> struct sequence {};

constexpr auto
operator"" _tag(const char* str, size_t n)
{
    return n == 0 ? string{} :
           n == 1 ? string{str[0]} :
           n == 2 ? string{str[0], str[1]} :
           n == 3 ? string{str[0], str[1], str[2]} :
           n == 4 ? string{str[0], str[1], str[2], str[3]} :
           n == 5 ? string{str[0], str[1], str[2], str[3], str[4]} :
                    string{str[0], str[1], str[2], str[3], str[4], str[5]};
}

int main()
{
    static constexpr auto string = "Hello!"_tag;
    using sequence = sequence<string[0], string[1], string[2], string[3], string[4], string[5]>;
    std::cout << typeid(sequence{}).name() << std::endl;
}

Output:

sequence<72ul, 101ul, 108ul, 108ul, 111ul, 33ul>

Question

Does this code invoke undefined behavior, or is it legal?

like image 982
void-pointer Avatar asked Sep 06 '14 15:09

void-pointer


1 Answers

It is correct. The conditional operator does not evaluate an operand unless necessary.

You're doing it unnecessarily complicated though. Try to use string literal operator templates (which are supported by both Clang and GCC). They were originally planned for C++1Y - the proposal got rejected though (no idea why, it was done by Richard Smith and the feature is essential).

And if that isn't portable enough (since it's just a compiler extension for now), you can use binary recursive Macros and an rtrim class template, like VTMPL:

# define VTMPL_SPLIT_1(s, x) ( x < sizeof(s) ? s[x] : decltype(*s)() )
# define VTMPL_SPLIT_4(s, x) VTMPL_SPLIT_1 (s, x), VTMPL_SPLIT_1 (s, x+1) , VTMPL_SPLIT_1 (s, x+2) , VTMPL_SPLIT_1 (s, x+3)
# define VTMPL_SPLIT_16(s, x) VTMPL_SPLIT_4 (s, x), VTMPL_SPLIT_4 (s, x+4) , VTMPL_SPLIT_4 (s, x+8) , VTMPL_SPLIT_4 (s, x+12)
# define VTMPL_SPLIT_64(s, x) VTMPL_SPLIT_16 (s, x), VTMPL_SPLIT_16 (s, x+16) , VTMPL_SPLIT_16 (s, x+32) , VTMPL_SPLIT_16 (s, x+48)
# define VTMPL_SPLIT_256(s, x) VTMPL_SPLIT_64 (s, x), VTMPL_SPLIT_64 (s, x+64) , VTMPL_SPLIT_64 (s, x+128), VTMPL_SPLIT_64 (s, x+194)
# define VTMPL_SPLIT_1024(s, x) VTMPL_SPLIT_256(s, x), VTMPL_SPLIT_256(s, x+256), VTMPL_SPLIT_256(s, x+512), VTMPL_SPLIT_256(s, x+768)

# define VTMPL_STRING_IMPL(str, n) vtmpl::rtrim<vtmpl::string<VTMPL_SPLIT_##n(str, 0)>>::type

# define VTMPL_STRING(str) VTMPL_STRING_IMPL(str, 64)
# define VTMPL_STRING_256(str) VTMPL_STRING_IMPL(str, 256)
# define VTMPL_STRING_1024(str) VTMPL_STRING_IMPL(str, 1024)
like image 90
Columbo Avatar answered Oct 21 '22 12:10

Columbo