pw_preprocessor: Rename macro; reorganize header

- Rename PW_ARG_COUNT to PW_MACRO_ARG_COUNT.
- Reorganize the arguments.h header to put the high-level, easier-to-use
  macros first (PW_DELEGATE_BY_ARG_COUNT and PW_COMMA_ARGS).
- Fix the test that demonstrates what happens when PW_COMMA_ARGS is
  used incorrectly.
- Update the documentation for arguments.h.

Change-Id: I741995c02e1f36ca805f4ba15eb113caa98164f8
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/17501
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_preprocessor/arguments_test.cc b/pw_preprocessor/arguments_test.cc
index 2ca76c5..4196298 100644
--- a/pw_preprocessor/arguments_test.cc
+++ b/pw_preprocessor/arguments_test.cc
@@ -11,12 +11,14 @@
 // 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.
+
+// Many of these tests are static asserts. If these compile, they pass. The TEST
+// functions are used for organization only.
 
 #include "pw_preprocessor/arguments.h"
 
+#include <tuple>
+
 #include "pw_unit_test/framework.h"
 
 namespace pw {
@@ -126,45 +128,38 @@
 #define BAD_DEMO(fmt, ...) _BAD_DEMO_ADD_123(fmt PW_COMMA_ARGS(__VA_ARGS__))
 
 #define _BAD_DEMO_ADD_123(fmt, ...) \
-  _BAD_DEMO_STRINGIFY("%d: " fmt, 123 PW_COMMA_ARGS(__VA_ARGS__))
+  _BAD_DEMO_CAPTURE_ARGS("%d: " fmt, 123 PW_COMMA_ARGS(__VA_ARGS__))
 
-#define _BAD_DEMO_STRINGIFY(...) PW_STRINGIFY(__VA_ARGS__)
+#define _BAD_DEMO_CAPTURE_ARGS(...) std::make_tuple(__VA_ARGS__)
 
-TEST(CommaVarargs, MisbehavesWithMacroToMacroUse) {
-// Disable this EXPECT for now; it breaks on ARM/discovery due to the
-// preprocessor adding space before the comma after Hello World:
-//
-//   "Hello world" , 123
-//
-// rather than (as on host builds):
-//
-//   "Hello world", 123
-//
-// TODO(keir): Fix the inconsistency and restore this expectation.
-#if 0
-  // With no additional arguments, everything is OK
-  EXPECT_STREQ(BAD_DEMO("Hello world"), R"("%d: " "Hello world", 123)");
-#endif
+TEST(CommaVarargs, MisbehavesWithMacroToMacroUse_NoArgs_ArgsAreOkay) {
+  auto [a1, a2] = BAD_DEMO("Hello world");
+  EXPECT_STREQ(a1, "%d: Hello world");
+  EXPECT_EQ(a2, 123);
+}
 
+TEST(CommaVarargs, MisbehavesWithMacroToMacroUse_WithArgs_ArgsOutOfOrder) {
   // If there is an additional argument, the order is incorrect! The 123
   // argument should go before the "world?" argument, but it is inserted after.
   // This would be a compilation error if these arguments were passed to printf.
   // What's worse is that this can silently fail if the arguments happen to be
   // compatible types.
-  EXPECT_STREQ(BAD_DEMO("Hello %s", "world?"),
-               R"("%d: " "Hello %s" , "world?", 123)");
+  const auto [a1, a2, a3] = BAD_DEMO("Hello %s", "world?");
+  EXPECT_STREQ(a1, "%d: Hello %s");
+  EXPECT_STREQ(a2, "world?");
+  EXPECT_EQ(a3, 123);
 }
 
 TEST(CountArgs, Zero) {
-  static_assert(PW_ARG_COUNT() == 0);
-  static_assert(PW_ARG_COUNT(/**/) == 0);
-  static_assert(PW_ARG_COUNT(/* uhm, hi */) == 0);
+  static_assert(PW_MACRO_ARG_COUNT() == 0);
+  static_assert(PW_MACRO_ARG_COUNT(/**/) == 0);
+  static_assert(PW_MACRO_ARG_COUNT(/* uhm, hi */) == 0);
 
   // clang-format off
-  static_assert(PW_ARG_COUNT(     ) == 0);
-  static_assert(PW_ARG_COUNT(
+  static_assert(PW_MACRO_ARG_COUNT(     ) == 0);
+  static_assert(PW_MACRO_ARG_COUNT(
       ) == 0);
-  static_assert(PW_ARG_COUNT(
+  static_assert(PW_MACRO_ARG_COUNT(
       // wow
       // This is a comment.
       ) == 0);
@@ -173,23 +168,23 @@
 
 TEST(CountArgs, Commas) {
   // clang-format off
-  static_assert(PW_ARG_COUNT(,) == 2);
-  static_assert(PW_ARG_COUNT(,,) == 3);
-  static_assert(PW_ARG_COUNT(,,,) == 4);
+  static_assert(PW_MACRO_ARG_COUNT(,) == 2);
+  static_assert(PW_MACRO_ARG_COUNT(,,) == 3);
+  static_assert(PW_MACRO_ARG_COUNT(,,,) == 4);
   // clang-format on
-  static_assert(PW_ARG_COUNT(, ) == 2);
-  static_assert(PW_ARG_COUNT(, , ) == 3);
-  static_assert(PW_ARG_COUNT(, , , ) == 4);
+  static_assert(PW_MACRO_ARG_COUNT(, ) == 2);
+  static_assert(PW_MACRO_ARG_COUNT(, , ) == 3);
+  static_assert(PW_MACRO_ARG_COUNT(, , , ) == 4);
 }
 
 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);
-  static_assert(PW_ARG_COUNT(1, (2, 3, 4), (<5, 6>)) == 3);
+  static_assert(PW_MACRO_ARG_COUNT(()) == 1);
+  static_assert(PW_MACRO_ARG_COUNT((1, 2, 3, 4)) == 1);
+  static_assert(PW_MACRO_ARG_COUNT((1, 2, 3), (1, 2, 3, 4)) == 2);
+  static_assert(PW_MACRO_ARG_COUNT((), ()) == 2);
+  static_assert(PW_MACRO_ARG_COUNT((-), (o)) == 2);
+  static_assert(PW_MACRO_ARG_COUNT((, , (, , ), ), (123, 4)) == 2);
+  static_assert(PW_MACRO_ARG_COUNT(1, (2, 3, 4), (<5, 6>)) == 3);
 }
 
 template <typename... Args>
@@ -260,7 +255,7 @@
   static_assert(FunctionArgCount(PW_DROP_LAST_ARG_IF_EMPTY(1, 2, 3, )) == 3);
 }
 
-#define SOME_VARIADIC_MACRO(...) PW_ARG_COUNT(__VA_ARGS__)
+#define SOME_VARIADIC_MACRO(...) PW_MACRO_ARG_COUNT(__VA_ARGS__)
 
 #define ANOTHER_VARIADIC_MACRO(arg, ...) SOME_VARIADIC_MACRO(__VA_ARGS__)
 
@@ -295,75 +290,75 @@
    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})  ')
+  print(f'  static_assert(PW_MACRO_ARG_COUNT({", ".join(args)}) == {i})  ')
 */
 TEST(CountArgs, AllSupported) {
   // clang-format off
-  static_assert(PW_ARG_COUNT() == 0);
-  static_assert(PW_ARG_COUNT(X1) == 1);
-  static_assert(PW_ARG_COUNT(X1, X2) == 2);
-  static_assert(PW_ARG_COUNT(X1, X2, X3) == 3);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4) == 4);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5) == 5);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6) == 6);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7) == 7);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8) == 8);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9) == 9);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10) == 10);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11) == 11);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12) == 12);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13) == 13);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14) == 14);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15) == 15);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16) == 16);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17) == 17);
-  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18) == 18);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
-  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);
+  static_assert(PW_MACRO_ARG_COUNT() == 0);
+  static_assert(PW_MACRO_ARG_COUNT(X1) == 1);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2) == 2);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3) == 3);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4) == 4);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5) == 5);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6) == 6);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7) == 7);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8) == 8);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9) == 9);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10) == 10);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11) == 11);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12) == 12);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13) == 13);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14) == 14);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15) == 15);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16) == 16);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17) == 17);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18) == 18);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19) == 19);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20) == 20);
+  static_assert(PW_MACRO_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21) == 21);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
+  static_assert(PW_MACRO_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);
   // clang-format on
 }
 
diff --git a/pw_preprocessor/docs.rst b/pw_preprocessor/docs.rst
index ae08b13..74345f1 100644
--- a/pw_preprocessor/docs.rst
+++ b/pw_preprocessor/docs.rst
@@ -17,6 +17,51 @@
 =======
 The preprocessor module provides several headers.
 
+pw_preprocessor/arguments.h
+---------------------------------
+Defines macros for handling variadic arguments to function-like macros. Macros
+include the following:
+
+.. c:function:: PW_DELEGATE_BY_ARG_COUNT(name, ...)
+
+  Selects and invokes a macro based on the number of arguments provided. Expands
+  to ``<name><arg_count>(...)``. For example,
+  ``PW_DELEGATE_BY_ARG_COUNT(foo_, 1, 2, 3)`` expands to ``foo_3(1, 2, 3)``.
+
+  This example shows how ``PW_DELEGATE_BY_ARG_COUNT`` could be used to log a
+  customized message based on the number of arguments provided.
+
+  .. code-block:: cpp
+
+      #define ARG_PRINT(...)  PW_DELEGATE_BY_ARG_COUNT(_ARG_PRINT, __VA_ARGS__)
+      #define _ARG_PRINT_0(a)        LOG_INFO("nothing!")
+      #define _ARG_PRINT_1(a)        LOG_INFO("1 arg: %s", a)
+      #define _ARG_PRINT_2(a, b)     LOG_INFO("2 args: %s, %s", a, b)
+      #define _ARG_PRINT_3(a, b, c)  LOG_INFO("3 args: %s, %s, %s", a, b, c)
+
+  When used, ``ARG_PRINT`` expands to the ``_ARG_PRINT_#`` macro corresponding
+  to the number of arguments.
+
+  .. code-block:: cpp
+
+      ARG_PRINT();               // Outputs: nothing!
+      ARG_PRINT("a");            // Outputs: 1 arg: a
+      ARG_PRINT("a", "b");       // Outputs: 2 args: a, b
+      ARG_PRINT("a", "b", "c");  // Outputs: 3 args: a, b, c
+
+.. c:function:: PW_COMMA_ARGS(...)
+
+  Expands to a comma followed by the arguments if any arguments are provided.
+  Otherwise, expands to nothing. If the final argument is empty, it is omitted.
+  This is useful when passing ``__VA_ARGS__`` to a variadic function or template
+  parameter list, since it removes the extra comma when no arguments are
+  provided. ``PW_COMMA_ARGS`` must NOT be used when invoking a macro from
+  another macro.
+
+  For example. ``PW_COMMA_ARGS(1, 2, 3)``, expands to ``, 1, 2, 3``, while
+  ``PW_COMMA_ARGS()`` expands to nothing. ``PW_COMMA_ARGS(1, 2, )`` expands to
+  ``, 1, 2``.
+
 pw_preprocessor/boolean.h
 -------------------------
 Defines macros for boolean logic on literal 1s and 0s. This is useful for
@@ -32,19 +77,6 @@
 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.
diff --git a/pw_preprocessor/public/pw_preprocessor/arguments.h b/pw_preprocessor/public/pw_preprocessor/arguments.h
index 2d9a51f..bb7e80b 100644
--- a/pw_preprocessor/public/pw_preprocessor/arguments.h
+++ b/pw_preprocessor/public/pw_preprocessor/arguments.h
@@ -11,128 +11,13 @@
 // 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.
+
+// Macros for working with arguments to function-like macros.
 #pragma once
 
 #include "pw_preprocessor/boolean.h"
 #include "pw_preprocessor/internal/arg_count_impl.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__))
-// clang-format on
-
-// Argument count for using with a C/C++ function or template parameter list.
-// The difference from PW_MACRO_ARG_COUNT is that the last argument is not
-// counted if it is empty. This makes it easier to drop the final comma when
-// expanding to C/C++ code.
-#define PW_FUNCTION_ARG_COUNT(...) \
-  _PW_FUNCTION_ARG_COUNT(PW_LAST_ARG(__VA_ARGS__), __VA_ARGS__)
-
-#define _PW_FUNCTION_ARG_COUNT(last_arg, ...) \
-  _PW_PASTE2(_PW_FUNCTION_ARG_COUNT_, PW_EMPTY_ARGS(last_arg))(__VA_ARGS__)
-
-#define _PW_FUNCTION_ARG_COUNT_0 PW_ARG_COUNT
-#define _PW_FUNCTION_ARG_COUNT_1(...) \
-  PW_ARG_COUNT(PW_DROP_LAST_ARG(__VA_ARGS__))
-
-// Evaluates to the last argument in the provided arguments.
-#define PW_LAST_ARG(...) \
-  _PW_PASTE2(_PW_LAST_ARG_, PW_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__)
-
-// Evaluates to the provided arguments, excluding the final argument.
-#define PW_DROP_LAST_ARG(...) \
-  _PW_PASTE2(_PW_DROP_LAST_ARG_, PW_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__)
-
-// Evaluates to the provided arguments excluding the final argument..
-#define PW_DROP_LAST_ARG_IF_EMPTY(...)                                       \
-  _PW_IF(                                                                    \
-      PW_EMPTY_ARGS(PW_LAST_ARG(__VA_ARGS__)), PW_DROP_LAST_ARG, _PW_EXPAND) \
-  (__VA_ARGS__)
-
-// Expands to 1 if one or more arguments are provided, 0 otherwise.
-#define PW_HAS_ARGS(...) PW_NOT(PW_EMPTY_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 presence 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_EMPTY_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__()))
-
-// clang-format off
-
-#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. If the final argument is empty, it is omitted.
 // This is useful when passing __VA_ARGS__ to a variadic function or template
@@ -173,18 +58,19 @@
 
 #define _PW_COMMA_ARGS(...) , __VA_ARGS__
 
-// Allows calling a different function-like macro based on the number of
-// arguments.  For example:
+// Allows calling a different function-like macros based on the number of
+// arguments. For example:
 //
 //   #define ARG_PRINT(...)  PW_DELEGATE_BY_ARG_COUNT(_ARG_PRINT, __VA_ARGS__)
 //   #define _ARG_PRINT1(a)        LOG_INFO("1 arg: %s", a)
 //   #define _ARG_PRINT2(a, b)     LOG_INFO("2 args: %s, %s", a, b)
 //   #define _ARG_PRINT3(a, b, c)  LOG_INFO("3 args: %s, %s, %s", a, b, c)
 //
-// This can the be called in code:
+// This can the be called from C/C++ code:
+//
 //    ARG_PRINT("a");            // Outputs: 1 arg: a
-//    ARG_PRINT("a", "b");       // Outputs: 2 arg: a, b
-//    ARG_PRINT("a", "b", "c");  // Outputs: 3 arg: a, b, c
+//    ARG_PRINT("a", "b");       // Outputs: 2 args: a, b
+//    ARG_PRINT("a", "b", "c");  // Outputs: 3 args: a, b, c
 //
 #define PW_DELEGATE_BY_ARG_COUNT(function, ...)                 \
   _PW_DELEGATE_BY_ARG_COUNT(                                    \
@@ -192,3 +78,117 @@
       PW_DROP_LAST_ARG_IF_EMPTY(__VA_ARGS__))
 
 #define _PW_DELEGATE_BY_ARG_COUNT(function, ...) function(__VA_ARGS__)
+
+// PW_MACRO_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_MACRO_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_MACRO_ARG_COUNT(__VA_ARGS__); \
+         SendLog(kArgCount, format, ##__VA_ARGS__);                    \
+       }
+*/
+// clang-format off
+#define PW_MACRO_ARG_COUNT(...)                      \
+  _PW_MACRO_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__))
+// clang-format on
+
+// Argument count for using with a C/C++ function or template parameter list.
+// The difference from PW_MACRO_ARG_COUNT is that the last argument is not
+// counted if it is empty. This makes it easier to drop the final comma when
+// expanding to C/C++ code.
+#define PW_FUNCTION_ARG_COUNT(...) \
+  _PW_FUNCTION_ARG_COUNT(PW_LAST_ARG(__VA_ARGS__), __VA_ARGS__)
+
+#define _PW_FUNCTION_ARG_COUNT(last_arg, ...) \
+  _PW_PASTE2(_PW_FUNCTION_ARG_COUNT_, PW_EMPTY_ARGS(last_arg))(__VA_ARGS__)
+
+#define _PW_FUNCTION_ARG_COUNT_0 PW_MACRO_ARG_COUNT
+#define _PW_FUNCTION_ARG_COUNT_1(...) \
+  PW_MACRO_ARG_COUNT(PW_DROP_LAST_ARG(__VA_ARGS__))
+
+// Evaluates to the last argument in the provided arguments.
+#define PW_LAST_ARG(...) \
+  _PW_PASTE2(_PW_LAST_ARG_, PW_MACRO_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__)
+
+// Evaluates to the provided arguments, excluding the final argument.
+#define PW_DROP_LAST_ARG(...) \
+  _PW_PASTE2(_PW_DROP_LAST_ARG_, PW_MACRO_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__)
+
+// Evaluates to the arguments, excluding the final argument if it is empty.
+#define PW_DROP_LAST_ARG_IF_EMPTY(...)                                       \
+  _PW_IF(                                                                    \
+      PW_EMPTY_ARGS(PW_LAST_ARG(__VA_ARGS__)), PW_DROP_LAST_ARG, _PW_EXPAND) \
+  (__VA_ARGS__)
+
+// Expands to 1 if one or more arguments are provided, 0 otherwise.
+#define PW_HAS_ARGS(...) PW_NOT(PW_EMPTY_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 presence 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_EMPTY_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__()))
+
+// clang-format off
+
+#define _PW_HAS_COMMA(...)                                           \
+  _PW_MACRO_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_MACRO_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(...) ,
diff --git a/pw_preprocessor/public/pw_preprocessor/concat.h b/pw_preprocessor/public/pw_preprocessor/concat.h
index e8045e1..0d9bc11 100644
--- a/pw_preprocessor/public/pw_preprocessor/concat.h
+++ b/pw_preprocessor/public/pw_preprocessor/concat.h
@@ -18,10 +18,11 @@
 
 // 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__)
+#define PW_CONCAT(...) \
+  _PW_CONCAT_IMPL1(PW_MACRO_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.
+// Expand the macro to allow PW_MACRO_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__)
 
diff --git a/pw_tokenizer/argument_types_test.cc b/pw_tokenizer/argument_types_test.cc
index a4d2676..67c1d17 100644
--- a/pw_tokenizer/argument_types_test.cc
+++ b/pw_tokenizer/argument_types_test.cc
@@ -75,7 +75,7 @@
 // uint64_t).
 #define PACKED_TYPES(...)                                                 \
   ((PW_CONCAT(0b, __VA_ARGS__, u) << PW_TOKENIZER_TYPE_COUNT_SIZE_BITS) | \
-   PW_ARG_COUNT(__VA_ARGS__))
+   PW_MACRO_ARG_COUNT(__VA_ARGS__))
 
 // Test this test macro for both uint32_t and uint64_t.
 #if PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 4
diff --git a/pw_tokenizer/public/pw_tokenizer/tokenize.h b/pw_tokenizer/public/pw_tokenizer/tokenize.h
index 3c36950..fd776b4 100644
--- a/pw_tokenizer/public/pw_tokenizer/tokenize.h
+++ b/pw_tokenizer/public/pw_tokenizer/tokenize.h
@@ -190,11 +190,11 @@
                                                                                \
   /* Check that the macro is invoked with a supported number of arguments. */  \
   static_assert(                                                               \
-      PW_ARG_COUNT(__VA_ARGS__) <= PW_TOKENIZER_MAX_SUPPORTED_ARGS,            \
+      PW_FUNCTION_ARG_COUNT(__VA_ARGS__) <= PW_TOKENIZER_MAX_SUPPORTED_ARGS,   \
       "Tokenized strings cannot have more than "                               \
       PW_STRINGIFY(PW_TOKENIZER_MAX_SUPPORTED_ARGS) " arguments; "             \
-      PW_STRINGIFY(PW_ARG_COUNT(__VA_ARGS__)) " arguments were used for "      \
-      #format " (" #__VA_ARGS__ ")");                                          \
+      PW_STRINGIFY(PW_FUNCTION_ARG_COUNT(__VA_ARGS__))                         \
+      " arguments were used for " #format " (" #__VA_ARGS__ ")");              \
                                                                                \
   /* Tokenize the string to a pw_tokenizer_Token at compile time. */           \
   _PW_TOKENIZER_CONST pw_tokenizer_Token _pw_tokenizer_token =                 \