No, memcmp
non è adatto a farlo. E la riflessione in C ++ non è sufficiente per fare questo a questo punto (ci saranno compilatori sperimentali che supportano la riflessione abbastanza forte da farlo già, e c ++ 23 potrebbe avere le caratteristiche di cui hai bisogno).
Senza la riflessione integrata, il modo più semplice per risolvere il problema è eseguire una riflessione manuale.
Prendi questo:
struct some_struct {
int x;
double d1, d2;
char c;
};
vogliamo fare la minima quantità di lavoro in modo da poter confrontare due di questi.
Se abbiamo:
auto as_tie(some_struct const& s){
return std::tie( s.x, s.d1, s.d2, s.c );
}
o
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 );
}
per c ++ 11 , quindi:
template<class S>
bool are_equal( S const& lhs, S const& rhs ) {
return as_tie(lhs) == as_tie(rhs);
}
fa un lavoro abbastanza decente.
Possiamo estendere questo processo in modo ricorsivo con un po 'di lavoro; invece di confrontare i legami, confronta ogni elemento racchiuso in un modello e quel modello operator==
applica ricorsivamente questa regola (avvolgendo l'elemento in as_tie
confronto) a meno che l'elemento non abbia già un funzionamento ==
e gestisca le matrici.
Ciò richiederà un po 'di libreria (100 righe di codice?) Insieme alla scrittura di un po' di dati "riflessi" manuali per membro. Se il numero di strutture che hai è limitato, potrebbe essere più semplice scrivere manualmente il codice per struttura.
Probabilmente ci sono modi per ottenere
REFLECT( some_struct, x, d1, d2, c )
per generare la as_tie
struttura usando macro orribili. Ma as_tie
è abbastanza semplice. In c ++ 11 la ripetizione è fastidiosa; questo è utile:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
in questa situazione e molti altri. Con RETURNS
, la scrittura as_tie
è:
auto as_tie(some_struct const& s)
RETURNS( std::tie( s.x, s.d1, s.d2, s.c ) )
rimuovendo la ripetizione.
Ecco una pugnalata per renderlo ricorsivo:
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 (array) (completamente ricorsivo, supporta anche array di array):
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>{} ) )
Esempio dal vivo .
Qui uso un std::array
di refl_tie
. Questo è molto più veloce della mia precedente tupla di refl_tie al momento della compilazione.
Anche
template<class T,
typename std::enable_if< !std::is_class<T>{}, bool>::type = true
>
auto refl_tie( T const& t )
RETURNS(std::cref(t))
usare std::cref
qui invece di std::tie
potrebbe risparmiare sui tempi di compilazione, in quanto cref
è una classe molto più semplice di tuple
.
Infine, dovresti aggiungere
template<class T, std::size_t N, class...Ts>
auto refl_tie( T(&t)[N], Ts&&... ) = delete;
che impedirà ai membri dell'array di decadere in puntatori e ricadere sull'uguaglianza dei puntatori (che probabilmente non si desidera dagli array).
Senza questo, se si passa un array a una struttura non riflessa, si ricorre alla struttura puntatore a non riflessa refl_tie
, che funziona e restituisce assurdità.
Con questo, si finisce con un errore in fase di compilazione.
Il supporto per la ricorsione attraverso i tipi di libreria è complicato. Potrestistd::tie
loro:
template<class T, class A>
auto refl_tie( std::vector<T, A> const& v )
RETURNS( std::tie(v) )
ma ciò non supporta la ricorsione attraverso di essa.