Let's assume that I declared a C struct for holding configuration information, which has lots of fields(~30), e.g.:
struct config {
int a;
int b;
int c;
...
char *str1;
char *str2;
...
};
What is the best way to compare two structs without simply enumerating and comparing each element? Of course I could use the following code but my question is, is there an easier way to achieve this?
static int
cmpInstances(struct config *l, struct config *r) {
return (
l->a == r->a &&
l->b == r->b &&
l->c == r->c &&
!strcmp(l->str1, r->str1) &&
!strcmp(l->str2, r->str2)
);
}
Is there some macro that could do that? For example:
cmpInstancesViaMacro(a,b,c) would expand into
l->a == r->a &&
l->b == r->b &&
l->c == r->c
CodePudding user response:
Perhaps some macros will help us reduce the lines of code and effort needed:
#define CMP(name) l->name == r->name &&
#define SCMP(name) !strcmp(l->name, r->name) &&
struct config
{
int a;
int b;
int c;
char *str1;
char *str2;
};
static int cmpInstances(struct config *l, struct config *r)
{
return CMP(a) CMP(b) CMP(c) SCMP(str1) SCMP(str2) 1;
}
#undef CMP
#undef SCMP
Here we define some macros that take only the name of the variable and compare them. One is written for integer comparison (CMP) and the other is written for string comparison (SCMP). Then you can use these macros to compare your data and undef them when you are done.
Thanks to chqrlie for adding 1 part.
CodePudding user response:
What is the best way to compare two structs without simply enumerating and comparing each element?
The presented way is the best. Except, the pointers should point to const - nothing is getting modified.
is there an easier way to achieve this?
The presented way is the easiest way to achieve it, it's also the most readable and self-explanatory, easy to refactor and reason about. Any other will add to complexity and result in maintenance burden.
I find strcmp(...) == 0 clearer then !strcmp(...).
CodePudding user response:
A generalized variant of so-called X Macros could be used.
In the example of the question, it would look like this:
#define STRUCT \
X_INT(a) \
X_INT(b) \
X_INT(c) \
X_STRING(str1) \
X_STRING(str2)
struct config {
#define X_INT(NAME) int NAME;
#define X_STRING(NAME) char *NAME;
STRUCT
#undef X_INT
#undef X_STRING
};
static int cmpInstances(struct config *l, struct config *r) {
#define X_INT(NAME) l->NAME == r->NAME &&
#define X_STRING(NAME) strcmp(l->NAME, r->NAME) == 0 &&
return STRUCT 1;
#undef X_INT
#undef X_STRING
}
With this solution, only the STRUCT macro needs to be changed when the structure is changed as long as there is no change to the set of types used. The structure and function would automatically be kept synchronized.
If a new type is introduced, the corresponding X Macros must be defined similar to X_INT and X_STRING above.
CodePudding user response:
If you're the author of the struct, you can lay it out so there's no padding, _Static_assert that there's no padding (sizeof(whole struct) == sum of component sizes) and then simply use one memcmp call if all the data is in the struct.
If the struct has string pointers (and you can't guarantee that a string with particular contents is stored only in one place), you'll need to do those separately.
You'll get the tightest code if you put all of them next to each other, union-them with a corresponding char*[] array, and then loop over the corresponding arrays calling strcmp on each pair and combine the result (if all were equal) with memcpy for the rest.
Something like:
#include <string.h>
struct config {
int m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12,m13,m14,m15,m16;
union {
struct { char *s1,*s2,*s3,*s4,*s5,*s6,*s7,*s8; };
char *strings[8];
};
};
_Static_assert(sizeof(struct config) == sizeof(int)*16 sizeof(char*)*8,"");
int strings_equal(char *const A[], char *const B[], size_t Count){
for(;Count;Count--) if(0!=strcmp(*A ,*B )) return 0;
return 1;
}
int configs_equal(struct config const *A, struct config const *B){
return 0==memcmp(A,B,sizeof(int)*16)
&& strings_equal(A->strings,B->strings,sizeof(A->strings)/sizeof(A->strings[0]));
}
