Я хочу сравнить структуры в общем виде, и я сделал что-то вроде этого (я не могу поделиться фактическим источником, поэтому попросите более подробную информацию, если это необходимо):
template<typename Data>
bool structCmp(Data data1, Data data2)
{
void* dataStart1 = (std::uint8_t*)&data1;
void* dataStart2 = (std::uint8_t*)&data2;
return memcmp(dataStart1, dataStart2, sizeof(Data)) == 0;
}
В основном это работает так, как задумано, за исключением того, что иногда оно возвращает false, даже если два экземпляра структуры имеют идентичные члены (я проверял с помощью отладчика eclipse). После некоторого поиска я обнаружил, что он memcmp
может потерпеть неудачу из-за заполнения используемой структуры.
Есть ли более правильный способ сравнения памяти, который безразличен для заполнения? Я не могу изменить используемые структуры (они являются частью API, который я использую), и многие различные используемые структуры имеют несколько различных членов и, следовательно, не могут сравниваться по отдельности в общем виде (насколько мне известно).
Изменить: я, к сожалению, застрял с C ++ 11. Должен был упомянуть об этом раньше ...
memcmp
включаете эти биты заполнения в ваше сравнение.==
оператор. Использованиеmemcmp
ненадежно, и рано или поздно вы столкнетесь с каким-то классом, который должен «делать это немного иначе, чем другие». Это очень чисто и эффективно, чтобы реализовать это в операторе. Фактическое поведение будет полиморфным, но исходный код будет чистым ... и, очевидно.Ответы:
Нет,
memcmp
не подходит для этого. И рефлексии в C ++ недостаточно, чтобы сделать это на данный момент (будут экспериментальные компиляторы, которые уже достаточно сильны для рефлексии, чтобы сделать это, и c ++ 23 может иметь необходимые вам функции).Без встроенного отражения, самый простой способ решить вашу проблему - это сделать некоторое ручное отражение.
Возьми это:
мы хотим выполнить минимальный объем работы, чтобы мы могли сравнить два из них.
Если мы имеем:
или
для c ++ 11 , то:
делает довольно приличную работу
Мы можем расширить этот процесс, чтобы он был рекурсивным с небольшим количеством работы; вместо сравнения связей сравнивайте каждый элемент, обернутый в шаблон, и этот шаблон
operator==
рекурсивно применяет это правило (обертывание элементаas_tie
для сравнения), если элемент уже не имеет работающего элемента==
и не обрабатывает массивы.Для этого потребуется немного библиотеки (100 строк кода?) Вместе с написанием небольшого количества ручных данных «отражения» для каждого члена. Если количество имеющихся у вас структур ограничено, может быть проще написать код для каждой структуры вручную.
Есть, вероятно, способы получить
генерировать
as_tie
структуру, используя ужасные макросы. Ноas_tie
достаточно просто. В с ++ 11 повторение раздражает; это полезно:в этой ситуации и многие другие. С
RETURNS
написаниемas_tie
:удаляя повторение
Вот попытка сделать это рекурсивным:
c ++ 17 refl_tie (array) (полностью рекурсивный, даже поддерживает массивы-массивы):
Живой пример .
Здесь я использую
std::array
изrefl_tie
. Это намного быстрее, чем мой предыдущий кортеж refl_tie во время компиляции.Также
использование
std::cref
здесь вместоstd::tie
может сэкономить на издержках времени компиляции, так какcref
это намного более простой класс, чемtuple
.Наконец, вы должны добавить
что предотвратит распад членов массива на указатели и возврат к равенству указателей (что, вероятно, не требуется для массивов).
Без этого, если вы передаете массив в неотраженную структуру, он возвращается к указателю на неотраженную структуру
refl_tie
, которая работает и возвращает бессмыслицу.При этом вы получите ошибку во время компиляции.
Поддержка рекурсии через типы библиотек довольно сложна. Вы могли бы
std::tie
их:но это не поддерживает рекурсию через это.
источник
as_tie
. Начиная с C ++ 14 это выводится автоматически. Вы можете использоватьauto as_tie (some_struct const & s) -> decltype(std::tie(s.x, s.d1, s.d2, s.c));
в C ++ 11. Или явно указать тип возвращаемого значения.as_tie
поддержку, автоматически работает) и поддержку элементов массива, не детализирована, но это возможно.inline
Ключевое слово должно убрать несколько ошибок определения. Используйте кнопку [задать вопрос] после того, как вы получите минимальный воспроизводимый примерВы правы, что заполнение мешает вам сравнивать произвольные типы таким способом.
Есть меры, которые вы можете предпринять:
Data
то, например, gcc имеет__attribute__((packed))
. Это влияет на производительность, но, возможно, стоит попробовать. Тем не менее, я должен признать, что не знаю,packed
позволяет ли вам полностью запретить заполнение. ГКК док говорит:Data
то, по крайней мере,std::has_unique_object_representations<T>
можете сказать, даст ли ваше сравнение правильные результаты:и далее:
PS: я обращался только к отступам, но не забывайте, что типы, которые могут сравниваться одинаково для экземпляров с разным представлением в памяти, ни в коем случае не редкость (например
std::string
,std::vector
и многие другие).источник
memcmp
на структурах без заполнения и выполнятьoperator==
только при необходимости.Вкратце: не возможно в общем смысле.
Проблема с
memcmp
в том, что заполнение может содержать произвольные данные, и, следовательно,memcmp
может произойти сбой. Если бы был способ выяснить, где находится заполнение, вы могли бы обнулить эти биты и затем сравнить представления данных, что проверило бы на равенство, если бы члены были тривиально сопоставимы (что не так, т. Е.std::string
Поскольку две строки могут содержат разные указатели, но два указанных массива одинаковы). Но я не знаю способа добраться до заполнения структур. Вы можете попытаться указать компилятору упаковать структуры, но это замедлит доступ и на самом деле не гарантирует его работу.Самый простой способ реализовать это - сравнить всех участников. Конечно, это не возможно в общем виде (пока мы не получим отражения времени компиляции и мета-классы в C ++ 23 или более поздней версии). Начиная с C ++ 20, можно создавать значения по умолчанию
operator<=>
но я думаю, что это также возможно только в качестве функции-члена, поэтому, опять же, это не совсем применимо. Если вам повезло, и у всех структур, которые вы хотите сравнить, естьoperator==
определенные, вы, конечно, можете просто использовать это. Но это не гарантировано.РЕДАКТИРОВАТЬ: Хорошо, на самом деле есть совершенно хакерский и несколько общий способ для агрегатов. (Я только написал преобразование в кортежи, у них есть оператор сравнения по умолчанию). godbolt
источник
C ++ 20 поддерживает сопоставления по умолчанию
источник
==
or<=>
могут быть выполнены только на уровне класса.Предполагая данные POD, оператор присваивания по умолчанию копирует только байты члена. (на самом деле не уверен на 100%, не верьте мне на слово)
Вы можете использовать это в ваших интересах:
источник
Я полагаю, что вы можете основать решение на удивительно коварном вуду Антония Полухина в
magic_get
библиотеке - для структур, а не для сложных классов.С помощью этой библиотеки мы можем перебирать различные поля структуры с их соответствующим типом в чисто общем шаблонном коде. Энтони, например, использовал это, чтобы иметь возможность потоковой передачи произвольных структур в выходной поток с правильными типами, совершенно обобщенно. Само собой разумеется, что сравнение также может быть возможным применением этого подхода.
... но вам нужен C ++ 14. По крайней мере, это лучше, чем C ++ 17 и последующие предложения в других ответах :-P
источник