msgbuf: Enable pldm_msgbuf_extract() into packed members

`pldm_msgbuf_extract()` should work correctly regardless of whether the
`dst` argument is a member of a packed or padded struct.

To get there while still achieving type safety we have to jump through
some hoops. Commentary in the patch hopefully captures many of them, but
a side-effect of the hoop-jumping is a couple of changes to ergonomics
of the msgbuf API:

1. `pldm_msgbuf_extract()` no-longer requires that the `dst`
   argument be a pointer. Instead, it must be an lvalue, removing all
   the `&<lvalue>` noise from the call-sites.

2. However, unfortunately the generic extraction macro has been split in
   two. We now have:

   2.1 `pldm_msgbuf_extract()`, and
   2.2 `pldm_msgbuf_extract_p()`, for when the reference we already have
        for the `dst` object is a pointer and not an lvalue.

The split was necessary because I couldn't get GCC and Clang to play
nice with differences required in the assignment expression for lvalue
and pointer type-names in the one macro. Whilst it causes a bunch of
churn it isn't a great concern as the APIs are purely internal to the
library implementation.

Change-Id: Ifc5440a5b838a48bb84c881ec334d9e145365edb
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
diff --git a/src/compiler.h b/src/compiler.h
new file mode 100644
index 0000000..31eeff9
--- /dev/null
+++ b/src/compiler.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
+#ifndef PLDM_COMPILER_H
+#define PLDM_COMPILER_H
+
+// NOLINTBEGIN(bugprone-macro-parentheses)
+/**
+ * Require that the given object is of the specified type.
+ *
+ * If the object is not of the required type then a diagnostic will be emitted.
+ *
+ * If you are reading this documentation due to hitting a compilation error
+ * passing through the macro, then you have a type error in your code that must
+ * be fixed. Despite the compiler output, the error is _not_ that some array
+ * is negatively sized, the array is negatively sized _because_ you have a type
+ * error.
+ *
+ * How this works:
+ *
+ * If the type of @p obj is not equivalent to the provided type @p type then
+ * we force the compiler to evaluate sizeof on a negatively-sized array. The
+ * C standard requires that the integer constant expression that specifies
+ * the array length must be greater than zero. Failure to meet this constraint
+ * generally terminates compilation of the translation unit as any other result
+ * cannot be handled in a sensible way. The array size is derived to an integer
+ * constant expression from a type eqivalence evaluated using _Generic()
+ * allowing us to stay within the language standard. The default generic
+ * association, representing a type mismatch, yields -1.
+ *
+ * pldm_require_obj_type() was introduced into the libpldm implementation to
+ * enable use of the pldm_msgbuf_extract*() APIs for objects that may or may not
+ * reside in a packed struct. See src/msgbuf.h for more details.
+ *
+ * @param obj The name of the object to evaluate
+ * @param type The required type of @p obj
+ *
+ * @return The expression either yields 1, or compilation is terminated
+ */
+#define pldm_require_obj_type(obj, type)                                       \
+	((void)(sizeof(                                                        \
+		struct { char buf[_Generic((obj), type: 1, default: -1)]; })))
+// NOLINTEND(bugprone-macro-parentheses)
+
+#endif