Ні, memcmp
не підходить для цього. І відображення в C ++ недостатньо для цього на даний момент (збираються експериментальні компілятори, які підтримують відображення досить сильно, щоб це зробити вже, і c ++ 23 може мати необхідні функції).
Без вбудованої рефлексії найпростіший спосіб вирішити свою проблему - це зробити певну ручну рефлексію.
Прийняти це:
struct some_struct {
int x;
double d1, d2;
char c;
};
ми хочемо виконати мінімальну кількість роботи, щоб порівняти дві з них.
Якщо у нас є:
auto as_tie(some_struct const& s){
return std::tie( s.x, s.d1, s.d2, s.c );
}
або
auto as_tie(some_struct const& s)
-> decltype(std::tie( s.x, s.d1, s.d2, s.c ))
{
return std::tie( s.x, s.d1, s.d2, s.c );
}
для c ++ 11 , тоді:
template<class S>
bool are_equal( S const& lhs, S const& rhs ) {
return as_tie(lhs) == as_tie(rhs);
}
робить досить гідну роботу.
Ми можемо розширити цей процес рекурсивно, доклавши трохи роботи; замість того, щоб порівнювати зв'язки, порівнюйте кожен елемент, загорнутий у шаблон, і цей шаблон operator==
рекурсивно застосовує це правило (загортання елемента as_tie
для порівняння), якщо елемент вже не працює ==
, і обробляє масиви.
Для цього знадобиться трохи бібліотеки (100ish рядків коду?) Разом із написанням трохи даних про "відображення" вручну на кожного члена. Якщо кількість структур у вас обмежена, можливо, буде простіше написати код структури вручну.
Ймовірно, способи отримати
REFLECT( some_struct, x, d1, d2, c )
для створення as_tie
структури за допомогою жахливих макросів. Але as_tie
досить просто. У c ++ 11 повторення дратує; це корисно:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
у цій ситуації та багатьох інших. З RETURNS
, написання as_tie
:
auto as_tie(some_struct const& s)
RETURNS( std::tie( s.x, s.d1, s.d2, s.c ) )
зняття повторення.
Ось таке, що робить його рекурсивним:
template<class T,
typename std::enable_if< !std::is_class<T>{}, bool>::type = true
>
auto refl_tie( T const& t )
RETURNS(std::tie(t))
template<class...Ts,
typename std::enable_if< (sizeof...(Ts) > 1), bool>::type = true
>
auto refl_tie( Ts const&... ts )
RETURNS(std::make_tuple(refl_tie(ts)...))
template<class T, std::size_t N>
auto refl_tie( T const(&t)[N] ) {
// lots of work in C++11 to support this case, todo.
// in C++17 I could just make a tie of each of the N elements of the array?
// in C++11 I might write a custom struct that supports an array
// reference/pointer of fixed size and implements =, ==, !=, <, etc.
}
struct foo {
int x;
};
struct bar {
foo f1, f2;
};
auto refl_tie( foo const& s )
RETURNS( refl_tie( s.x ) )
auto refl_tie( bar const& s )
RETURNS( refl_tie( s.f1, s.f2 ) )
c ++ 17 refl_tie (масив) (повністю рекурсивний, навіть підтримує масиви масивів):
template<class T, std::size_t N, std::size_t...Is>
auto array_refl( T const(&t)[N], std::index_sequence<Is...> )
RETURNS( std::array<decltype( refl_tie(t[0]) ), N>{ refl_tie( t[Is] )... } )
template<class T, std::size_t N>
auto refl_tie( T(&t)[N] )
RETURNS( array_refl( t, std::make_index_sequence<N>{} ) )
Живий приклад .
Тут я використовую std::array
з refl_tie
. Це набагато швидше, ніж мій попередній пакет refl_tie під час компіляції.
Також
template<class T,
typename std::enable_if< !std::is_class<T>{}, bool>::type = true
>
auto refl_tie( T const& t )
RETURNS(std::cref(t))
використання std::cref
тут замість std::tie
може економити на накладних витратах часу компіляції, оскільки cref
це набагато простіший клас, ніж tuple
.
Нарешті, слід додати
template<class T, std::size_t N, class...Ts>
auto refl_tie( T(&t)[N], Ts&&... ) = delete;
що не дозволить членам масиву занепасти до покажчиків і повернутися назад до рівності вказівника (чого, мабуть, не потрібно з масивів).
Без цього, якщо ви передаєте масив нерефлексованій структурі в, він потрапляє назад на структуру вказівника на нерефлексію refl_tie
, яка працює і повертає нісенітницю.
З цим ви закінчуєте помилку часу компіляції.
Підтримка рекурсії через типи бібліотек є складною. Ви можете std::tie
їм:
template<class T, class A>
auto refl_tie( std::vector<T, A> const& v )
RETURNS( std::tie(v) )
але це не підтримує рекурсію через неї.