No, memcmp
no es adecuado para hacer esto. Y la reflexión en C ++ es insuficiente para hacer esto en este momento (habrá compiladores experimentales que admitirán la reflexión lo suficientemente fuerte como para hacerlo ya, y c ++ 23 podría tener las características que necesita).
Sin una reflexión incorporada, la forma más fácil de resolver su problema es hacer una reflexión manual.
Toma esto:
struct some_struct {
int x;
double d1, d2;
char c;
};
queremos hacer la mínima cantidad de trabajo para poder comparar dos de estos.
Si tenemos:
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 );
}
para c ++ 11 , entonces:
template<class S>
bool are_equal( S const& lhs, S const& rhs ) {
return as_tie(lhs) == as_tie(rhs);
}
hace un trabajo bastante decente.
Podemos extender este proceso para que sea recursivo con un poco de trabajo; en lugar de comparar vínculos, compare cada elemento envuelto en una plantilla, y esa plantilla operator==
aplica esta regla de manera recursiva (ajustando el elemento as_tie
para comparar) a menos que el elemento ya tenga un funcionamiento ==
y maneje matrices.
Esto requerirá un poco de una biblioteca (¿100 líneas de código?) Junto con la escritura de un poco de datos de "reflexión" manuales por miembro. Si el número de estructuras que tiene es limitado, podría ser más fácil escribir el código por estructura manualmente.
Probablemente hay formas de obtener
REFLECT( some_struct, x, d1, d2, c )
para generar la as_tie
estructura usando macros horribles. Pero as_tie
es lo suficientemente simple. En c ++ 11 la repetición es molesta; esto es útil:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
En esta situación y en muchas otras. Con RETURNS
, la escritura as_tie
es:
auto as_tie(some_struct const& s)
RETURNS( std::tie( s.x, s.d1, s.d2, s.c ) )
eliminando la repetición.
Aquí hay una puñalada para hacerlo recursivo:
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) (totalmente recursivo, incluso admite matrices de matrices):
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>{} ) )
Ejemplo en vivo .
Aquí uso un std::array
de refl_tie
. Esto es mucho más rápido que mi tupla anterior de refl_tie en tiempo de compilación.
también
template<class T,
typename std::enable_if< !std::is_class<T>{}, bool>::type = true
>
auto refl_tie( T const& t )
RETURNS(std::cref(t))
usar std::cref
aquí en lugar de std::tie
podría ahorrar tiempo de compilación, ya que cref
es una clase mucho más simple que tuple
.
Finalmente, deberías agregar
template<class T, std::size_t N, class...Ts>
auto refl_tie( T(&t)[N], Ts&&... ) = delete;
lo que evitará que los miembros de la matriz se descompongan en punteros y vuelvan a caer en la igualdad de puntero (que probablemente no desee de las matrices).
Sin esto, si pasa una matriz a una estructura no reflejada, recurre a una estructura de puntero a no reflejada refl_tie
, que funciona y devuelve tonterías.
Con esto, terminas con un error en tiempo de compilación.
El soporte para la recursividad a través de tipos de biblioteca es complicado. Podrías std::tie
ellos:
template<class T, class A>
auto refl_tie( std::vector<T, A> const& v )
RETURNS( std::tie(v) )
pero eso no admite la recursividad a través de él.