Bug 989460 - Part 1: Add MacroArgs.h for macros related to implementing variadic macros. r=Waldo
authorBirunthan Mohanathas <birunthan@mohanathas.com>
Thu, 24 Apr 2014 10:48:00 +0200
changeset 180070 c7399918527aae630408026e801464308a95419c
parent 180069 e4f8969d11422863be77c4842a64e609a4c8d42d
child 180071 910de4b746382f35a350f534889c3b47743adbcb
push id42683
push usercbook@mozilla.com
push dateFri, 25 Apr 2014 06:30:37 +0000
treeherdermozilla-inbound@910de4b74638 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs989460
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 989460 - Part 1: Add MacroArgs.h for macros related to implementing variadic macros. r=Waldo This also converts MOZ_RELEASE_ASSERT and MOZ_BEGIN_NESTED_ENUM_CLASS to use the new helper macros.
mfbt/Assertions.h
mfbt/MacroArgs.h
mfbt/TypedEnum.h
mfbt/moz.build
mfbt/tests/TestMacroArgs.cpp
mfbt/tests/moz.build
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -7,16 +7,17 @@
 /* Implementations of runtime and static assertion macros for C and C++. */
 
 #ifndef mozilla_Assertions_h
 #define mozilla_Assertions_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Compiler.h"
 #include "mozilla/Likely.h"
+#include "mozilla/MacroArgs.h"
 
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #ifdef WIN32
    /*
     * TerminateProcess and GetCurrentProcess are defined in <winbase.h>, which
     * further depends on <windef.h>.  We hardcode these few definitions manually
@@ -290,40 +291,23 @@ MOZ_ReportCrash(const char* s, const cha
 /* Now the two-argument form. */
 #define MOZ_ASSERT_HELPER2(expr, explain) \
    do { \
      if (MOZ_UNLIKELY(!(expr))) { \
        MOZ_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \
        MOZ_REALLY_CRASH(); \
      } \
    } while (0)
-/* And now, helper macrology up the wazoo. */
-/*
- * Count the number of arguments passed to MOZ_ASSERT, very carefully
- * tiptoeing around an MSVC bug where it improperly expands __VA_ARGS__ as a
- * single token in argument lists.  See these URLs for details:
- *
- *   http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement
- *   http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/#comment-644
- */
-#define MOZ_COUNT_ASSERT_ARGS_IMPL2(_1, _2, count, ...) \
-   count
-#define MOZ_COUNT_ASSERT_ARGS_IMPL(args) \
-       MOZ_COUNT_ASSERT_ARGS_IMPL2 args
-#define MOZ_COUNT_ASSERT_ARGS(...) \
-   MOZ_COUNT_ASSERT_ARGS_IMPL((__VA_ARGS__, 2, 1, 0))
-/* Pick the right helper macro to invoke. */
-#define MOZ_ASSERT_CHOOSE_HELPER2(count) MOZ_ASSERT_HELPER##count
-#define MOZ_ASSERT_CHOOSE_HELPER1(count) MOZ_ASSERT_CHOOSE_HELPER2(count)
-#define MOZ_ASSERT_CHOOSE_HELPER(count) MOZ_ASSERT_CHOOSE_HELPER1(count)
-/* The actual macros. */
-#define MOZ_ASSERT_GLUE(x, y) x y
+
+#define MOZ_RELEASE_ASSERT_GLUE(a, b) a b
 #define MOZ_RELEASE_ASSERT(...) \
-   MOZ_ASSERT_GLUE(MOZ_ASSERT_CHOOSE_HELPER(MOZ_COUNT_ASSERT_ARGS(__VA_ARGS__)), \
-                   (__VA_ARGS__))
+   MOZ_RELEASE_ASSERT_GLUE( \
+     MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \
+     (__VA_ARGS__))
+
 #ifdef DEBUG
 #  define MOZ_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__)
 #else
 #  define MOZ_ASSERT(...) do { } while(0)
 #endif /* DEBUG */
 
 /*
  * MOZ_ASSERT_IF(cond1, cond2) is equivalent to MOZ_ASSERT(cond2) if cond1 is
new file mode 100644
--- /dev/null
+++ b/mfbt/MacroArgs.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implements various macros meant to ease the use of variadic macros.
+ */
+
+#ifndef mozilla_MacroArgs_h
+#define mozilla_MacroArgs_h
+
+/*
+ * MOZ_PASTE_PREFIX_AND_ARG_COUNT(aPrefix, ...) counts the number of variadic
+ * arguments and prefixes it with |aPrefix|. For example:
+ *
+ *   MOZ_PASTE_PREFIX_AND_ARG_COUNT(, foo, 42) expands to 2
+ *   MOZ_PASTE_PREFIX_AND_ARG_COUNT(A, foo, 42, bar) expands to A3
+ *
+ * You must pass in between 1 and 50 (inclusive) variadic arguments, past
+ * |aPrefix|. It is not legal to do
+ *
+ *   MOZ_PASTE_PREFIX_AND_ARG_COUNT(prefix)
+ *
+ * (that is, pass in 0 variadic arguments). To ensure that a compile-time
+ * error occurs when these constraints are violated, use the
+ * MOZ_STATIC_ASSERT_VALID_ARG_COUNT macro with the same variaidc arguments
+ * wherever this macro is used.
+ *
+ * Passing (__VA_ARGS__, <rest of arguments>) rather than simply calling
+ * MOZ_MACROARGS_ARG_COUNT_HELPER2(__VA_ARGS__, <rest of arguments>) very
+ * carefully tiptoes around a MSVC bug where it improperly expands __VA_ARGS__
+ * as a single token in argument lists. For details, see:
+ *
+ *   http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement
+ *   http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/#comment-644
+ */
+#define MOZ_PASTE_PREFIX_AND_ARG_COUNT(aPrefix, ...) \
+  MOZ_MACROARGS_ARG_COUNT_HELPER((__VA_ARGS__, \
+    aPrefix##50, aPrefix##49, aPrefix##48, aPrefix##47, aPrefix##46, \
+    aPrefix##45, aPrefix##44, aPrefix##43, aPrefix##42, aPrefix##41, \
+    aPrefix##40, aPrefix##39, aPrefix##38, aPrefix##37, aPrefix##36, \
+    aPrefix##35, aPrefix##34, aPrefix##33, aPrefix##32, aPrefix##31, \
+    aPrefix##30, aPrefix##29, aPrefix##28, aPrefix##27, aPrefix##26, \
+    aPrefix##25, aPrefix##24, aPrefix##23, aPrefix##22, aPrefix##21, \
+    aPrefix##20, aPrefix##19, aPrefix##18, aPrefix##17, aPrefix##16, \
+    aPrefix##15, aPrefix##14, aPrefix##13, aPrefix##12, aPrefix##11, \
+    aPrefix##10, aPrefix##9,  aPrefix##8,  aPrefix##7,  aPrefix##6,  \
+    aPrefix##5,  aPrefix##4,  aPrefix##3,  aPrefix##2,  aPrefix##1, aPrefix##0))
+
+#define MOZ_MACROARGS_ARG_COUNT_HELPER(aArgs) \
+  MOZ_MACROARGS_ARG_COUNT_HELPER2 aArgs
+
+#define MOZ_MACROARGS_ARG_COUNT_HELPER2( \
+   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, a33, a34, a35, a36, a37, a38, a39, a40, \
+  a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, \
+  a51, ...) a51
+
+/*
+ * MOZ_STATIC_ASSERT_VALID_ARG_COUNT ensures that a compile-time error occurs
+ * when the argument count constraints of MOZ_PASTE_PREFIX_AND_ARG_COUNT are
+ * violated. Use this macro wherever MOZ_PASTE_PREFIX_AND_ARG_COUNT is used
+ * and pass it the same variadic arguments.
+ *
+ * This macro employs a few dirty tricks to function. To detect the zero
+ * argument case, |(__VA_ARGS__)| is stringified, sizeof-ed, and compared to
+ * what it should be in the absence of arguments.
+ *
+ * Detecting too many arguments is a little trickier. With a valid argument
+ * count and a prefix of 1, MOZ_PASTE_PREFIX_AND_ARG_COUNT expands to e.g. 14.
+ * With a prefix of 0.0, it expands to e.g. 0.04. If there are too many
+ * arguments, it expands to the first argument over the limit. If this
+ * exceeding argument is a number, the assertion will fail as there is no
+ * number than can simultaneously be both > 10 and < 0.1. If the exceeding
+ * argument is not a number, a compile-time error will still occur because the
+ * exceeding argument is compared to an int and a double.
+ */
+#define MOZ_MACROARGS_STRINGIFY_HELPER(x) #x
+#define MOZ_STATIC_ASSERT_VALID_ARG_COUNT(...) \
+  static_assert( \
+    sizeof(MOZ_MACROARGS_STRINGIFY_HELPER((__VA_ARGS__))) != sizeof("()") && \
+      MOZ_PASTE_PREFIX_AND_ARG_COUNT(1, __VA_ARGS__) > 10 && \
+      MOZ_PASTE_PREFIX_AND_ARG_COUNT(0.0, __VA_ARGS__) < 0.1, \
+    "MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments") /* ; */
+
+/*
+ * MOZ_ARGS_AFTER_N expands to its arguments excluding the first |N|
+ * arguments. For example:
+ *
+ *   MOZ_ARGS_AFTER_2(a, b, c, d) expands to: c, d
+ */
+#define MOZ_ARGS_AFTER_1(a1, ...) __VA_ARGS__
+#define MOZ_ARGS_AFTER_2(a1, a2, ...) __VA_ARGS__
+
+/*
+ * MOZ_ARG_N expands to its |N|th argument.
+ */
+#define MOZ_ARG_1(a1, ...) a1
+#define MOZ_ARG_2(a1, a2, ...) a2
+
+#endif /* mozilla_MacroArgs_h */
--- a/mfbt/TypedEnum.h
+++ b/mfbt/TypedEnum.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Macros to emulate C++11 typed enums and enum classes. */
 
 #ifndef mozilla_TypedEnum_h
 #define mozilla_TypedEnum_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/MacroArgs.h"
 
 #if defined(__cplusplus)
 
 #if defined(__clang__)
    /*
     * Per Clang documentation, "Note that marketing version numbers should not
     * be used to check for language features, as different vendors use different
     * numbering schemes. Instead, use the feature checking macros."
@@ -285,43 +286,22 @@
    * as T depends on template parameters (more specifically here, T _is_ a template
    * parameter) so as MOZ_ENUM_CLASS_ENUM_TYPE(T) expands to T::Enum, we are missing
    * the required "typename" keyword. So here, MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE
    * is needed.
    */
 #  define MOZ_TEMPLATE_ENUM_CLASS_ENUM_TYPE(Name) typename Name::Enum
 #endif
 
-   /*
-    * Count the number of arguments passed to MOZ_COUNT_BEGIN_ENUM_CLASS_ARGS,
-    * very carefully tiptoeing around an MSVC bug where it improperly expands
-    * __VA_ARGS__ as a single token in argument lists. See these URLs for
-    * details:
-    *
-    *   http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement
-    *   http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/#comment-644
-    */
-#  define MOZ_COUNT_BEGIN_ENUM_CLASS_ARGS_IMPL2(_1, _2, count, ...) \
-     count
-#  define MOZ_COUNT_BEGIN_ENUM_CLASS_ARGS_IMPL(args) \
-     MOZ_COUNT_BEGIN_ENUM_CLASS_ARGS_IMPL2 args
-#  define MOZ_COUNT_BEGIN_ENUM_CLASS_ARGS(...) \
-     MOZ_COUNT_BEGIN_ENUM_CLASS_ARGS_IMPL((__VA_ARGS__, 2, 1, 0))
-   /* Pick the right helper macro to invoke. */
-#  define MOZ_BEGIN_NESTED_ENUM_CLASS_CHOOSE_HELPER2(count) \
-    MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER##count
-#  define MOZ_BEGIN_NESTED_ENUM_CLASS_CHOOSE_HELPER1(count) \
-     MOZ_BEGIN_NESTED_ENUM_CLASS_CHOOSE_HELPER2(count)
-#  define MOZ_BEGIN_NESTED_ENUM_CLASS_CHOOSE_HELPER(count) \
-     MOZ_BEGIN_NESTED_ENUM_CLASS_CHOOSE_HELPER1(count)
-   /* The actual macro. */
-#  define MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE(x, y) x y
+#  define MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE(a, b) a b
 #  define MOZ_BEGIN_NESTED_ENUM_CLASS(...) \
-     MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE(MOZ_BEGIN_NESTED_ENUM_CLASS_CHOOSE_HELPER(MOZ_COUNT_BEGIN_ENUM_CLASS_ARGS(__VA_ARGS__)), \
-                                      (__VA_ARGS__))
+     MOZ_BEGIN_NESTED_ENUM_CLASS_GLUE( \
+       MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER, \
+                                      __VA_ARGS__), \
+       (__VA_ARGS__))
 
 #  define MOZ_BEGIN_ENUM_CLASS(...) MOZ_BEGIN_NESTED_ENUM_CLASS(__VA_ARGS__)
 #  define MOZ_END_ENUM_CLASS(Name) \
      MOZ_END_NESTED_ENUM_CLASS(Name) \
      MOZ_FINISH_NESTED_ENUM_CLASS(Name)
 
 #endif /* __cplusplus */
 
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -31,16 +31,17 @@ EXPORTS.mozilla = [
     'EnumSet.h',
     'FloatingPoint.h',
     'GuardObjects.h',
     'HashFunctions.h',
     'IntegerPrintfMacros.h',
     'IntegerTypeTraits.h',
     'Likely.h',
     'LinkedList.h',
+    'MacroArgs.h',
     'MathAlgorithms.h',
     'Maybe.h',
     'MemoryChecking.h',
     'MemoryReporting.h',
     'Move.h',
     'MSIntTypes.h',
     'NullPtr.h',
     'NumericLimits.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestMacroArgs.cpp
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/MacroArgs.h"
+
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(100, a, b, c) == 1003, "");
+
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, a, b, c) == 3, "");
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, a) == 1, "");
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, !a) == 1, "");
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, (a, b)) == 1, "");
+
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, MOZ_ARGS_AFTER_1(a, b, c)) == 2,
+              "MOZ_ARGS_AFTER_1(a, b, c) should expand to 'b, c'");
+static_assert(MOZ_ARGS_AFTER_2(a, b, 3) == 3,
+              "MOZ_ARGS_AFTER_2(a, b, 3) should expand to '3'");
+
+static_assert(MOZ_ARG_1(10, 20, 30) == 10, "");
+static_assert(MOZ_ARG_2(10, 20, 30) == 20, "");
+
+int main()
+{
+}
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -12,16 +12,17 @@ CPP_UNIT_TESTS += [
     'TestCeilingFloor.cpp',
     'TestCheckedInt.cpp',
     'TestCountPopulation.cpp',
     'TestCountZeroes.cpp',
     'TestEndian.cpp',
     'TestEnumSet.cpp',
     'TestFloatingPoint.cpp',
     'TestIntegerPrintfMacros.cpp',
+    'TestMacroArgs.cpp',
     'TestRollingMean.cpp',
     'TestSHA1.cpp',
     'TestTypedEnum.cpp',
     'TestTypeTraits.cpp',
     'TestWeakPtr.cpp',
 ]
 
 if not CONFIG['MOZ_ASAN']: