*
* Example:
* #include <ccan/structeq/structeq.h>
+ * // Defines sha256_eq
+ * STRUCTEQ_DEF(sha256, 0, u);
*
* static void next_hash(const struct sha256 *hash)
* {
* else {
* struct sha256 check;
* assert(shachain_get_hash(&chain, index+1, &check));
- * assert(structeq(&check, hash));
+ * assert(sha256_eq(&check, hash));
* }
* }
*/
-../../licenses/CC0
\ No newline at end of file
+../../licenses/BSD-MIT
\ No newline at end of file
* structeq - bitwise comparison of structs.
*
* This is a replacement for memcmp, which checks the argument types are the
- * same.
+ * same, and takes into account padding in the structure. When there is no
+ * padding, it becomes a memcmp at compile time (assuming a
+ * constant-optimizing compiler).
*
- * License: CC0 (Public domain)
+ * License: BSD-MIT
* Author: Rusty Russell <rusty@rustcorp.com.au>
*
* Example:
* struct mydata {
* int start, end;
* };
+ * // Defines mydata_eq(a, b)
+ * STRUCTEQ_DEF(mydata, 0, start, end);
*
* int main(void)
* {
* struct mydata a, b;
*
- * // No padding in struct, otherwise this doesn't work!
- * BUILD_ASSERT(sizeof(a) == sizeof(a.start) + sizeof(a.end));
- *
* a.start = 100;
* a.end = 101;
*
- * b.start = 100;
- * b.end = 101;
- *
* // They are equal.
- * assert(structeq(&a, &b));
+ * assert(mydata_eq(&a, &b));
*
* b.end++;
* // Now they are not.
- * assert(!structeq(&a, &b));
+ * assert(!mydata_eq(&a, &b));
*
* return 0;
* }
return 1;
if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/build_assert\n");
+ printf("ccan/cppmagic\n");
return 0;
}
-/* CC0 (Public domain) - see LICENSE file for details */
+/* MIT (BSD) license - see LICENSE file for details */
#ifndef CCAN_STRUCTEQ_H
#define CCAN_STRUCTEQ_H
+#include <ccan/build_assert/build_assert.h>
+#include <ccan/cppmagic/cppmagic.h>
#include <string.h>
+#include <stdbool.h>
/**
- * structeq - are two structures bitwise equal (including padding!)
- * @a: a pointer to a structure
- * @b: a pointer to a structure of the same type.
+ * STRUCTEQ_DEF - define an ..._eq function to compare two structures.
+ * @sname: name of the structure, and function (<sname>_eq) to define.
+ * @padbytes: number of bytes of expected padding, or -1 if unknown.
+ * @...: name of every member of the structure.
*
- * If you *know* a structure has no padding, you can memcmp them. At
- * least this way, the compiler will issue a warning if the structs are
- * different types!
+ * This generates a single memcmp() call in the common case where the
+ * structure contains no padding. Since it can't tell the difference between
+ * padding and a missing member, @padbytes can be used to assert that
+ * there isn't any, or how many we expect. -1 means "expect some", since
+ * it can be platform dependent.
*/
-#define structeq(a, b) \
- (memcmp((a), (b), sizeof(*(a)) + 0 * sizeof((a) == (b))) == 0)
+#define STRUCTEQ_DEF(sname, padbytes, ...) \
+static inline bool CPPMAGIC_GLUE2(sname, _eq)(const struct sname *_a, \
+ const struct sname *_b) \
+{ \
+ BUILD_ASSERT(((padbytes) < 0 && \
+ CPPMAGIC_JOIN(+, CPPMAGIC_MAP(STRUCTEQ_MEMBER_SIZE_, \
+ __VA_ARGS__)) \
+ > sizeof(*_a)) \
+ || CPPMAGIC_JOIN(+, CPPMAGIC_MAP(STRUCTEQ_MEMBER_SIZE_, \
+ __VA_ARGS__)) \
+ + (padbytes) == sizeof(*_a)); \
+ if (CPPMAGIC_JOIN(+, CPPMAGIC_MAP(STRUCTEQ_MEMBER_SIZE_, __VA_ARGS__)) \
+ == sizeof(*_a)) \
+ return memcmp(_a, _b, sizeof(*_a)) == 0; \
+ else \
+ return CPPMAGIC_JOIN(&&, \
+ CPPMAGIC_MAP(STRUCTEQ_MEMBER_CMP_, \
+ __VA_ARGS__)); \
+}
+
+/* Helpers */
+#define STRUCTEQ_MEMBER_SIZE_(m) sizeof((_a)->m)
+#define STRUCTEQ_MEMBER_CMP_(m) memcmp(&_a->m, &_b->m, sizeof(_a->m)) == 0
+
#endif /* CCAN_STRUCTEQ_H */
int start, end;
};
+STRUCTEQ_DEF(mydata1, 0, start, end);
+
int main(void)
{
struct mydata1 a = { 0, 100 };
#endif
b = { 0, 100 };
- return structeq(&a, &b);
+ return mydata1_eq(&a, &b);
}
--- /dev/null
+#include <ccan/structeq/structeq.h>
+
+struct mydata {
+ int start, end;
+};
+#ifdef FAIL
+#define PADDING -1
+#else
+#define PADDING 0
+#endif
+
+STRUCTEQ_DEF(mydata, PADDING, start, end);
+
+int main(void)
+{
+ struct mydata a = { 0, 100 };
+
+ return mydata_eq(&a, &a);
+}
--- /dev/null
+#include <ccan/structeq/structeq.h>
+
+struct mydata {
+ int start, end;
+};
+#ifdef FAIL
+#define PADDING 1
+#else
+#define PADDING 0
+#endif
+
+STRUCTEQ_DEF(mydata, PADDING, start, end);
+
+int main(void)
+{
+ struct mydata a = { 0, 100 };
+
+ return mydata_eq(&a, &a);
+}
--- /dev/null
+#include <ccan/structeq/structeq.h>
+
+struct mydata {
+ int start, end;
+ int pad;
+};
+#ifdef FAIL
+#define PADDING 0
+#else
+#define PADDING sizeof(int)
+#endif
+
+STRUCTEQ_DEF(mydata, PADDING, start, end);
+
+int main(void)
+{
+ struct mydata a = { 0, 100 };
+
+ return mydata_eq(&a, &a);
+}
--- /dev/null
+#include <ccan/structeq/structeq.h>
+#include <ccan/tap/tap.h>
+
+/* In theory, this could be generated without padding, if alignof(int) were 0,
+ * and test would fail. Call me when that happens. */
+struct mydata {
+ char start;
+ int end;
+};
+
+STRUCTEQ_DEF(mydata, sizeof(int) - sizeof(char), start, end);
+
+int main(void)
+{
+ struct mydata a, b;
+
+ /* This is how many tests you plan to run */
+ plan_tests(3);
+
+ a.start = 0;
+ a.end = 100;
+ ok1(mydata_eq(&a, &a));
+
+ b = a;
+ ok1(mydata_eq(&a, &b));
+
+ b.end++;
+ ok1(!mydata_eq(&a, &b));
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
int start, end;
};
+STRUCTEQ_DEF(mydata, 0, start, end);
+
int main(void)
{
struct mydata a, b;
a.start = 0;
a.end = 100;
- ok1(structeq(&a, &a));
+ ok1(mydata_eq(&a, &a));
b = a;
- ok1(structeq(&a, &b));
+ ok1(mydata_eq(&a, &b));
b.end++;
- ok1(!structeq(&a, &b));
+ ok1(!mydata_eq(&a, &b));
/* This exits depending on whether all tests passed */
return exit_status();