Je veux comparer les structures de manière générique et j'ai fait quelque chose comme ça (je ne peux pas partager la source réelle, alors demandez plus de détails si nécessaire):
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;
}
Cela fonctionne principalement comme prévu, sauf qu'il retourne parfois false même si les deux instances de structure ont des membres identiques (j'ai vérifié avec le débogueur eclipse). Après quelques recherches, j'ai découvert que cela memcmp
pouvait échouer en raison du remplissage de la structure utilisée.
Existe-t-il un moyen plus approprié de comparer la mémoire indifférente au rembourrage? Je ne suis pas en mesure de modifier les structures utilisées (elles font partie d'une API que j'utilise) et les nombreuses structures différentes utilisées ont des membres différents et ne peuvent donc pas être comparées individuellement de manière générique (à ma connaissance).
Edit: je suis malheureusement bloqué avec C ++ 11. J'aurais dû le mentionner plus tôt ...
memcmp
incluez ces bits de remplissage dans votre comparaison.==
opérateur. L'utilisationmemcmp
n'est pas fiable et, tôt ou tard, vous aurez affaire à une classe qui doit «le faire un peu différemment des autres». C'est très propre et efficace de mettre cela en œuvre chez un opérateur. Le comportement réel sera polymorphe mais le code source sera propre ... et, évident.Réponses:
Non,
memcmp
ne convient pas pour cela. Et la réflexion en C ++ est insuffisante pour ce faire à ce stade (il y aura des compilateurs expérimentaux qui prennent en charge la réflexion suffisamment forte pour le faire déjà, et c ++ 23 pourrait avoir les fonctionnalités dont vous avez besoin).Sans réflexion intégrée, la façon la plus simple de résoudre votre problème consiste à effectuer une réflexion manuelle.
Prends ça:
nous voulons faire le minimum de travail afin de pouvoir comparer deux d'entre eux.
Si nous avons:
ou
pour c ++ 11 , alors:
fait un travail assez décent.
Nous pouvons étendre ce processus pour qu'il soit récursif avec un peu de travail; au lieu de comparer les liens, comparez chaque élément encapsulé dans un modèle, et ce modèle
operator==
applique récursivement cette règle (encapsulant l'élémentas_tie
pour comparer) à moins que l'élément n'ait déjà un travail==
et gère les tableaux.Cela nécessitera un peu de bibliothèque (100 lignes de code?) Avec l'écriture d'un peu de données de «réflexion» manuelles par membre. Si le nombre de structures que vous avez est limité, il pourrait être plus facile d'écrire manuellement du code par structure.
Il existe probablement des moyens
pour générer la
as_tie
structure en utilisant d'horribles macros. Maisas_tie
c'est assez simple. En c ++ 11, la répétition est ennuyeuse; c'est utile:dans cette situation et bien d'autres. Avec
RETURNS
, écrireas_tie
c'est:supprimer la répétition.
Voici un coup de couteau pour le rendre récursif:
c ++ 17 refl_tie (array) (entièrement récursif, prend même en charge les tableaux de tableaux):
Exemple en direct .
Ici, j'utilise un
std::array
derefl_tie
. C'est beaucoup plus rapide que mon précédent tuple de refl_tie au moment de la compilation.Aussi
utiliser
std::cref
ici au lieu destd::tie
pourrait économiser sur la surcharge au moment de la compilation, commecref
c'est une classe beaucoup plus simple quetuple
.Enfin, vous devez ajouter
ce qui empêchera les membres du tableau de se décomposer en pointeurs et de retomber sur l'égalité du pointeur (ce que vous ne voulez probablement pas des tableaux).
Sans cela, si vous passez un tableau à une structure non réfléchie, il retombe sur une structure pointeur vers non réfléchie
refl_tie
, qui fonctionne et renvoie un non-sens.Avec cela, vous vous retrouvez avec une erreur de compilation.
La prise en charge de la récursivité à travers les types de bibliothèques est délicate. Vous pourriez
std::tie
les:mais cela ne prend pas en charge la récursivité à travers elle.
la source
as_tie
. À partir de C ++ 14, cela se déduit automatiquement. Vous pouvez utiliserauto as_tie (some_struct const & s) -> decltype(std::tie(s.x, s.d1, s.d2, s.c));
en C ++ 11. Ou indiquez explicitement le type de retour.as_tie
support, fonctionne automatiquement) et les membres du tableau de support ne sont pas détaillés, mais c'est possible.inline
mot-clé devrait faire disparaître les erreurs de définition multiples. Utilisez le bouton [poser une question] après avoir obtenu un exemple reproductible minimalVous avez raison de dire que le remplissage vous empêche de comparer des types arbitraires de cette manière.
Vous pouvez prendre des mesures:
Data
alors par exemple gcc a__attribute__((packed))
. Cela a un impact sur les performances, mais cela pourrait valoir la peine de l'essayer. Cependant, je dois admettre que je ne sais pas sipacked
vous permet d'interdire complètement le rembourrage. Le doc Gcc dit:Data
au moinsstd::has_unique_object_representations<T>
vous dire si votre comparaison donnera des résultats corrects:et plus loin:
PS: je n'ai abordé que le remplissage, mais n'oubliez pas que les types qui peuvent comparer des instances égales avec une représentation différente en mémoire ne sont en aucun cas rares (par exemple
std::string
,std::vector
et bien d'autres).la source
memcmp
sur des structures sans remplissage etoperator==
ne l' implémenter qu'en cas de besoin.En bref: pas possible de manière générique.
Le problème avec
memcmp
est que le remplissage peut contenir des données arbitraires et doncmemcmp
peut échouer. S'il y avait un moyen de savoir où se trouve le remplissage, vous pourriez mettre à zéro ces bits puis comparer les représentations de données, cela vérifierait l'égalité si les membres sont trivialement comparables (ce qui n'est pas le cas,std::string
car deux chaînes peuvent contiennent des pointeurs différents, mais les deux tableaux de caractères pointés sont égaux). Mais je ne connais aucun moyen d'accéder au remplissage des structures. Vous pouvez essayer de dire à votre compilateur de compresser les structures, mais cela ralentira les accès et ne sera pas vraiment garanti de fonctionner.La façon la plus simple de mettre en œuvre cela est de comparer tous les membres. Bien sûr, cela n'est pas vraiment possible de manière générique (jusqu'à ce que nous obtenions des réflexions de temps de compilation et des méta-classes en C ++ 23 ou version ultérieure). A partir de C ++ 20, on pourrait générer une valeur par défaut
operator<=>
mais je pense que cela ne serait également possible qu'en tant que fonction membre donc, encore une fois, cela n'est pas vraiment applicable. Si vous êtes chanceux et que toutes les structures que vous souhaitez comparer ont unoperator==
défini, vous pouvez bien sûr simplement l'utiliser. Mais ce n'est pas garanti.EDIT: Ok, il existe en fait un moyen totalement hacky et quelque peu générique pour les agrégats. (J'ai seulement écrit la conversion en tuples, ceux-ci ont un opérateur de comparaison par défaut). Godbolt
la source
C ++ 20 prend en charge les comparaisons par défaut
la source
==
ou<=>
ne peut être effectuée que au niveau de la classe.En supposant des données POD, l'opérateur d'affectation par défaut copie uniquement les octets membres. (en fait, je n'en suis pas sûr à 100%, ne me croyez pas sur parole)
Vous pouvez utiliser ça à votre avantage:
la source
Je pense que vous pourrez peut-être baser une solution sur le vaudou merveilleusement sournois d'Antony Polukhin dans la
magic_get
bibliothèque - pour les structures, pas pour les classes complexes.Avec cette bibliothèque, nous sommes en mesure d'itérer les différents champs d'une structure, avec leur type approprié, en code purement général. Antony l'a utilisé, par exemple, pour pouvoir diffuser des structures arbitraires dans un flux de sortie avec les types corrects, de manière complètement générique. Il va de soi que la comparaison pourrait également être une application possible de cette approche.
... mais vous auriez besoin de C ++ 14. Au moins, c'est mieux que le C ++ 17 et les suggestions ultérieures dans d'autres réponses :-P
la source