diff options
author | Alexei Frolov <frolv@google.com> | 2019-11-01 16:31:19 -0700 |
---|---|---|
committer | Alexei Frolov <frolv@google.com> | 2019-11-04 16:07:34 -0800 |
commit | c10c81201dba4d6533b1270baec0cd24aca752b9 (patch) | |
tree | c56efe2a0bfb9ba2398868a7ec144deea6226d24 /pw_preprocessor | |
parent | 1a82c146ff11c1b0486d12716082476eabad19dd (diff) | |
download | pigweed-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.gn | 88 | ||||
-rw-r--r-- | pw_preprocessor/README.md | 4 | ||||
-rw-r--r-- | pw_preprocessor/boolean_test.cc | 81 | ||||
-rw-r--r-- | pw_preprocessor/concat_test.cc | 86 | ||||
-rw-r--r-- | pw_preprocessor/docs.rst | 63 | ||||
-rw-r--r-- | pw_preprocessor/macro_arg_count_test.cc | 255 | ||||
-rw-r--r-- | pw_preprocessor/public/pw_preprocessor/boolean.h | 74 | ||||
-rw-r--r-- | pw_preprocessor/public/pw_preprocessor/compiler.h | 60 | ||||
-rw-r--r-- | pw_preprocessor/public/pw_preprocessor/concat.h | 66 | ||||
-rw-r--r-- | pw_preprocessor/public/pw_preprocessor/macro_arg_count.h | 124 | ||||
-rw-r--r-- | pw_preprocessor/public/pw_preprocessor/util.h | 57 | ||||
-rw-r--r-- | pw_preprocessor/util_test.cc | 66 |
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 |