LINUX.ORG.RU

Вопрос по User-defined literals template

 ,


0

2

Даже не знаю как User-define literals правильно перевести на Русский, посему пусть будет так.

Чего хочется - создать Шаблон для UDL, дабы подобный код прекрасно работал

auto a { "1a2bdead99"_hex };
auto b { "1111111111abcde"_hex };

Сам UDL составить не сложно, для входа фиксированной длинны, вот так например

using sha2 = std::array<uint8_t, 32>;

constexpr sha2 operator "" _hex(const char lit[64], size_t size) {
	sha2 result {0};
	
	char hex_str[3] { 0 };
	char byte {0};

	for (size_t i = 0; i < size; i += 2) {
		hex_str[0] = lit[i];
		hex_str[1] = lit[i+1];
    	byte = (char)strtol(hex_str, 0, 16);
    	result[i/2]=byte;
  	}

	return result;
}

Но никак не могу подогнать под него шаблон

Чутка поэксперементировал, вот с этим

template <typename T, size_t n>
constexpr std::array<T,n> arr_test( const T (&arr)[n] ) {
        std::array<T,n> result {0};
        std::copy(std::begin(arr), std::end(arr), result.begin());
        return result;
}


uint8_t my_array[200];
auto x = arr_test(my_array);
cout << x.size() << endl;

И тут всё хорошо, но только если не натягивать его на operator

В ограничениях на UDL говорится, что

If the literal operator is a template, it must have an empty parameter list and can have only one template parameter, which must be a non-type template parameter pack with element type char (in which case it is known as a numeric literal operator template)

И только сейчас до меня дошло, что надо использовать variadic template, в любом случае, если есть идеи как это сделать красиво, буду рад послушать

Вот такой черновик получился, может кому будет полезен

template <typename T, T... CharSet>
constexpr std::array<uint8_t,(sizeof...(CharSet))/2> operator "" _hex () {
        std::array<uint8_t,(sizeof...(CharSet))/2> result;

        T lit[sizeof...(CharSet)] { CharSet... };

        char hex_str[3] { 0 };
        char byte {0};

        for (size_t i = 0; i < sizeof...(CharSet); i += 2) {
                hex_str[0] = lit[i];
                hex_str[1] = lit[i+1];
                byte = (char)strtol(hex_str, 0, 16);
                result[i/2]=byte;
        }

        return result;
}

auto some_hex { "aabbccddee1234"_hex};

cout << "Result type: " << abi::__cxa_demangle(typeid(some_hex).name(),0,0,&status) << endl;
cout << std::hex << setw(2) << setfill('0');
for (auto i : some_hex) {
        cout << +i;
}
cout << endl;
Result type: std::array<unsigned char, 7ul>
aabbccddee1234
★★★★

Последнее исправление: sparks (всего исправлений: 2)
Ответ на: комментарий от fsb4000

Пасиб)) Это черновик, я как раз читаю спецификацию к constexpr и там нет ограничения на вызываемые внутри функции

надо тоже себя к godbolt’у приучить

Кстати о -std=c++20 они там чутка подрастянули шаблоны для UDL

sparks ★★★★
() автор топика
Последнее исправление: sparks (всего исправлений: 1)
Ответ на: комментарий от fsb4000

Воистину

<source>:32:36: error: call to non-'constexpr' function 'long int strtol(const char*, char**, int)'

   32 |                 byte = (char)strtol(hex_str, 0, 16);

      |                              ~~~~~~^~~~~~~~~~~~~~~~

Значит если хочешь Constexpr, то всё в цепочке должно быть Constexpr

sparks ★★★★
() автор топика
Ответ на: комментарий от sparks

Решил вывести, получил ошибку, вроде поправил, теперь не выходит за границы массива.

https://gcc.godbolt.org/z/f7qY7x

Значит если хочешь Constexpr, то всё в цепочке должно быть Constexpr

Нет. Только в том пути по которому проходит constexpr, смотри на функцию: constexpr_convert. Там есть throw, который не constexpr, но до тех пор пока выполнение функции туда не доходит, то всё норм, а если дойдёт, то будет ошибка компиляции

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 3)
Ответ на: комментарий от fsb4000

Окэ, значит всё что считается в Compile time должно быть Constexpr, а как вот это инстанцируется?

template<Sha A>
constexpr auto operator "" _hex() 
{
    return A.result;	
}

Компилятор подставляет

_hex<char, 'a', 'b', 'c'> ()

Как оно делает deduct на Sha A???

Всё норм)) это как раз новшества c++20, в 17м не собирается

sparks ★★★★
() автор топика
Последнее исправление: sparks (всего исправлений: 2)
Ответ на: комментарий от sparks

Полезная тема, проверил свой пример на MSVC, там не работает, а они писали что реализовали http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0732r2.pdf в 19.26, а на деле даже в 19.28 Preview не работает. Создал баг репорт, может до релиза 19.28 исправят…

А по поводу constexpr, самая помогающая фича писать constexpr-friendly из С++20 это:

https://en.cppreference.com/w/cpp/types/is_constant_evaluated

Поддерживается во всех трёх компиляторах (в gcc и clang начиная с 9)

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от fsb4000

я на таком варианте пока остановился, пока хватает, может потом ещё поковыряю

constexpr uint8_t char2hex(char input) {
    if (input >= '0' && input <= '9')
        return input - '0';
    if (input >= 'A' && input <= 'F')
        return input - 'A' + 10;
    if (input >= 'a' && input <= 'f')
        return input - 'a' + 10;

    throw std::runtime_error("Incorrect symbol in hex string");
}

template <char... T>
constexpr size_t hex_size() {
    return (sizeof...(T))/2 + ((sizeof...(T))%2);
}

template <typename T, T... CharSet>
constexpr std::array<uint8_t, hex_size<CharSet...>()> operator "" _hex () {
        std::array<uint8_t, hex_size<CharSet...>()> result {0};
        T lit[] { '0', CharSet... };

        char byte {0};

        for (size_t i = 1 ^ (sizeof...(CharSet)%2); i < sizeof(lit); i += 2) {
                byte = char2hex(lit[i]) << 4 | char2hex(lit[i+1]);
                result[i/2]=byte;
        }

        return result;
}
sparks ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.