Use C11 static assert mechanism by default (#761, #766)
Hopefully this works better by default and is less confusing
when it doesn't. Added notes to migration document on how
to restore previous behavior.
diff --git a/docs/migration.md b/docs/migration.md
index 52711b5..f781276 100644
--- a/docs/migration.md
+++ b/docs/migration.md
@@ -31,6 +31,25 @@
**Error indications:** "`protoc: Unknown flag: --nanopb_opt`"
+### `pb.h` uses C11 `_Static_assert` keyword by default
+
+**Rationale:** The nanopb generated headers use static assertions to catch
+errors at compile time. There are several mechanisms to implement this.
+The most widely supported is C11 `_Static_assert` keyword.
+Previously the code used negative size array definition trick, which is
+supported already in C99 but does not work with every compiler and can
+produce confusing error messages.
+
+**Changes:** Now `_Static_assert` is used by default. If preferred, the
+old default behavior can be restored by defining `PB_C99_STATIC_ASSERT`
+in `pb.h` or on compiler command line.
+
+**Required actions:** If the keyword is not recognized, set the compiler to
+C11 standard mode if available. If it is not available, define either `PB_C99_STATIC_ASSERT`
+or `PB_NO_STATIC_ASSERT`.
+
+**Error indications:** "`Undefined identifier _Static_assert`
+
Nanopb-0.4.4 (2020-11-25)
-------------------------
diff --git a/docs/reference.md b/docs/reference.md
index e378881..2d591ae 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -20,6 +20,8 @@
* `PB_ENCODE_ARRAYS_UNPACKED`: Encode scalar arrays in the unpacked format, which takes up more space. Only to be used when the decoder on the receiving side cannot process packed arrays, such as [protobuf.js versions before 2020](https://github.com/protocolbuffers/protobuf/issues/1701).
* `PB_CONVERT_DOUBLE_FLOAT`: Convert doubles to floats for platforms that do not support 64-bit `double` datatype. Mainly `AVR` processors.
* `PB_VALIDATE_UTF8`: Check whether incoming strings are valid UTF-8 sequences. Adds a small performance and code size penalty.
+* `PB_C99_STATIC_ASSERT`: Use C99 style negative array trick for static assertions. For compilers that do not support C11 standard.
+* `PB_NO_STATIC_ASSERT`: Disable static assertions at compile time. Only for compilers with limited support of C standards.
The `PB_MAX_REQUIRED_FIELDS` and `PB_FIELD_32BIT` settings allow
raising some datatype limits to suit larger messages. Their need is
diff --git a/pb.h b/pb.h
index cc32546..644a759 100644
--- a/pb.h
+++ b/pb.h
@@ -165,14 +165,17 @@
# if defined(__ICCARM__)
/* IAR has static_assert keyword but no _Static_assert */
# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG);
-# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
- /* C11 standard _Static_assert mechanism */
-# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG);
-# else
+# elif defined(PB_C99_STATIC_ASSERT)
/* Classic negative-size-array static assert mechanism */
# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER
+# elif defined(__cplusplus)
+ /* C++11 standard static_assert mechanism */
+# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG);
+# else
+ /* C11 standard _Static_assert mechanism */
+# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG);
# endif
# endif
#else
diff --git a/tests/SConstruct b/tests/SConstruct
index 93b1ce9..b2b8b36 100644
--- a/tests/SConstruct
+++ b/tests/SConstruct
@@ -162,12 +162,13 @@
# Debug info, warnings as errors
env.Append(CFLAGS = '-g -Wall -Werror ')
- env.Append(CORECFLAGS = '-Wextra')
+ env.Append(CORECFLAGS = '-Wextra ')
# Pedantic ANSI C. On AVR this doesn't work because we use large
# enums in some of the tests.
if env.get("EMBEDDED") != "AVR":
- env.Append(CFLAGS = '-ansi -pedantic')
+ env.Append(CFLAGS = '-ansi ')
+ env.Append(CORECFLAGS = '-pedantic ')
# Profiling and coverage
if not env.get("EMBEDDED"):
diff --git a/tests/cxx_descriptor/SConscript b/tests/cxx_descriptor/SConscript
index a11aff0..1b609b7 100644
--- a/tests/cxx_descriptor/SConscript
+++ b/tests/cxx_descriptor/SConscript
@@ -19,6 +19,9 @@
print("Skipping {} test - compiler doesn't support it".format(std))
continue
+ if std == 'c++03':
+ e.Append(CPPDEFINES = {'PB_C99_STATIC_ASSERT': 1})
+
o1 = e.Object('message_descriptor_{}'.format(std), 'message_descriptor.cc')
o2 = e.Object('message.pb_{}'.format(std), 'message.pb.c')
p = e.Program([o1, o2])
diff --git a/tests/splint/splint.rc b/tests/splint/splint.rc
index 0cf4376..c01e2fc 100644
--- a/tests/splint/splint.rc
+++ b/tests/splint/splint.rc
@@ -36,3 +36,5 @@
-noeffect
-usedef
+# Splint doesn't support C11
+-DPB_C99_STATIC_ASSERT