Странная ошибка в существующем коде Crypto , указывающая на void*

#c #casting #void-pointers #crypto #reinterpret-cast

#c #Кастинг #void-указатели #crypto #переинтерпретировать-приведение

Вопрос:

Я получаю странную ошибку приведения при компиляции с помощью Crypto
(исходный код Crypto напрямую #include обрабатывается моим приложением в той же сборке.
Все *.cpp исходные файлы Crypto добавляются в CMake и непосредственно компилируются. Обычно это работает нормально.)

 /*******************************************
** Snippet of unedited Crypto   source code:
**     misc.h
*******************************************/

template <class T>
inline void SecureWipeArray(T *buf, size_t n)
{
    if (sizeof(T) % 8 == 0 amp;amp; GetAlignmentOf<T>() % GetAlignmentOf<word64>() == 0)
        SecureWipeBuffer(reinterpret_cast<word64 *>(static_cast<void *>(buf)), n * (sizeof(T)/8)); //error
    else if (sizeof(T) % 4 == 0 amp;amp; GetAlignmentOf<T>() % GetAlignmentOf<word32>() == 0)
        SecureWipeBuffer(reinterpret_cast<word32 *>(static_cast<void *>(buf)), n * (sizeof(T)/4)); //error
    else if (sizeof(T) % 2 == 0 amp;amp; GetAlignmentOf<T>() % GetAlignmentOf<word16>() == 0)
        SecureWipeBuffer(reinterpret_cast<word16 *>(static_cast<void *>(buf)), n * (sizeof(T)/2)); //error
    else
        SecureWipeBuffer(reinterpret_cast<byte *>(static_cast<void *>(buf)), n * sizeof(T)); //error
}
 

Все четыре строки выдают одну и ту же ошибку:

‘static_cast’: не удается преобразовать из ‘T *’ в ‘void *’
‘CryptoPP::SecureWipeBuffer’: не найдена соответствующая перегруженная функция

Я компилирую с C 20, но это не должно вызывать проблем со сбоями при компиляции кода до C 20, верно?

Я написал класс «SecArray», который наследует std::array и использует SecureWipeArray при уничтожении объекта.
(Примечание: должен ли деструктор быть виртуальным? std::array не имеет никаких деструкторов)

 namespace myNamespace
{

    template<class T, size_t length>
    class array : public std::array<T, length>
    {
    private:
        using base = std::array<T, length>;

    public:
        inline ~array() noexcept
        {
            SecureWipeArray(base::data(), length);
        }

        constexpr inline operator T* () noexcept { return base::data(); }
        constexpr inline operator T const* () const noexcept { return base::data(); }
    };
}
 

Это единственный раз, когда мой код вызывает SecureWipeArray напрямую.
(Я также написал несколько классов контейнеров, которые используют std контейнеры с CryptoPP::AllocatorWithCleanup , которые, в свою очередь, вызывают SecureWipeArray )

Но T все равно должен быть базовый тип, например char , uint , string ,
и т.д. Поэтому я не уверен, что вызывает эту ошибку.
Где мне следует искать потенциальную причину?

Комментарии:

1. Сообщение об ошибке должно продолжаться и указывать, какой тип T находится в расширении шаблона?

2. @RichardCritten По-видимому, Microsoft Visual Studio не обеспечивает такого уровня детализации ошибки, даже при компиляции проектов CMake с использованием Ninja? Есть ли «подробная» опция, которую я должен включить, чтобы получить более подробную информацию об ошибке?

3. @rustyx Нет, это отдельная ошибка. SecureWipeBuffer вызывается функция, которую компилятор не может понять, не пройдя сначала мимо void* проблемы.

4. @n.’местоимения’м. О, ГЕНИАЛЬНО. Это потому, что я где-то объявил array<const char> или что-то в этом роде. Поэтому я должен использовать conditional_t<is_const_v<...>...> для проверки наличия const типов по T мере необходимости и const_cast по мере необходимости. Давайте попробуем это и посмотрим, что произойдет. Спасибо за совет!!

5. Вы смотрите на представление ошибок или представление вывода компилятора? Вывод компилятора должен показывать гораздо больше деталей, чем это.

Ответ №1:

Если у вас есть массив std, который является const , любая попытка изменить его содержимое является неопределенным поведением.

Это означает, что поведение вашей программы до и после записи не имеет ограничений, налагаемых на нее стандартом C . Да, UB может привести к перемещению во времени, и это так.

Наиболее вероятными последствиями являются то, что запись не происходит, не весь код видит запись (непоследовательным образом), и / или данные находятся на защищенной странице или ПЗУ, и ваша программа или компьютер выходят из строя.

Данные Const (в отличие от указателей const) в C на самом деле являются const .

Если вы хотите это сделать, удалите const в strange и предоставьте ему интерфейс const . Тогда то, что вы хотите сделать, является законным.

Однако не используйте объединения для ввода pun . В большинстве случаев это незаконно в C .

 template<class T, size_t length>
struct array<T const, length> : private std::array<T, length>
{
  using base=std::array<T, length>;
  // then forward:
  T const*begin()const{return base::begin();}
  // etc
  array(std::array<T const, length> arr):base(arr){}
};
 

Комментарии:

1. Использование специализации шаблона (и, возможно, наследования) для обработки оболочек const — лучший подход, чем моя сумасшедшая идея объединения. Спасибо, что указали мне правильное направление.