Приветствую всех С++ гуру. На днях столкнулся с очередной особенностью С++17, а именно: невозможность преобразовать указатели разных типов друг к другу в compile-time.
Мне в моей библиотеке необходим специальный класс Buffer, для работы с буферами данных разного типа, в котором хотелось бы иметь возможность работать с указателями во время компиляции, в том числе. Проблема:
принимает любой указатель без необходимости явного преобразования, но после, непонятно как реализовать операцию инкремента, т.к. компилятор запрещает преобразования между указателями void* и, например, uint8_t* — для того чтобы можно было как-то сдвинуть указатель буфера.
работает, но теперь неудобно работать с классом, так как нужно явно кастировать указатели разных типов, в том числе и void*, к указателям на uint8_t*.
Класс Buffer является оберткой для "сырых" данных. Размер m_size для любых буферов указывается в байтах. Операция инкремент должна сдвигать буфер на один байт. Просьба помочь с дизайном и заодно советом, как реализовать операцию инкремент в compile-time для такого случая, или объяснить почему это не возможно.
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) — не разрешен во времени компиляции. В этом и вопрос, а как "двигать" указатель без каста тогда?
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
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. Посмотрим, что скажут последние версии.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Да, и у меня то же самое. Я сейчас тяну обновления для студий: 2017 — 5.9.17 и 2019 — 6.3.9. Посмотрим, что скажут последние версии.
У меня рабочая версия VS 2017 — 15.9.17 (последняя) — на ней не работает. На 19-ой под https://gcc.godbolt.org/ — вроде как работает, но к сожалению это пока не мой вариант
Насколько я понимаю, это ограничения стандарта, т.к. всякие aliasing-и и UB внутри компилятора они пока не могут себе позволить
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*.
В лоб задача нерешаема, по определению — на этапе компиляции никаких адресов нет, так что и преобразовать ничего невозможно.
Здравствуйте, Videoman, Вы писали:
wl.>>на 19-й тоже не работает, ни v142, ни llvm
V>А как тогда вот это понимать ?
Я тоже только что попробовал: самая последняя студия — 2019(16.3.9), самый последний тулсет (v142), самый последний стандарт (Preview — Features from the Latest C++ Working Draft (std:c++latest)) — все та же ошибка. Похоже, так тому и быть.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, 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-=(), могут быть реализованы на этапе компиляции.