aboutsummaryrefslogtreecommitdiff
path: root/pw_preprocessor
diff options
context:
space:
mode:
authorAlexei Frolov <frolv@google.com>2019-11-01 16:31:19 -0700
committerAlexei Frolov <frolv@google.com>2019-11-04 16:07:34 -0800
commitc10c81201dba4d6533b1270baec0cd24aca752b9 (patch)
treec56efe2a0bfb9ba2398868a7ec144deea6226d24 /pw_preprocessor
parent1a82c146ff11c1b0486d12716082476eabad19dd (diff)
downloadpigweed-c10c81201dba4d6533b1270baec0cd24aca752b9.tar.gz
Add preprocessor and unit_test modules
This change adds two Pigweed modules: pw_preprocessor and pw_unit_test. The preprocessor module contains header files providing helpful macros for the C preprocessor. The unit test module contains a starter implementation of a unit testing framework for Pigweed. Change-Id: I46e1a4cae1fd8ce36d7840a2e92f8013fb489cde
Diffstat (limited to 'pw_preprocessor')
-rw-r--r--pw_preprocessor/BUILD.gn88
-rw-r--r--pw_preprocessor/README.md4
-rw-r--r--pw_preprocessor/boolean_test.cc81
-rw-r--r--pw_preprocessor/concat_test.cc86
-rw-r--r--pw_preprocessor/docs.rst63
-rw-r--r--pw_preprocessor/macro_arg_count_test.cc255
-rw-r--r--pw_preprocessor/public/pw_preprocessor/boolean.h74
-rw-r--r--pw_preprocessor/public/pw_preprocessor/compiler.h60
-rw-r--r--pw_preprocessor/public/pw_preprocessor/concat.h66
-rw-r--r--pw_preprocessor/public/pw_preprocessor/macro_arg_count.h124
-rw-r--r--pw_preprocessor/public/pw_preprocessor/util.h57
-rw-r--r--pw_preprocessor/util_test.cc66
12 files changed, 1024 insertions, 0 deletions
diff --git a/pw_preprocessor/BUILD.gn b/pw_preprocessor/BUILD.gn
new file mode 100644
index 000000000..ccc89ad97
--- /dev/null
+++ b/pw_preprocessor/BUILD.gn
@@ -0,0 +1,88 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+config("default_config") {
+ include_dirs = [ "public" ]
+}
+
+source_set("pw_preprocessor") {
+ public_configs = [
+ "$dir_pw_build:pw_default_cpp",
+ ":default_config",
+ ]
+ public = [
+ "public/pw_preprocessor/boolean.h",
+ "public/pw_preprocessor/compiler.h",
+ "public/pw_preprocessor/concat.h",
+ "public/pw_preprocessor/macro_arg_count.h",
+ "public/pw_preprocessor/util.h",
+ ]
+ sources = public
+}
+
+group("pw_preprocessor_tests") {
+ deps = [
+ ":boolean_test",
+ ":concat_test",
+ ":macro_arg_count_test",
+ ":util_test",
+ ]
+}
+
+group("pw_preprocessor_tests_linux") {
+ deps = [
+ ":pw_preprocessor_tests($dir_pw_toolchain:x86_linux_o2)",
+ ]
+}
+
+# TODO(frolv): Change these to special unit test executables.
+executable("boolean_test") {
+ deps = [
+ ":pw_preprocessor",
+ "$dir_pw_unit_test:main",
+ ]
+ sources = [
+ "boolean_test.cc",
+ ]
+}
+
+executable("concat_test") {
+ deps = [
+ ":pw_preprocessor",
+ "$dir_pw_unit_test:main",
+ ]
+ sources = [
+ "concat_test.cc",
+ ]
+}
+
+executable("macro_arg_count_test") {
+ deps = [
+ ":pw_preprocessor",
+ "$dir_pw_unit_test:main",
+ ]
+ sources = [
+ "macro_arg_count_test.cc",
+ ]
+}
+
+executable("util_test") {
+ deps = [
+ ":pw_preprocessor",
+ "$dir_pw_unit_test:main",
+ ]
+ sources = [
+ "util_test.cc",
+ ]
+}
diff --git a/pw_preprocessor/README.md b/pw_preprocessor/README.md
new file mode 100644
index 000000000..d56480977
--- /dev/null
+++ b/pw_preprocessor/README.md
@@ -0,0 +1,4 @@
+# pw\_preprocessor: Useful C preprocessor macros
+
+The pw\_preprocessor module provides several helpful preprocessor macros for use
+in C and C++ code.
diff --git a/pw_preprocessor/boolean_test.cc b/pw_preprocessor/boolean_test.cc
new file mode 100644
index 000000000..ab8ada300
--- /dev/null
+++ b/pw_preprocessor/boolean_test.cc
@@ -0,0 +1,81 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+//
+// All of these tests are static asserts. If the test compiles, it has already
+// passed. The TEST functions are used for organization only.
+#include "pw_preprocessor/boolean.h"
+
+#include "pw_unit_test/framework.h"
+
+namespace pw {
+namespace {
+
+#define ONE 1
+#define ZERO() 0
+
+TEST(BooleanMacros, And) {
+ static_assert(PW_AND(ZERO(), 0) == 0);
+ static_assert(PW_AND(ZERO(), ONE) == 0);
+ static_assert(PW_AND(1, 0) == 0);
+ static_assert(PW_AND(ONE, PW_NOT(ZERO())) == 1);
+}
+
+TEST(BooleanMacros, Or) {
+ static_assert(PW_OR(ZERO(), 0) == 0);
+ static_assert(PW_OR(ZERO(), ONE) == 1);
+ static_assert(PW_OR(1, 0) == 1);
+ static_assert(PW_OR(ONE, PW_NOT(ZERO())) == 1);
+}
+
+TEST(BooleanMacros, Not) {
+ static_assert(PW_NOT(0) == 1);
+ static_assert(PW_NOT(1) == 0);
+ static_assert(PW_NOT(ONE) == 0);
+ static_assert(PW_NOT(ZERO()) == 1);
+}
+
+TEST(BooleanMacros, Xor) {
+ static_assert(PW_XOR(ZERO(), 0) == 0);
+ static_assert(PW_XOR(ZERO(), ONE) == 1);
+ static_assert(PW_XOR(1, 0) == 1);
+ static_assert(PW_XOR(ONE, PW_NOT(ZERO())) == 0);
+}
+
+TEST(BooleanMacros, Nand) {
+ static_assert(PW_NAND(ZERO(), 0) == 1);
+ static_assert(PW_NAND(ZERO(), ONE) == 1);
+ static_assert(PW_NAND(1, 0) == 1);
+ static_assert(PW_NAND(ONE, PW_NOT(ZERO())) == 0);
+}
+
+TEST(BooleanMacros, Nor) {
+ static_assert(PW_NOR(ZERO(), 0) == 1);
+ static_assert(PW_NOR(ZERO(), ONE) == 0);
+ static_assert(PW_NOR(1, 0) == 0);
+ static_assert(PW_NOR(ONE, PW_NOT(ZERO())) == 0);
+}
+
+TEST(BooleanMacros, Xnor) {
+ static_assert(PW_XNOR(ZERO(), 0) == 1);
+ static_assert(PW_XNOR(ZERO(), ONE) == 0);
+ static_assert(PW_XNOR(1, 0) == 0);
+ static_assert(PW_XNOR(ONE, PW_NOT(ZERO())) == 1);
+}
+
+TEST(BooleanMacros, Nested) {
+ static_assert(PW_AND(1, PW_AND(PW_OR(ZERO(), ONE), PW_XOR(ONE, 0))) == 1);
+}
+
+} // namespace
+} // namespace pw
diff --git a/pw_preprocessor/concat_test.cc b/pw_preprocessor/concat_test.cc
new file mode 100644
index 000000000..cd3af3a31
--- /dev/null
+++ b/pw_preprocessor/concat_test.cc
@@ -0,0 +1,86 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+#include "pw_preprocessor/concat.h"
+
+#include "pw_preprocessor/util.h"
+#include "pw_unit_test/framework.h"
+
+namespace pw {
+namespace {
+
+TEST(Concat, WithoutMacroExpansions) {
+ static_assert(PW_CONCAT() 9000 PW_CONCAT() == 9000);
+ static_assert(PW_CONCAT(1, 2) == 12);
+ static_assert(PW_CONCAT(1, 2, 3, 4) == 1234);
+ static_assert(PW_CONCAT(1, 2, 3, 4, 5) == 12345);
+ static_assert(PW_CONCAT(1, 2, 3, 4, 5, 6) == 123456);
+ static_assert(PW_CONCAT(1, 2, 3, 4, 5, 6, 7) == 1234567);
+ static_assert(PW_CONCAT(1, 2, 3, 4, 5, 6, 7, 8) == 12345678);
+
+ static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, llu) == 0x345678llu);
+ static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, llu) == 0x3456789llu);
+ static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, llu) == 0x3456789Allu);
+ static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, llu) ==
+ 0x3456789ABllu);
+ static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, llu) ==
+ 0x3456789ABCllu);
+ static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, llu) ==
+ 0x3456789ABCDllu);
+ static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, llu) ==
+ 0x3456789ABCDEllu);
+ static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, llu) ==
+ 0x3456789ABCDEFllu);
+ static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, F, llu) ==
+ 0x3456789ABCDEFFllu);
+}
+
+// Macros which expand out to test that PW_CONCAT properly expands them.
+#define _PW_TEST_SECTION(x) "section: " x
+#define _PW_TEST_NUMBER 123
+#define _PW_OUTER_MACRO(section_name) \
+ _PW_TEST_SECTION(PW_STRINGIFY(PW_CONCAT(section_name, _PW_TEST_NUMBER)))
+
+#define _PW_WHOSE() my
+#define _PW_WHAT var
+
+TEST(Concat, WithMacroExpansions) {
+ static_assert(PW_CONCAT(_PW_TEST_NUMBER, 5) == 1235);
+
+ int my_var;
+ EXPECT_EQ(&PW_CONCAT(_PW_WHOSE(), _, _PW_WHAT), &my_var);
+
+ EXPECT_STREQ(_PW_OUTER_MACRO(what is up), "section: what is up123");
+}
+
+// Test concatenation up to the maximum supported length.
+TEST(Concat, MaximumConcatenationLength) {
+ constexpr int value31_234567890123456789012345678901 = 1337;
+ constexpr int value32_2345678901234567890123456789012 = 101010;
+
+ // clang-format off
+ static_assert(PW_CONCAT(value31_, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+ 1) == 1337);
+
+ static_assert(PW_CONCAT(value32_, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+ 1, 2) == 101010);
+ // clang-format on
+}
+
+} // namespace
+} // namespace pw
diff --git a/pw_preprocessor/docs.rst b/pw_preprocessor/docs.rst
new file mode 100644
index 000000000..37ddbf6b4
--- /dev/null
+++ b/pw_preprocessor/docs.rst
@@ -0,0 +1,63 @@
+.. _chapter-preprocessor:
+
+.. default-domain:: cpp
+
+.. highlight:: sh
+
+------------
+Preprocessor
+------------
+The preprocessor module provides various helpful preprocessor macros.
+
+Compatibility
+=============
+C and C++
+
+Dependencies
+============
+This module has no dependencies.
+
+Headers
+=======
+The preprocessor module provides several headers.
+
+pw_preprocessor/boolean.h
+-------------------------
+Defines macros for boolean logic on literal 1s and 0s. This is useful for
+situations when a literal is needed to build the name of a function or macro.
+
+pw_preprocessor/compiler.h
+--------------------------
+Macros for compiler-specific features, such as attributes or builtins.
+
+pw_preprocessor/concat.h
+------------------------
+Defines the ``PW_CONCAT(...)`` macro, which expands its arguments if they are
+macros and token pastes the results. This can be used for building names of
+classes, variables, macros, etc.
+
+pw_preprocessor/macro_arg_count.h
+---------------------------------
+Defines the ``PW_ARG_COUNT(...)`` macro, which counts the number of arguments it
+was passed. It can be invoked directly or with ``__VA_ARGS__`` in another macro.
+``PW_ARG_COUNT(...)`` evaluates to a literal of the number of arguments which
+can be used directly or concatenated to build other names. Unlike many common
+implementations, this macro correctly evaluates to ``0`` when it is invoked
+without arguments.
+
+This header also defines ``PW_HAS_ARGS(...)`` and ``PW_HAS_NO_ARGS(...)``,
+which evaluate to ``1`` or ``0`` depending on whether they are invoked with
+arguments.
+
+pw_preprocessor/util.h
+----------------------
+General purpose, useful macros.
+
+* ``PW_ARRAY_SIZE(array)`` -- calculates the size of a C array
+* ``PW_UNUSED(value)`` -- silences "unused variable" compiler warnings
+* ``PW_STRINGIFY(...)`` -- expands its arguments as macros and converts them to
+ a string literal
+* ``PW_EXTERN_C`` -- declares a name to be ``extern "C"`` in C++; expands to
+ nothing in C
+* ``PW_EXTERN_C_START`` / ``PW_EXTERN_C_END`` -- declares an ``extern "C" { }``
+ block in C++; expands to nothing in C
diff --git a/pw_preprocessor/macro_arg_count_test.cc b/pw_preprocessor/macro_arg_count_test.cc
new file mode 100644
index 000000000..3b483b910
--- /dev/null
+++ b/pw_preprocessor/macro_arg_count_test.cc
@@ -0,0 +1,255 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+//
+// All of these tests are static asserts. If the test compiles, it has already
+// passed. The TEST functions are used for organization only.
+
+#include "pw_preprocessor/macro_arg_count.h"
+
+#include "pw_unit_test/framework.h"
+
+namespace pw {
+namespace {
+
+TEST(HasArgs, WithoutArguments) {
+ static_assert(PW_HAS_ARGS() == 0);
+ static_assert(PW_HAS_ARGS(/**/) == 0);
+ static_assert(PW_HAS_ARGS(/* uhm, hi */) == 0);
+
+ // Test how the macro handles whitespace and comments.
+ // clang-format off
+ static_assert(PW_HAS_ARGS( ) == 0); // NOLINT
+ static_assert(PW_HAS_ARGS(
+ ) == 0); // NOLINT
+ static_assert(PW_HAS_ARGS(
+ // wow
+ // This is a comment.
+ ) == 0); // NOLINT
+ // clang-format on
+
+ static_assert(PW_HAS_NO_ARGS() == 1);
+ static_assert(PW_HAS_NO_ARGS(/* hello */) == 1);
+ static_assert(PW_HAS_NO_ARGS(
+ // hello
+ /* goodbye */) == 1);
+}
+
+TEST(HasArgs, WithArguments) {
+ static_assert(PW_HAS_ARGS(()) == 1);
+ static_assert(PW_HAS_ARGS(0) == 1);
+ static_assert(PW_HAS_ARGS(, ) == 1); // NOLINT
+ static_assert(PW_HAS_ARGS(a, b, c) == 1);
+ static_assert(PW_HAS_ARGS(PW_HAS_ARGS) == 1);
+ static_assert(PW_HAS_ARGS(PW_HAS_ARGS()) == 1);
+
+ static_assert(PW_HAS_NO_ARGS(0) == 0);
+ static_assert(PW_HAS_NO_ARGS(, ) == 0); // NOLINT
+ static_assert(PW_HAS_NO_ARGS(a, b, c) == 0);
+ static_assert(PW_HAS_NO_ARGS(PW_HAS_ARGS) == 0);
+ static_assert(PW_HAS_NO_ARGS(PW_HAS_ARGS()) == 0);
+}
+
+constexpr int TestFunc(int arg, ...) { return arg; }
+
+#define CALL_FUNCTION(arg, ...) TestFunc(arg PW_COMMA_ARGS(__VA_ARGS__))
+
+template <typename T, typename... Args>
+constexpr T TemplateArgCount() {
+ return sizeof...(Args);
+}
+
+#define COUNT_ARGS_TEMPLATE(...) \
+ TemplateArgCount<int PW_COMMA_ARGS(__VA_ARGS__)>()
+
+TEST(CommaVarargs, NoArguments) {
+ static_assert(TestFunc(0 PW_COMMA_ARGS()) == 0);
+ static_assert(TestFunc(1 /* whoa */ PW_COMMA_ARGS(
+ /* this macro */) /* is cool! */) == 1);
+
+ static_assert(TemplateArgCount<int PW_COMMA_ARGS()>() == 0);
+ static_assert(TemplateArgCount<int PW_COMMA_ARGS(/* nothing */)>() == 0);
+
+ static_assert(CALL_FUNCTION(2) == 2);
+ static_assert(CALL_FUNCTION(3, ) == 3);
+ static_assert(CALL_FUNCTION(4, /* nothing */) == 4);
+
+ static_assert(COUNT_ARGS_TEMPLATE() == 0);
+ static_assert(COUNT_ARGS_TEMPLATE(/* nothing */) == 0);
+}
+
+TEST(CommaVarargs, WithArguments) {
+ static_assert(TestFunc(0 PW_COMMA_ARGS(1)) == 0);
+ static_assert(TestFunc(1 PW_COMMA_ARGS(1, 2)) == 1);
+ static_assert(TestFunc(2 PW_COMMA_ARGS(1, 2, "three")) == 2);
+
+ static_assert(TemplateArgCount<int PW_COMMA_ARGS(bool)>() == 1);
+ static_assert(TemplateArgCount<int PW_COMMA_ARGS(char, const char*)>() == 2);
+ static_assert(TemplateArgCount<int PW_COMMA_ARGS(int, char, const char*)>() ==
+ 3);
+
+ static_assert(CALL_FUNCTION(3) == 3);
+ static_assert(CALL_FUNCTION(4, ) == 4);
+ static_assert(CALL_FUNCTION(5, /* nothing */) == 5);
+
+ static_assert(COUNT_ARGS_TEMPLATE(int) == 1);
+ static_assert(COUNT_ARGS_TEMPLATE(int, int) == 2);
+ static_assert(COUNT_ARGS_TEMPLATE(int, int, int) == 3);
+}
+
+TEST(CountArgs, Zero) {
+ static_assert(PW_ARG_COUNT() == 0);
+ static_assert(PW_ARG_COUNT(/**/) == 0);
+ static_assert(PW_ARG_COUNT(/* uhm, hi */) == 0);
+
+ // clang-format off
+ static_assert(PW_ARG_COUNT( ) == 0); // NOLINT
+ static_assert(PW_ARG_COUNT(
+ ) == 0); // NOLINT
+ static_assert(PW_ARG_COUNT(
+ // wow
+ // This is a comment.
+ ) == 0); // NOLINT
+ // clang-format on
+}
+
+TEST(CountArgs, Commas) {
+ // clang-format off
+ static_assert(PW_ARG_COUNT(,) == 2); // NOLINT
+ static_assert(PW_ARG_COUNT(,,) == 3); // NOLINT
+ static_assert(PW_ARG_COUNT(,,,) == 4); // NOLINT
+ // clang-format on
+ static_assert(PW_ARG_COUNT(, ) == 2); // NOLINT
+ static_assert(PW_ARG_COUNT(, , ) == 3); // NOLINT
+ static_assert(PW_ARG_COUNT(, , , ) == 4); // NOLINT
+}
+
+TEST(CountArgs, Parentheses) {
+ static_assert(PW_ARG_COUNT(()) == 1);
+ static_assert(PW_ARG_COUNT((1, 2, 3, 4)) == 1);
+ static_assert(PW_ARG_COUNT((1, 2, 3), (1, 2, 3, 4)) == 2);
+ static_assert(PW_ARG_COUNT((), ()) == 2);
+ static_assert(PW_ARG_COUNT((-), (o)) == 2);
+ static_assert(PW_ARG_COUNT((, , (, , ), ), (123, 4)) == 2); // NOLINT
+ static_assert(PW_ARG_COUNT(1, (2, 3, 4), (<5, 6>)) == 3);
+}
+
+#define SOME_VARIADIC_MACRO(...) PW_ARG_COUNT(__VA_ARGS__)
+
+#define ANOTHER_VARIADIC_MACRO(arg, ...) SOME_VARIADIC_MACRO(__VA_ARGS__)
+
+#define ALWAYS_ONE_ARG(...) SOME_VARIADIC_MACRO((__VA_ARGS__))
+
+TEST(CountArgs, NestedMacros) {
+ static_assert(SOME_VARIADIC_MACRO() == 0);
+ static_assert(SOME_VARIADIC_MACRO(X1) == 1);
+ static_assert(SOME_VARIADIC_MACRO(X1, X2) == 2);
+ static_assert(SOME_VARIADIC_MACRO(X1, X2, X3) == 3);
+ static_assert(SOME_VARIADIC_MACRO(X1, X2, X3, X4) == 4);
+ static_assert(SOME_VARIADIC_MACRO(X1, X2, X3, X4, X5) == 5);
+
+ static_assert(ANOTHER_VARIADIC_MACRO() == 0);
+ static_assert(ANOTHER_VARIADIC_MACRO(X0) == 0);
+ static_assert(ANOTHER_VARIADIC_MACRO(X0, X1) == 1);
+ static_assert(ANOTHER_VARIADIC_MACRO(X0, X1, X2) == 2);
+ static_assert(ANOTHER_VARIADIC_MACRO(X0, X1, X2, X3) == 3);
+ static_assert(ANOTHER_VARIADIC_MACRO(X0, X1, X2, X3, X4) == 4);
+ static_assert(ANOTHER_VARIADIC_MACRO(X0, X1, X2, X3, X4, X5) == 5);
+
+ static_assert(ALWAYS_ONE_ARG() == 1);
+ static_assert(ALWAYS_ONE_ARG(X0) == 1);
+ static_assert(ALWAYS_ONE_ARG(X0, X1) == 1);
+ static_assert(ALWAYS_ONE_ARG(X0, X1, X2) == 1);
+ static_assert(ALWAYS_ONE_ARG(X0, X1, X2, X3) == 1);
+ static_assert(ALWAYS_ONE_ARG(X0, X1, X2, X3, X4) == 1);
+ static_assert(ALWAYS_ONE_ARG(X0, X1, X2, X3, X4, X5) == 1);
+}
+
+/* Tests all supported arg counts. This test was generated by the following
+ Python 3 code:
+for i in range(64 + 1):
+ args = [f'X{x}' for x in range(1, i + 1)]
+ print(f' static_assert(PW_ARG_COUNT({", ".join(args)}) == {i}) // NOLINT')
+*/
+TEST(CountArgs, AllSupported) {
+ // clang-format off
+ static_assert(PW_ARG_COUNT() == 0); // NOLINT
+ static_assert(PW_ARG_COUNT(X1) == 1); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2) == 2); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3) == 3); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4) == 4); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5) == 5); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6) == 6); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7) == 7); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8) == 8); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9) == 9); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10) == 10); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11) == 11); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12) == 12); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13) == 13); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14) == 14); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15) == 15); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16) == 16); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17) == 17); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18) == 18); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19) == 19); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20) == 20); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21) == 21); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22) == 22); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23) == 23); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24) == 24); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25) == 25); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26) == 26); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27) == 27); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28) == 28); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29) == 29); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30) == 30); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31) == 31); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32) == 32); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33) == 33); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34) == 34); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35) == 35); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36) == 36); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37) == 37); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38) == 38); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39) == 39); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40) == 40); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41) == 41); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42) == 42); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43) == 43); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44) == 44); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45) == 45); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46) == 46); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47) == 47); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48) == 48); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49) == 49); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50) == 50); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51) == 51); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52) == 52); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53) == 53); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54) == 54); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55) == 55); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56) == 56); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57) == 57); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58) == 58); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59) == 59); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60) == 60); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60, X61) == 61); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60, X61, X62) == 62); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60, X61, X62, X63) == 63); // NOLINT
+ static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60, X61, X62, X63, X64) == 64); // NOLINT
+ // clang-format on
+}
+
+} // namespace
+} // namespace pw
diff --git a/pw_preprocessor/public/pw_preprocessor/boolean.h b/pw_preprocessor/public/pw_preprocessor/boolean.h
new file mode 100644
index 000000000..cb2af1833
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/boolean.h
@@ -0,0 +1,74 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+// Preprocessor boolean operation macros that evaluate to 0 or 1.
+//
+// These macros perform boolean operations in the C preprocessor that evaluate
+// to a literal 1 or 0. They can be used for a few purposes:
+//
+// - Generate other macros that evaluate to a 1 or 0, instead of a
+// parenthesized boolean expression.
+// - Ensure that the operands are defined and evaluate to 1 or 0 themselves.
+// - Write macros that conditionally use other macros by token pasting the
+// resulting 1 or 0 to form a new macro name.
+//
+// These macros should not be used outside of macro definitions. Use normal C
+// operators (&&, ||, !, ==, !=) instead. For example, to check whether two
+// flags are set, the C operators are the best choice:
+//
+// #if RELEASE && OPTIMIZED
+//
+// However, there are cases when a literal 0 or 1 is required. For example:
+//
+// #define SELECT_ALGORITHM() PW_CONCAT(ALGO_, PW_AND(RELEASE, OPTIMIZED))
+//
+// SELECT_ALGORITHM evaluates to ALGO_0 or ALGO_1, depending on whether RELEASE
+// and OPTIMIZED are set to 1.
+
+// Boolean AND of two preprocessor expressions that evaluate to 0 or 1.
+#define PW_AND(a, b) _PW_AND(a, b) // Expand the macro an extra time to
+#define _PW_AND(a, b) _PW_AND_##a##b() // allow macro substitution to occur.
+#define _PW_AND_00() 0
+#define _PW_AND_01() 0
+#define _PW_AND_10() 0
+#define _PW_AND_11() 1
+
+// Boolean OR of two preprocessor expressions that evaluate to 0 or 1.
+#define PW_OR(a, b) _PW_OR(a, b)
+#define _PW_OR(a, b) _PW_OR_##a##b()
+#define _PW_OR_00() 0
+#define _PW_OR_01() 1
+#define _PW_OR_10() 1
+#define _PW_OR_11() 1
+
+// Boolean NOT of a preprocessor expression that evaluates to 0 or 1.
+#define PW_NOT(value) _PW_NOT(value)
+#define _PW_NOT(value) _PW_NOT_##value()
+#define _PW_NOT_0() 1
+#define _PW_NOT_1() 0
+
+// Boolean XOR of two preprocessor expressions that evaluate to 0 or 1.
+#define PW_XOR(a, b) _PW_XOR(a, b)
+#define _PW_XOR(a, b) _PW_XOR_##a##b()
+#define _PW_XOR_00() 0
+#define _PW_XOR_01() 1
+#define _PW_XOR_10() 1
+#define _PW_XOR_11() 0
+
+// Boolean NAND, NOR, and XNOR of expressions that evaluate to 0 or 1.
+#define PW_NAND(a, b) PW_NOT(PW_AND(a, b))
+#define PW_NOR(a, b) PW_NOT(PW_OR(a, b))
+#define PW_XNOR(a, b) PW_NOT(PW_XOR(a, b))
diff --git a/pw_preprocessor/public/pw_preprocessor/compiler.h b/pw_preprocessor/public/pw_preprocessor/compiler.h
new file mode 100644
index 000000000..eb1e4337a
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/compiler.h
@@ -0,0 +1,60 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+//
+// Preprocessor macros that wrap compiler-specific features.
+// This file is used by both C++ and C code.
+#pragma once
+
+// Marks a struct or class as packed.
+#define PW_PACKED(declaration) declaration __attribute__((packed))
+
+// Marks a function or object as used, ensuring code for it is generated.
+#define PW_USED __attribute__((used))
+
+// Prevents generation of a prologue or epilogue for a function. This is
+// helpful when implementing the function in assembly.
+#define PW_NO_PROLOGUE __attribute__((naked))
+
+// Marks that a function declaration takes a printf-style format string and
+// variadic arguments. This allows the compiler to perform check the validity of
+// the format string and arguments. This macro must only be on the function
+// declaration, not the definition.
+//
+// The format_index is index of the format string parameter and parameter_index
+// is the starting index of the variadic arguments. Indices start at 1. For C++
+// class member functions, add one to the index to account for the implicit this
+// parameter.
+//
+// This example shows a function where the format string is argument 2 and the
+// varargs start at argument 3.
+//
+// int PrintfStyleFunction(char* buffer,
+// const char* fmt, ...) PW_PRINTF_FORMAT(2,3);
+//
+// int PrintfStyleFunction(char* buffer, const char* fmt, ...) {
+// ... implementation here ...
+// }
+//
+#define PW_PRINTF_FORMAT(format_index, parameter_index) \
+ __attribute__((format(printf, format_index, parameter_index)))
+
+// Places a variable in the specified linker section and directs the compiler
+// to keep the variable, even if it is not used. Depending on the linker
+// options, the linker may still remove this section if it is not declared in
+// the linker script and marked KEEP.
+#if __APPLE__
+#define PW_KEEP_IN_SECTION(name) __attribute__((section("__DATA," name), used))
+#else
+#define PW_KEEP_IN_SECTION(name) __attribute__((section(name), used))
+#endif // __APPLE__
diff --git a/pw_preprocessor/public/pw_preprocessor/concat.h b/pw_preprocessor/public/pw_preprocessor/concat.h
new file mode 100644
index 000000000..7b6f1351f
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/concat.h
@@ -0,0 +1,66 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+
+#pragma once
+
+#include "pw_preprocessor/macro_arg_count.h"
+
+// Expands macros and concatenates the results using preprocessor ##
+// concatentation. Supports up to 32 arguments.
+#define PW_CONCAT(...) _PW_CONCAT_IMPL1(PW_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)
+
+// Expand the macro to allow PW_ARG_COUNT and any caller-provided macros to be
+// evaluated before concatenating the tokens.
+#define _PW_CONCAT_IMPL1(count, ...) _PW_CONCAT_IMPL2(count, __VA_ARGS__)
+#define _PW_CONCAT_IMPL2(count, ...) _PW_CONCAT_##count(__VA_ARGS__)
+
+// clang-format off
+/* This macro implementation was generated with the following Python 3 code:
+for i in range(32 + 1):
+ args = [f'a{x}' for x in range(1, i + 1)]
+ print(f'#define _PW_CONCAT_{i}({", ".join(args)}) {"##".join(args)} // NOLINT')
+*/
+
+#define _PW_CONCAT_0() // NOLINT
+#define _PW_CONCAT_1(a1) a1 // NOLINT
+#define _PW_CONCAT_2(a1, a2) a1##a2 // NOLINT
+#define _PW_CONCAT_3(a1, a2, a3) a1##a2##a3 // NOLINT
+#define _PW_CONCAT_4(a1, a2, a3, a4) a1##a2##a3##a4 // NOLINT
+#define _PW_CONCAT_5(a1, a2, a3, a4, a5) a1##a2##a3##a4##a5 // NOLINT
+#define _PW_CONCAT_6(a1, a2, a3, a4, a5, a6) a1##a2##a3##a4##a5##a6 // NOLINT
+#define _PW_CONCAT_7(a1, a2, a3, a4, a5, a6, a7) a1##a2##a3##a4##a5##a6##a7 // NOLINT
+#define _PW_CONCAT_8(a1, a2, a3, a4, a5, a6, a7, a8) a1##a2##a3##a4##a5##a6##a7##a8 // NOLINT
+#define _PW_CONCAT_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) a1##a2##a3##a4##a5##a6##a7##a8##a9 // NOLINT
+#define _PW_CONCAT_10(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10 // NOLINT
+#define _PW_CONCAT_11(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11 // NOLINT
+#define _PW_CONCAT_12(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12 // NOLINT
+#define _PW_CONCAT_13(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13 // NOLINT
+#define _PW_CONCAT_14(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14 // NOLINT
+#define _PW_CONCAT_15(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15 // NOLINT
+#define _PW_CONCAT_16(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16 // NOLINT
+#define _PW_CONCAT_17(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17 // NOLINT
+#define _PW_CONCAT_18(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18 // NOLINT
+#define _PW_CONCAT_19(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19 // NOLINT
+#define _PW_CONCAT_20(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20 // NOLINT
+#define _PW_CONCAT_21(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21 // NOLINT
+#define _PW_CONCAT_22(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22 // NOLINT
+#define _PW_CONCAT_23(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23 // NOLINT
+#define _PW_CONCAT_24(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24 // NOLINT
+#define _PW_CONCAT_25(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25 // NOLINT
+#define _PW_CONCAT_26(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26 // NOLINT
+#define _PW_CONCAT_27(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27 // NOLINT
+#define _PW_CONCAT_28(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28 // NOLINT
+#define _PW_CONCAT_29(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28##a29 // NOLINT
+#define _PW_CONCAT_30(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28##a29##a30 // NOLINT
+#define _PW_CONCAT_31(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28##a29##a30##a31 // NOLINT
+#define _PW_CONCAT_32(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28##a29##a30##a31##a32 // NOLINT
diff --git a/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h b/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h
new file mode 100644
index 000000000..e55ff0c94
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h
@@ -0,0 +1,124 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+//
+// Macros for counting the number of arguments passed to a variadic
+// function-like macro.
+#pragma once
+
+#include "pw_preprocessor/boolean.h"
+
+// PW_ARG_COUNT counts the number of arguments it was called with. It evalulates
+// to an integer literal in the range 0 to 64. Counting more than 64 arguments
+// is not currently supported.
+//
+// PW_ARG_COUNT is most commonly used to count __VA_ARGS__ in a variadic macro.
+// For example, the following code counts the number of arguments passed to a
+// logging macro:
+//
+/* #define LOG_INFO(format, ...) { \
+ static const int kArgCount = PW_ARG_COUNT(__VA_ARGS__); \
+ SendLog(kArgCount, format, ##__VA_ARGS__); \
+ }
+*/
+// clang-format off
+#define PW_ARG_COUNT(...) \
+ _PW_ARG_COUNT_IMPL(__VA_ARGS__, \
+ 64, 63, 62, 61, 60, 59, 58, 57, \
+ 56, 55, 54, 53, 52, 51, 50, 49, \
+ 48, 47, 46, 45, 44, 43, 42, 41, \
+ 40, 39, 38, 37, 36, 35, 34, 33, \
+ 32, 31, 30, 29, 28, 27, 26, 25, \
+ 24, 23, 22, 21, 20, 19, 18, 17, \
+ 16, 15, 14, 13, 12, 11, 10, 9, \
+ 8, 7, 6, 5, 4, 3, 2, PW_HAS_ARGS(__VA_ARGS__))
+
+// Expands to 1 if one or more arguments are provided, 0 otherwise.
+#define PW_HAS_ARGS(...) PW_NOT(PW_HAS_NO_ARGS(__VA_ARGS__))
+
+// Expands to 0 if one or more arguments are provided, 1 otherwise. This
+// approach is from Jens Gustedt's blog:
+// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
+//
+// Normally, with a standard-compliant C preprocessor, it's impossible to tell
+// whether a variadic macro was called with no arguments or with one argument.
+// A macro invoked with no arguments is actually passed one empty argument.
+//
+// This macro works by checking for the presense of a comma in four situations.
+// These situations give the following information about __VA_ARGS__:
+//
+// 1. It is two or more variadic arguments.
+// 2. It expands to one argument surrounded by parentheses.
+// 3. It is a function-like macro that produces a comma when invoked.
+// 4. It does not interfere with calling a macro when placed between it and
+// parentheses.
+//
+// If a comma is not present in 1, 2, 3, but is present in 4, then __VA_ARGS__
+// is empty. For this case (0001), and only this case, a corresponding macro
+// that expands to a comma is defined. The presence of this comma determines
+// whether any arguments were passed in.
+//
+// C++20 introduces __VA_OPT__, which would greatly simplify this macro.
+#define PW_HAS_NO_ARGS(...) \
+ _PW_HAS_NO_ARGS(_PW_HAS_COMMA(__VA_ARGS__), \
+ _PW_HAS_COMMA(_PW_MAKE_COMMA_IF_CALLED __VA_ARGS__), \
+ _PW_HAS_COMMA(__VA_ARGS__()), \
+ _PW_HAS_COMMA(_PW_MAKE_COMMA_IF_CALLED __VA_ARGS__()))
+
+#define _PW_HAS_COMMA(...) \
+ _PW_ARG_COUNT_IMPL(__VA_ARGS__, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
+
+#define _PW_ARG_COUNT_IMPL(a64, a63, a62, a61, a60, a59, a58, a57, \
+ a56, a55, a54, a53, a52, a51, a50, a49, \
+ a48, a47, a46, a45, a44, a43, a42, a41, \
+ a40, a39, a38, a37, a36, a35, a34, a33, \
+ a32, a31, a30, a29, a28, a27, a26, a25, \
+ a24, a23, a22, a21, a20, a19, a18, a17, \
+ a16, a15, a14, a13, a12, a11, a10, a09, \
+ a08, a07, a06, a05, a04, a03, a02, a01, \
+ count, ...) \
+ count
+
+// clang-format on
+#define _PW_HAS_NO_ARGS(a1, a2, a3, a4) \
+ _PW_HAS_COMMA(_PW_PASTE_RESULTS(a1, a2, a3, a4))
+#define _PW_PASTE_RESULTS(a1, a2, a3, a4) _PW_HAS_COMMA_CASE_##a1##a2##a3##a4
+#define _PW_HAS_COMMA_CASE_0001 ,
+#define _PW_MAKE_COMMA_IF_CALLED(...) ,
+
+// Expands to a comma followed by __VA_ARGS__, if __VA_ARGS__ is non-empty.
+// Otherwise, expands to nothing. This is useful when calling a function with
+// __VA_ARGS__, since it removes the extra comma when no arguments are
+// provided.
+//
+// This is a more flexible, standard-compliant version of ##__VA_ARGS__. Unlike
+// ##__VA_ARGS__, this can be used to eliminate an unwanted comma when
+// __VA_ARGS__ expands to an empty argument because an outer macro was called
+// with __VA_ARGS__ instead of ##__VA_ARGS__.
+//
+// This can be used to call variadic functions or provide variadic template
+// parameters from a macro. For example:
+//
+// #define MY_PRINTF(fmt, ...) MY_PRINTF_IMPL(fmt, __VA_ARGS__)
+// #define MY_PRINTF_IMPL(fmt, ...) printf(fmt PW_COMMA_ARGS(__VA_ARGS__))
+//
+#define PW_COMMA_ARGS(...) _PW_COMMA_ARGS(PW_HAS_ARGS(__VA_ARGS__), __VA_ARGS__)
+
+#define _PW_COMMA_ARGS(has_args, ...) _PW_COMMA_ARGS_X(has_args, __VA_ARGS__)
+#define _PW_COMMA_ARGS_X(has_args, ...) _PW_COMMA_ARGS_##has_args(__VA_ARGS__)
+#define _PW_COMMA_ARGS_0(...) // no args, no comma
+#define _PW_COMMA_ARGS_1(...) , __VA_ARGS__ // comma, followed by args
diff --git a/pw_preprocessor/public/pw_preprocessor/util.h b/pw_preprocessor/public/pw_preprocessor/util.h
new file mode 100644
index 000000000..11cd15896
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/util.h
@@ -0,0 +1,57 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+//
+// Small, general preprocessor macros for C and C++ code.
+#pragma once
+
+// Returns the number of elements in a C array.
+#define PW_ARRAY_SIZE(array) (sizeof(array) / sizeof(*array))
+
+// Prevents unused value compiler warnings.
+#define PW_UNUSED(value) ((void)sizeof(value))
+
+// Returns a string literal of the arguments after expanding macros.
+#define PW_STRINGIFY(...) _PW_STRINGIFY(__VA_ARGS__)
+#define _PW_STRINGIFY(...) #__VA_ARGS__
+
+#ifdef __cplusplus
+
+// Macro for inline extern "C" declarations. The following will compile
+// correctly for C and C++:
+//
+// PW_EXTERN_C ThisFunctionHasCLinkage(void);
+//
+#define PW_EXTERN_C extern "C"
+
+// Macros for opening and closing an extern "C" block. This avoids the need for
+// an #ifdef __cplusplus check around the extern "C" { and closing }. Example:
+//
+// PW_EXTERN_C_START
+//
+// void FunctionDeclarationForCppAndC(void);
+//
+// void AnotherFunctionDeclarationForCppAndC(int, char);
+//
+// PW_EXTERN_C_END
+//
+#define PW_EXTERN_C_START extern "C" {
+#define PW_EXTERN_C_END } // extern "C"
+
+#else // extern "C" is removed from C code
+
+#define PW_EXTERN_C
+#define PW_EXTERN_C_START
+#define PW_EXTERN_C_END
+
+#endif // __cplusplus
diff --git a/pw_preprocessor/util_test.cc b/pw_preprocessor/util_test.cc
new file mode 100644
index 000000000..0ce027f53
--- /dev/null
+++ b/pw_preprocessor/util_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+#include "pw_preprocessor/util.h"
+
+#include <cstdint>
+
+#include "pw_unit_test/framework.h"
+
+namespace pw {
+namespace {
+
+TEST(Macros, ArraySize) {
+ uint32_t hello_there[123];
+ static_assert(PW_ARRAY_SIZE(hello_there) == 123);
+
+ char characters[500];
+ static_assert(PW_ARRAY_SIZE(characters) == 500);
+ static_assert(PW_ARRAY_SIZE("2345") == 5);
+
+ struct Object {
+ int a;
+ uint64_t array[2048];
+ };
+ Object objects[404];
+
+ static_assert(PW_ARRAY_SIZE(objects) == 404);
+ static_assert(PW_ARRAY_SIZE(Object::array) == 2048);
+ static_assert(PW_ARRAY_SIZE(objects[1].array) == 2048);
+}
+
+TEST(Macros, UnusedVariable) {
+ int this_is_not_used = 12;
+ PW_UNUSED(this_is_not_used);
+
+ volatile void* volatile wow;
+ wow = nullptr;
+ PW_UNUSED(wow);
+}
+
+#define HELLO hello
+#define WORLD WORLD_IMPL()
+#define WORLD_IMPL() WORLD !
+
+TEST(Macros, Stringify) {
+ EXPECT_STREQ("", PW_STRINGIFY());
+ EXPECT_STREQ("> _ <", PW_STRINGIFY(> _ <));
+ EXPECT_STREQ("hello WORLD !", PW_STRINGIFY(HELLO WORLD));
+ EXPECT_STREQ("hello, WORLD !", PW_STRINGIFY(HELLO, WORLD));
+ EXPECT_STREQ("a, b, c, hello, WORLD ! 2",
+ PW_STRINGIFY(a, b, c, HELLO, WORLD 2));
+}
+
+} // namespace
+} // namespace pw