-
Notifications
You must be signed in to change notification settings - Fork 3k
Add static assert macro #3113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add static assert macro #3113
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯 👍
error messages sounds little strange for me. Could it be more descriptive ? |
I'm open to suggestions if you know of a construct that provides the error more clearly, but unfortunately the error message is a bit inflexible without language support. The macro can be improved by support for compiler-specific static-assert builtins, but I couldn't find any. |
@geky It would be nice to add a string describing the failure (similar to |
@kjbracey-arm @SeppoTakalo any thoughts about this ? |
Can do. I was wondering if anyone would request this since I haven't seen the message used for much. An alternative would be to stringize the I also found this construct for gcc in c, but I can't find anything similar for c++03: #define MBED_STATIC_ASSERT(expr, msg) __extension__ _Static_assert(expr, msg) |
I found static_assert very useful, so put a version in ns_types.h - this does the necessary rooting around to find a real version: https://github.com/ARMmbed/nanostack-libservice/blob/master/mbed-client-libservice/ns_types.h But it doesn't fall back to a "portable" one, because I failed to find a construct that worked in all compilers and all circumstances. Including it as a structure member is particularly problematic - and that's what I actually needed first. Would yours work there? Is a function declaration legal in a C structure? Not sure... Maybe you could borrow the checks before falling back to your base one,. Figured that one was good enough, because it worked at least on any GCC we were using, and most static asserts would be valid across all compilers, so at least they'd show up on the GCC builds. Note that the version checking there can be fragile, thanks to compilers amusingly defining the macros from other compilers. ARMCC deciding to define GNUC and the relevant version number without providing the GNU static assert extension was very amusing. |
#define CONCAT_(lhs, rhs) lhs##rhs
#define CONCAT(lhs, rhs) CONCAT_(lhs, rhs)
// identifier
CONCAT(my_identifier_at_, __LINE__)
#define STATIC_ASSERTION(cond, msg) \
enum { CONCAT(static_assertion_at_, __LINE__) = sizeof(char[cond ? 1 : -1]) }
@kjbracey-arm It is possible to have a portable implementation of static assert which work in C struct; but it will only work outside of a c struct! The trick is to use a zero width bitfield:
The bitfield declared does not occupy any size:
As a result it is possible to write: #define C_STRUCT_STATIC_ASSERTION(cond, msg) int : cond ? 0 : -1
struct foo_t {
int foo;
/* uncomment this to get a compile time error
C_STRUCT_STATIC_ASSERTION(0 == 1, "error");
*/
C_STRUCT_STATIC_ASSERTION(1 == 1, "success");
}; |
@pan- sneaky, yes, you could have two different macros for block/file versus struct scope. I was trying to come up with a single definition, but that's not a hard requirement. |
+1, I'll look into adding these
Is this problematic in the case of a function declaration? Redeclaring the function with the same type should have no issues. EDIT: This appears to break-down for member functions:
It should probably be given an mbed prefix, but with a prefix I'm not sure we should be too concerned with conflicts. |
I'm quoting the spec:
So yes, it is problematic. IIRC, The compilation of the static assert from this PR in a struct fail with ARMCC and IAR.
Is it not problematic, but it let the possibility to the user to use something unspecified (it will fail at link time,,,); I'm not sure it is the most correct things to do. |
@pan- makes good points, so I've updated the pr with the enum construct presented by @pan-. Although I did move the unique-symbol logic into a separate macro in an effort to make the error message cleaner. Also added tests I also adopted @kjbracey-arm's compiler checks (and a bit of his documentation ;) ) which gets us the gcc definitions when available, which is a big improvement on gcc builds. In regards to default behaviour, static assertions need to garuntee program conditions across compilers. It seems risky to just disable the assertions for the majority of C compilers supported by mbed. I went ahead and added the @pan-'s static-struct-assertion to support static assertion's in structs, though I'm not too opinionated on how struct assertions are handled. Let me know if there are other concerns |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good PR, but it requires few changes to be perfect. There is two points which raise questions:
- Should we encourage users (and ourselves) to use
MBED_STRUCT_STATIC_ASSERT
in C and C++ to enforce consistency ? - Is it possible write test which pass when their compilation fail ? This is the main point of this feature, makes the compilation fail when something incorrect is detected at compile time. Right now, only the case where the assertion should be true are tested. It would be helpful to test the cases where the assertion is false and ensure that the compilation fail as expected.
@@ -49,6 +49,65 @@ do { \ | |||
} while (0) | |||
#endif | |||
|
|||
|
|||
#define _MBED_CONCAT_(a, b) a##b |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just wondering if these kind of macro can be packed together. Preprocessor token concatenation is a common operation and in many places across the code base it is not done correctly (arguments not expanded...).
This might be done in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just wondering if these kind of macro can be packed together.
Good point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What file should these belong to do you think? toolchain.h? new file for preprocessor declarations? mbed_preprocessor.h?
|
||
#define _MBED_CONCAT_(a, b) a##b | ||
#define _MBED_CONCAT(a, b) _MBED_CONCAT_(a, b) | ||
#define _MBED_ASSERTION _MBED_CONCAT(_MBED_ASSERTION_AT_, __LINE__) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_MBED_CONCAT
and _MBED_ASSERTION
are reserved identifiers according to the C standard:
All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, my bad, will fix
* } | ||
* @endcode | ||
*/ | ||
#ifdef __cplusplus |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When IAR is used, it is possible to delegate handling of static assertion to the extension static_assert
.
/** MBED_STATIC_ASSERT | ||
* Declare compile-time assertions, results in compile-time error if condition is false | ||
* | ||
* The assertion acts as a declaration that can be placed at file scope, in a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to enhance the documentation by explaining that this macro is valid in C and C++ at file scope and in a code block. Then split the structure case in two:
- C++: this macro is valid.
- C: should use
MBED_STRUCT_STATIC_ASSERT
which is also valid for C++ code.
* }; | ||
* @endcode | ||
*/ | ||
#if defined(__cplusplus) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, a C++ compiler can treat a structure as a C one if the structure is imported as extern "C"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing with gcc 5.3.1, armc 5, and iar 7.4 show this is not the case. That being said I'll go ahead and drop the c++ special case to be safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your correct, "extern C" statement just affect linkage, it doesn't change anything in the compilation process. Indeed, when an element can't be translated to C linkage then it fallback to C++ linkage.
At the end, the change I've requested is not needed.
Still needs a bit of work for feedback, sounds like another file should be added for preprocessor definitions. mbed_preprocessor.h? |
@sg- There is two things which can be improved with the PR:
Both can be done at a latter stage but is should be referenced at least if it didn't happen in this PR. |
Lets get them resolved in this PR. Todos
Not fully onboard with using a builtin where this seems a language feature, not a tool, but guess it doesn't involve much. #bikesheding |
Added MBED_STATIC_ASSERT for compile-time assertions, results in compile-time error if condition is false The assertion acts as a declaration that can be placed at file scope, in a code block (except after a label), or as a member of a C++ class/struct/union. Unfortunately, there does not exist a backup construct for use in C class/struct/union contexts. An alternative macro, MBED_STRUCT_STATIC_ASSERT provides this ability to avoid disabling static assertions for the majority of mbed-supported C compilers.
@pan-, pr should be updated, let me know if I missed anything |
Open for review ! |
@geky You forgot to add |
Woops! Glad you noticed 😆 |
/morph nightly-build |
/morph test-nightly |
Result: ABORTEDYour command has finished executing! Here's what you wrote!
|
Failure was my fault, a few machines were stuck in maintenance mode and halted the whole process. Restarting now. /morph test-nightly |
Result: SUCCESSYour command has finished executing! Here's what you wrote!
Outputmbed Build Number: 999 All builds and test passed! |
Add static assert macro
Adds
MBED_STATIC_ASSERT
for compile-time assertions, results in compile-time error if condition is false.Example:
Assertion failure on GCC 5.3.1:
Assertion failure on ARMC5:
Assertion failure on IAR 7.4:
Open to suggestions
cc @theotherjimmy, @pan-