This function template is constexpr if and only if each of To, From and the types of all subobjects of To and From:
is not a union type;
is not a pointer type;
is not a pointer to member type;
is not a volatile-qualified type; and
has no non-static data member of reference type.
Приветствую всех С++ гуру. На днях столкнулся с очередной особенностью С++17, а именно: невозможность преобразовать указатели разных типов друг к другу в compile-time.
Мне в моей библиотеке необходим специальный класс Buffer, для работы с буферами данных разного типа, в котором хотелось бы иметь возможность работать с указателями во время компиляции, в том числе. Проблема:
принимает любой указатель без необходимости явного преобразования, но после, непонятно как реализовать операцию инкремента, т.к. компилятор запрещает преобразования между указателями void* и, например, uint8_t* — для того чтобы можно было как-то сдвинуть указатель буфера.
работает, но теперь неудобно работать с классом, так как нужно явно кастировать указатели разных типов, в том числе и void*, к указателям на uint8_t*.
Класс Buffer является оберткой для "сырых" данных. Размер m_size для любых буферов указывается в байтах. Операция инкремент должна сдвигать буфер на один байт. Просьба помочь с дизайном и заодно советом, как реализовать операцию инкремент в compile-time для такого случая, или объяснить почему это не возможно.
Здравствуйте, rg45, Вы писали:
R>Да, и у меня то же самое. Я сейчас тяну обновления для студий: 2017 — 5.9.17 и 2019 — 6.3.9. Посмотрим, что скажут последние версии.
У меня рабочая версия VS 2017 — 15.9.17 (последняя) — на ней не работает. На 19-ой под https://gcc.godbolt.org/ — вроде как работает, но к сожалению это пока не мой вариант
Насколько я понимаю, это ограничения стандарта, т.к. всякие aliasing-и и UB внутри компилятора они пока не могут себе позволить
В C++ 20 появился std::bit_cast(), но он никем ещё не реализован, вроде.
Здравствуйте, Videoman, Вы писали:
V>Приветствую всех С++ гуру. На днях столкнулся с очередной особенностью С++17, а именно: невозможность преобразовать указатели разных типов друг к другу в compile-time. V>Мне в моей библиотеке необходим специальный класс Buffer, для работы с буферами данных разного типа, в котором хотелось бы иметь возможность работать с указателями во время компиляции, в том числе. Проблема:
Здравствуйте, Videoman, Вы писали:
wl.>>на 19-й тоже не работает, ни v142, ни llvm
V>А как тогда вот это понимать ?
Я тоже только что попробовал: самая последняя студия — 2019(16.3.9), самый последний тулсет (v142), самый последний стандарт (Preview — Features from the Latest C++ Working Draft (std:c++latest)) — все та же ошибка. Похоже, так тому и быть.
--
Не можешь достичь желаемого — пожелай достигнутого.
d:\development\hopsteam\ingest\trunk\htcapture\src\main.cpp(74): error C2131: expression did not evaluate to a constant
d:\development\hopsteam\ingest\trunk\htcapture\src\main.cpp(54): note: failure was caused by a conversion from void* to a pointer-to-object type
R>Формулировка странная, да, но к intptr_t тоже не дает, я уже попробовал. Наверное, это как reinterpret_cast расценивается (п.16). Я уже и через арифметику указателей пробовал его обхитрить, все равно не дает: "failure was caused by subtracting pointers to elements of different arrays". Замуровали демоны.
И не даст. Не может дать даже если бы захотел. На этапе компиляции адресов в принципе нет. Все указатели времени компиляции (аргументы шаблонов или внутри constexpr функций) трактуются как сами объекты. А когда ты начинаешь делать такие касты, ты просишь компилятор из ничего создать новый объект, которого у него нет. Это ограничение именно из-за практической невозможности, не из-за того, что кому-то так захотелось.
Здравствуйте, B0FEE664, Вы писали:
BFE>Топикастеру ничто не мешает написать утилиту, которая будет создавать нужные ему объекты, выполнят в рантайме необходимые посчёты, а результаты записывать в виде С++ кода с уже записанными константами, хотя, конечно, это не очень удобно с точки зрения организации кода и процесса сборки проекта.
Ну послал, так послал . Топикастеру это нафиг не здалось, так как и без всяких constexp эти маленькие классы инлайнятся и растворяются в "ничего". Я просто изучаю новые, для меня, границы С++17.
V>Класс Buffer является оберткой для "сырых" данных. Размер m_size для любых буферов указывается в байтах. Операция инкремент должна сдвигать буфер на один байт. Просьба помочь с дизайном и заодно советом, как реализовать операцию инкремент в compile-time для такого случая, или объяснить почему это не возможно.
Так оставь в объявлении члена uint8_t*, а в конструкторе void*. Таким образом, касты становятся деталью реализации класса и не затрагивают клиентский код. При желании преобразования можно локализовать в специальных закрытых функциях так, что это не будет доставлять дискомфорта даже при имплементации самого класса.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Так оставь в объявлении члена uint8_t*, а в конструкторе void*. Таким образом, касты становятся деталью реализации класса и не затрагивают клиентский код. При желании преобразования можно локализовать в специальных закрытых функциях так, что это не будет доставлять дискомфорта даже при имплементации самого класса.
Ну, собственно, так и было сделано до constexpr. m_data был void*, но как только к конструктору или оператору добавляется constexpr, компилятор тут же начинает ругаться что static_cast<uint8_t*>(m_data) — не разрешен во времени компиляции. В этом и вопрос, а как "двигать" указатель без каста тогда?
Здравствуйте, Videoman, Вы писали:
V>Ну, собственно, так и было сделано до constexpr. m_data был void*, но как только к конструктору или оператору добавляется constexpr, компилятор тут же начинает ругаться что static_cast<uint8_t*>(m_data) — не разрешен во времени компиляции. В этом и вопрос, а как "двигать" указатель без каста тогда?
V>d:\development\hopsteam\ingest\trunk\htcapture\src\main.cpp(74): error C2131: expression did not evaluate to a constant
V>d:\development\hopsteam\ingest\trunk\htcapture\src\main.cpp(54): note: failure was caused by a conversion from void* to a pointer-to-object type
Да, и у меня то же самое. Я сейчас тяну обновления для студий: 2017 — 5.9.17 и 2019 — 6.3.9. Посмотрим, что скажут последние версии.
--
Не можешь достичь желаемого — пожелай достигнутого.
V>У меня рабочая версия VS 2017 — 15.9.17 (последняя) — на ней не работает. На 19-ой под https://gcc.godbolt.org/ — вроде как работает, но к сожалению это пока не мой вариант
Здравствуйте, Videoman, Вы писали:
V>У меня не пашет под VS 2017. Выдает такое: V>
V>d:\development\hopsteam\ingest\trunk\htcapture\src\main.cpp(74): error C2131: expression did not evaluate to a constant
V>d:\development\hopsteam\ingest\trunk\htcapture\src\main.cpp(54): note: failure was caused by a conversion from void* to a pointer-to-object type
Ой, чего я только не пытался сделать — всё таже самая ошибка: failure was caused by a conversion from void* to a pointer-to-object type
Собственно поэтому и пришлось задать вопрос в форуме, как народ решает эту проблему, проблема ли это, и что делать.
V>Это как бы уже поняли, а как тогда строить дизайн класса, типа как я привел? Как двигать указатель, никак?
Дизайн КАКОГО класса? В том, что ты привел, есть приватный член и его инкремент, который совершенно непонятно для чего нужен. Если ты покажешь весь класс, станет понятнее, как его сделать, и для чего тебе инкременитровать void*.
В лоб задача нерешаема, по определению — на этапе компиляции никаких адресов нет, так что и преобразовать ничего невозможно.
Здравствуйте, rg45, Вы писали:
R>Я тоже только что попробовал: самая последняя студия — 2019(16.3.9), самый последний тулсет (v142), самый последний стандарт (Preview — Features from the Latest C++ Working Draft (std:c++latest)) — все та же ошибка. Похоже, так тому и быть.
Спасибо за попытку! Если вдруг случайно появится решение проблемы, прошу дать знать
Здравствуйте, Vamp, Вы писали:
V>Дизайн КАКОГО класса? В том, что ты привел, есть приватный член и его инкремент, который совершенно непонятно для чего нужен. Если ты покажешь весь класс, станет понятнее, как его сделать, и для чего тебе инкременитровать void*. V>В лоб задача нерешаема, по определению — на этапе компиляции никаких адресов нет, так что и преобразовать ничего невозможно.
Ок. У меня со всеми библиотеками уже лет 10 используется такой класс — идея подсмотрена в ASIO:
Он используется во всех операциях ввода вывода, и не только. Прелесть его в том, что если в руках уже есть такой MutableBuffer, то, создавая на основе него другие MutableBuffer и используя только им предоставленные операции, мы можем только сужать диапазон исходного буфера, даже передавая туда неправильные размеры и смещения. При этом в своей реализации он совсем не использует исключения и на практике не вносит overhead при оптимизации.
На практике удалось заметить, что такой подход привел практически к полному исчезновению ошибок выхода за границы буферов чтения/записи, что не может не радовать
Теперь по мере переползания на C++17 решили добавить constexpr. Как видно, все основные операции, кроме operator+=() и operator-=(), могут быть реализованы на этапе компиляции.
Первое что приходит в голову — хранить смещение в отдельном члене, и возвращать смещенный указатель в Data, с которой constexpr убрать. Все равно ее результат не может использоваться в констекспр контексте.
BFE>conversion from cv void* to any pointer-to-object type
BFE>странная формулировка. BFE>К pointer-to-object type нельзя, а, например, к intptr_t можно?
Формулировка странная, да, но к intptr_t тоже не дает, я уже попробовал. Наверное, это как reinterpret_cast расценивается (п.16). Я уже и через арифметику указателей пробовал его обхитрить, все равно не дает: "failure was caused by subtracting pointers to elements of different arrays". Замуровали демоны.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Vamp, Вы писали:
R>>Формулировка странная, да, но к intptr_t тоже не дает, я уже попробовал. Наверное, это как reinterpret_cast расценивается (п.16). Я уже и через арифметику указателей пробовал его обхитрить, все равно не дает: "failure was caused by subtracting pointers to elements of different arrays". Замуровали демоны.
V>И не даст. Не может дать даже если бы захотел. На этапе компиляции адресов в принципе нет.
В принципе адреса могут быть:
const char* p = (char*)0x00004000;
V>Все указатели времени компиляции (аргументы шаблонов или внутри constexpr функций) трактуются как сами объекты. А когда ты начинаешь делать такие касты, ты просишь компилятор из ничего создать новый объект, которого у него нет. Это ограничение именно из-за практической невозможности, не из-за того, что кому-то так захотелось.
Ну почему же...
Всё что требуется от компилятора, это рассматривать константный объект одного типа, как константный объект другого типа:
constexpr int n = 2345;
constexpr char* pn = (char*)&n;
В принципе это реализуемо, но я не уверен, что такое надо добавлять прямо в компилятор.
В данном вопросе я скорее за кодогенерацию, чем за добавление в стандарт новых преобразований.
Топикастеру ничто не мешает написать утилиту, которая будет создавать нужные ему объекты, выполнят в рантайме необходимые посчёты, а результаты записывать в виде С++ кода с уже записанными константами, хотя, конечно, это не очень удобно с точки зрения организации кода и процесса сборки проекта.
Здравствуйте, Videoman, Вы писали:
R>>Теперь ждем, когда он появится в списке запрещенных выражений
V>Это вот от туда, из описания: V>
V>This function template is constexpr if and only if each of To, From and the types of all subobjects of To and From:
V>is not a union type;
V>is not a pointer type;
V>is not a pointer to member type;
V>is not a volatile-qualified type; and
V>has no non-static data member of reference type.
V>Вроде как запрещает, не?
Так bit_cast — это же синтаксический сахар над memcpy с дополнительными проверками типов в компайл-тайме.
"For every complex problem, there is a solution that is simple, neat,
and wrong."