I want to compare structs in a generic way and I've done something like this (I cannot share the actual source, so ask for more details if necessary):
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;
}
This mostly works as intended, except sometimes it returns false even though the two struct instances have identical members (I've checked with eclipse debugger). After some searching I discovered that memcmp can fail due to the struct used being padded.
Is there a more proper way of comparing memory that's indifferent to padding? I'm not able to modify the structs used (they're part of an API I'm using) and the many different structs used has some differing members and thus cannot be compared individually in a generic way (to my knowledge).
Edit: I'm unfortunately stuck with C++11. Should've mentioned this earlier...
No,
memcmpis not suitable to do this. And reflection in C++ is insufficient to do this at this point (there are going to be experimental compilers that support reflection strong enough to do this already, and c++23 might have the features you need).Without built-in reflection, the easiest way to solve your problem is to do some manual reflection.
Take this:
we want to do the minimal amount of work so we can compare two of these.
If we have:
or
for c++11, then:
does a pretty decent job.
We can extend this process to be recursive with a bit of work; instead of comparing ties, compare each element wrapped in a template, and that template's
operator==recursively applies this rule (wrapping the element inas_tieto compare) unless the element already has a working==, and handles arrays.This will require a bit of a library (100ish lines of code?) together with writing a bit of manual per-member "reflection" data. If the number of structs you have is limited, it might be easier to write per-struct code manually.
There are probably ways to get
to generate the
as_tiestructure using horrible macros. Butas_tieis simple enough. In c++11 the repetition is annoying; this is useful:in this situation and many others. With
RETURNS, writingas_tieis:removing the repetition.
Here is a stab at making it recursive:
c++17 refl_tie(array) (fully recursive, even supports arrays-of-arrays):
Live example.
Here I use a
std::arrayofrefl_tie. This is much faster than my previous tuple of refl_tie at compile time.Also
using
std::crefhere instead ofstd::tiecould save on compile-time overhead, ascrefis a much simpler class thantuple.Finally, you should add
which will prevent array members from decaying to pointers and falling back on pointer-equality (which you probably don't want from arrays).
Without this, if you pass an array to a non-reflected struct in, it falls back on pointer-to-non-reflected struct
refl_tie, which works and returns nonsense.With this, you end up with a compile-time error.
Support for recursion through library types is tricky. You could
std::tiethem:but that doesn't support recursion through it.