Custom literal for MPL sequence: is this legal?

The ability to convert a string passed to a literal operator to an MPL sequence will be useful, since then we can control the generation of code based on the contents of the string. I used to think that this was impossible, since the arguments to the constexpr function are not considered constant expressions inside the body of the function. 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 cause undefined behavior, or is it legal?

+6
source share
1 answer

It is right. The conditional statement does not evaluate the operand if necessary.

You make it unnecessarily complicated. Try using operator literal string patterns (which are supported by both Clang and GCC). They were originally planned for C ++ 1Y - the proposal was rejected (I don’t know why, it was made by Richard Smith, and this function is important).

And if this is not portable enough (since at the moment it’s just a compiler extension), 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) 
+3
source

Source: https://habr.com/ru/post/974918/


All Articles