static_assert() is a pretty great capability available since C11.
For pre-C11 compilers though, this capability must be emulated.
It's not too hard, there are many examples available over Internet.
For example :
#define STATIC_ASSERT(CONDITION, MSG) \
typedef char static_assert_##MSG[(CONDITION)?1:-1]
This makes it possible to transfer an error message in the condition, which is handy to explain what's going wrong if it ever gets triggered.
However, this MSG is a lot different from the one in C11's static_assert() :
It must be a single word
It must use only identifier characters
It cannot be a string with double quotes
This is so different from C11's static_assert() that it seems impossible to create a macro which would switch transparently between the C11 and the C90 version depending on the compiler.
In an effort to accept an error message which "looks like C11", aka a string with double quote, I've tested a new macro :
#define STATIC_ASSERT(CONDITION, MSG) \
typedef char static_assert[((void)(MSG), ((CONDITION)?1:-1))]
Using the , comma operator, this macro should accept MSG as a string, and just disregard it. But it will be displayed in case of error, which is the intention.
It works fine on clang, but not of gcc : error: variably modified at file scope.
I'm trying to understand why, and if there is a work around
If you replace the typedef-array-trick with the enum-trick, then you will get something that seems to work with both clang and gcc:
#define CONDITION 1
#define TOKENPASTE(a, b) a ## b // "##" is the "Token Pasting Operator"
#define TOKENPASTE2(a,b) TOKENPASTE(a, b) // expand then paste
#define static_assert(x, msg) enum { TOKENPASTE2(ASSERT_line_,__LINE__) \
= 1 / (msg && (x)) }
static_assert( CONDITION, "This should pass");
static_assert(!CONDITION, "This should fail");
This gives me, with gcc for example, on line 9 of foo.c:
foo.c:9: warning: division by zero [-Wdiv-by-zero]
static_assert(!CONDITION, "This should fail");
^
foo.c:9: error: enumerator value for 'ASSERT_line_9' is not an integer constant
static_assert(!CONDITION, "This should fail");
^~~~~~~~~~~~~
(Here the gcc switch -ftrack-macro-expansion=0 is used, as the extra error messages are not that helpful and just add noise.)
Note that some mangling of the name is still necessary, which you omitted. Here the text ASSERT_line_ is combined with the variable __LINE__. This ensures a unique name, provided:
You don't use it twice on a single line.
You don't use it in header files (or trust to luck).
Your code doesn't happen to use the identifiers like ASSERT_line_9 elsewhere.
For header files, you will need to add somewhere a single word with only identifier characters. For example:
#define static_assert3(x, msg, file) enum { TOKENPASTE2(file,__LINE__) = \
1 / (msg && (x)) }
#define static_assert(x, msg) static_assert3(x, msg, my_header_h_)
If this fails on line 17, gcc will give an error such as:
error: enumerator value for 'my_header_h_17' is not an integer constant
An alternative for the mangling in header files is to replace __LINE__ with __COUNTER__. I've not used it, because it is non-standard and because clang was slow to adopt it. But now it has been in gcc, msvc, and clang for about five years.
You could try the same modification with your typedef-array idea, and replace the comma operator with &&. Then your gcc error changes into a warning. For example, modifying your godbolt example to:
typedef char static_assert_2["hello world!" && (CONDITION) ? 1 : -1];
gives the unwanted warning: variably modified 'static_assert_2' at file scope for gcc.
One-liner
#define STATIC_ASSERT(CONDITION, MSG) { typedef char test[(CONDITION)?1:-1]; (void)(test*) #MSG; } (void)0
Related
My goal is to print the filenames and not relative path to the filename. I'm experimenting with it using the macro TRACE().
Since it's all in the same file, I'm simulating the filename as an input to TRACE(). So in real life, you could say the inputs are replaced with __FILE__.
Code:
#include <stdio.h>
#include <string.h>
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define __FILENAME__(x) TOSTRING(strrchr(x, '\\'))
#define TRACE(s, ...) \
{ \
if (strrchr(s, '\\')) { \
static const char str[] = __FILENAME__(s) "\n\r"; \
printf(str, ##__VA_ARGS__); \
} else { \
static const char str[] = s "\n\r"; \
printf(str, ##__VA_ARGS__); \
} \
}
int main() {
TRACE("file.c");
TRACE("parent\\file.c");
return 0;
}
Output:
file.c
strrchr("parent\\file.c", '\\')
So if it's local file, it's printed as file.c, which is great. This means the ifcase in the macro is working :). But when it's a file in another folder, I fail to "stringify" the computation strrchr(s, '\\'). Why?
Furthermore, I don't see an issue with the computation in the define, since everything is defined at compile time!! (That's why the if case is working, right?)
If I remove the TOSTRING() from __FILENAME__ I get loads of errors instead. Because it fails to concatenate the output of __FILENAME__ with str[]
Is there a way to solve this?
Preliminary observations
Note that in C (as opposed to C++), you can't initialize a static const char str[] array with the result of a function call. If the strrchr() found a backslash, you probably want to print the name from one after the backslash. And the stringification isn't going to stringify the result of invoking strrchr().
Also note that you should not create function or variable names that start with an underscore, in general. C11 §7.1.3 Reserved identifiers says (in part):
All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.
See also What does double underscore (__const) mean in C?
Since the first argument to your TRACE macro is already a string, there's not much benefit to applying the stringification — unless you want the double quotes to appear when the name is printed.
Simple adaptation
To get more or less the result you want, you would need to accept that there'll be run-time overhead invoking strrchr() each time you pass the trace (or a more elaborate scheme for initialization), along the lines of:
#define TRACE(s, ...) \
do { \
const char *basename = strrchr(s, '\\'); \
if (basename == 0) \
basename = s; \
else \
basename++; \
printf(basename, ## __VA_ARGS__); \
} while (0)
The do { … } while (0) idiom is standard; it allows you to write:
if (something)
TRACE("hocuspocus.c: test passed\n");
else
TRACE("abracadabra.c: test failed\n");
If you use the braces-only notation in the question, the semicolon after the first TRACE makes the else into a syntax error. See also C #define macro for debug printing and Why use apparently meaningles do { … } while (0) and if … else statements in macros? and do { … } while (0) — what is it good for?
The ## __VA_ARGS__ trick is fine as long as you know that it is a GCC (and Clang because it is compatible with GCC) extension, and not a part of standard C.
It also isn't entirely clear how you plan to use the variable arguments. It looks as though you'd be able to do:
TRACE("some\\kibbitzer.c: value %d is out of the range [%d..%d]\n",
value, MIN_RANGE, MAX_RANGE);
where the file name is embedded in the format string. Maybe you have in mind:
TRACE(__FILE__ ": value %d is out of the range [%d..%d]\n",
value, MIN_RANGE, MAX_RANGE);
That can work; __FILE__ is a string literal, unlike __func__ which is a predefined identifier (static const char __func__[] = "…function name…";).
Finally (for now), consider whether trace output should go to standard output or to standard error. It is easily arguable it should go to standard error; it (probably) isn't part of the regular output of the program.
I recommend looking at the 'debug macro' question and answer — but I am biassed since I wrote the top-scoring answer.
Reducing runtime overhead
You can reduce the runtime overhead to a single call to strrchr() per file name, as long as you aren't messing with automatic variables etc. You'll be OK if you're using string literals.
#define TRACE(s, ...) \
do { \
static const char *basename = 0;
if (basename == 0) \
{
if ((basename = strrchr(s, '\\')) == 0) \
basename = s; \
else \
basename++; \
} \
printf(basename, ## __VA_ARGS__); \
} while (0)
This initializes the basename to null; on the first pass through the code, basename is set to the correct position in the string; thereafter, there is no further call to strrchr().
Warning: the code shown has not been compiled.
I think there is some issue with the understanding of how macros and functions work.
Macros are not "executed", they are just simple text substitution. Yes, that happens in the compile time (actually pre compiling), but just the substitution.
Macros won't execute and code or call any functions (like strrchr) while compiling.
In your code you have -
#define __FILENAME__(x) TOSTRING(strrchr(x, '\\'))
Whenever __FILENAME__(foo) is used, it is replaced with "strrchr(foo, '\\')". I am sure this is not what you want.
Personally, I don't see any reason for using macros here. Just make it into a normal function. The compiler will optimize it for you.
Is there a general method to shield comments in macro arguments in C? I know that parentheses can be used for this purpose, but that will not work in cases where added parentheses result in syntax errors in the macro output. I've heard that ({ }) works to shield commas in GCC, but I need this code to also work in VC++ (one of the recent versions which does conform to the C standard with regard to commas in macros). I also cannot use variadic macros in my case.
The specific case I'm trying to do is this (lengthof is a macro defined elsewhere). I'm trying to write a single macro for the entire thing because this will be used many times, and having a multi-macro solution would add a large amount of additional testing code.
#define TEST_UNFUNC(func, res_type, res_set, op_type, op_set) \
{ \
static const res_type res[] = res_set; \
static const op_type op[] = op_set; \
int i; \
for (i = 0; i < MIN(lengthof(res), lengthof(op)); i++) \
assert(func(op[i]) == res[i]); \
}
If possible I would like a general answer and not merely a workaround specific to this particular macro.
Use parentheses to shield the comma, and then pass them through a special unparen macro, defined in the example below:
#include <stdio.h>
#define really_unparen(...) __VA_ARGS__
#define invoke(expr) expr
#define unparen(args) invoke(really_unparen args)
#define fancy_macro(a) printf("%s %s\n", unparen(a))
int main()
{
fancy_macro(("Hello", "World"));
}
The trick here is that the invoke macro forces an extra expansion, allowing really_unparen to be called even though it's not followed by parentheses in the source.
Edit: per comment below, this appears to not be necessary in this case. Though I'm sure I've hit a case where I needed it sometime ... and it doesn't hurt.
I'm using Oxygen with CDT 9.3.0 built-in.
When I use a macro I defined that uses _Generic, all those macro uses are underlined with "syntax error", but the project compiles fine (which is setup to use my makefiles).
After reading a similar so question, and since _Generic begin from C11 possibly not supported by eclipse's code analysis, I tried defining a symbol for my macro definition to empty but it didn't work. (At project settings, C/C++ General->Paths and Symbols->Symbols Tab, GNU C, added symbol CONVERT(...) without a value and added a symbol CONVERT(X), and CONVERT() and CONVERT without a value).
For example my macro is:
#define FIRST_(_1, ...) _1
#define FIRST(...) FIRST_(__VA_ARGS__, _1, _2, _3)
#define CONVERT(...) \
_Generic((FIRST(__VA_ARGS__)), \
char* : toText, \
int : toInt, \
) (__VA_ARGS__)
and usage point, that gives the syntax error:
void* test = CONVERT("testme");
As #ErikW pointed out, _Generic is a C11 feature that Eclipse CDT's parser does not support yet. This bug tracks adding support for it.
(By the way, contributions to Eclipse CDT's C11 support are very welcome!)
It is possible to work around this using macros.
The problem with trying to define another version of the CONVERT(...) macro in "Paths and Symbols" is that the macros defined there are treated as if you wrote them at the very top of your file. A subsequent redefinition in your actual code overwrites the definition from "Paths and Symbols".
I can think of two approaches to go about this:
Approach 1
CDT defines a special macro __CDT_PARSER__ which evaluates to true when it's parsing the code, but false when the code is actually compiled.
You can take advantage of this to define a different version of CONVERT(...) for CDT's purposes:
#ifdef __CDT_PARSER__
#define CONVERT(...)
#else
#define CONVERT(...) \
_Generic((FIRST(__VA_ARGS__)), \
char* : toText, \
int : toInt, \
) (__VA_ARGS__)
#endif
This almost works, but not quite. We still get a syntax error, because this line:
void* test = CONVERT("testme", 42);
will now expand to:
void* test = ;
As you can see, we don't actually want an empty expansion for CONVERT(...). We want an expansion that will parse as a variable's initializer. 0 will work:
#ifdef __CDT_PARSER__
#define CONVERT(...) 0
#else
...
#endif
Approach 2
Instead of defining a different version of CONVERT(...), we could define _Generic(...) itself to be a macro for CDT's purposes.
This time, we can do it in "Paths and Symbols", because there is no redefinition of _Generic(...) in the code that would mess it up.
So let's define a symbol in "Paths and Symbols", with _Generic(...) as the name and an empty value.
Now, this line:
void* test = CONVERT("testme", 42);
will expand to:
void* test = _Generic((FIRST("testme", 42)), \
char* : toText, \
int : toInt, \
) ("testme", 42)
which will in turn expand to:
void* test = ("testme", 42);
which parses (("testme", 42) parses as a parenthesized comma-expression and is thus a valid initializer).
This approach has the advantage that you don't need to modify your actual code, and that it handles all uses of the _Generic macro rather than just the one in CONVERT.
On the other hand, it's possible that for some other uses of the _Generic macro, this particular expansion won't parse. If that's the case, you might be able to come up with a different expansion that will parse for all uses, or else you can go with Approach 1.
I want to deprecate a macro in such a way that it will print a nice warning even if used inside of a #if statement.
This answer is very nearly what I want, but it throws an error when the macro is accessed from within a #if statement.
#include <stdio.h>
#define DEPRECATED_CONSTANT _Pragma ("GCC warning \"Deprecated constant!\"") 0
#define DEPRECATED_FUNCTION(...) _Pragma ("GCC warning \"Deprecated function!\"") printf(__VA_ARGS__)
int main() {
// Prints a warning (good)
int n = DEPRECATED_CONSTANT;
// Prints a warning (good)
DEPRECATED_FUNCTION("%d\n", n);
// Throws an error (bad)
#if DEPRECATED_CONSTANT
return 1;
#else
return 2;
#endif
}
The error is:
error: missing binary operator before token "("
Bonus points if you can find me a cross-platform compatible solution!
EDIT
I'm trying to handle a breaking change in a library gracefully - I want users to have a nice, clear warning (or error) whenever they use an old macro, so it will be clear that they need to migrate their code to using the new macro. These pragma solutions only work if the value of that constant is used in code, not if the value is accessed in a preprocessor directive.
According to the answers provided below, it seems like there's not a solution to this problem (except possibly when using clang?). Thanks, everyone.
I want to deprecate a macro in such a way that it will print a nice
warning even if used inside of a #if statement.
I was going to suggest the comma operator, but that doesn't seem to work because the _Pragma macro probably yields no real code. Also, gcc, at least, explicitly says you can't do what you suggested with _Pragma():
https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html
The standard is unclear on where a _Pragma operator can appear. The
preprocessor does not accept it within a preprocessing conditional
directive like ‘#if’. To be safe, you are probably best keeping it out
of directives other than ‘#define’, and putting it on a line of its
own.
PS - clang 8.1.0 didn't error on your program and gave the warnings you want ...
As #jschultz410 mentions, what you are trying to do is explicitly forbidden in gcc (see https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html).
Nested macros seem appropriate for such a use case:
#include <stdio.h>
#define DEPRECATED_CONSTANT_VALUE 0
#define DEPRECATED_CONSTANT _Pragma ("GCC warning \"Deprecated constant!\"") DEPRECATED_CONSTANT_VALUE
#define DEPRECATED_FUNCTION(...) _Pragma ("GCC warning \"Deprecated function!\"") printf(__VA_ARGS__)
int main() {
// Prints a warning (good)
int n = DEPRECATED_CONSTANT;
// Prints a warning (good)
DEPRECATED_FUNCTION("%d\n", n);
// Throws an error (bad)
#if DEPRECATED_CONSTANT_VALUE
return 1;
#else
return 2;
#endif
}
Yes, this is kind of gross, but in the land of preprocessor logic we're already giving up on any kind of design elegance in the first place. At least this way the macro interface is maintained in non-preprocessor code. (Yes, this would not print the preprocessor warning in the #if statement, but unfortunately that's not possible with gcc).
I am now read some c code. And is not very clear about the "#define someting" expression.
For example, I saw this code:
typedef enum cairo_path_op {
CAIRO_PATH_OP_MOVE_TO = 0,
CAIRO_PATH_OP_LINE_TO = 1,
CAIRO_PATH_OP_CURVE_TO = 2,
CAIRO_PATH_OP_CLOSE_PATH = 3
} __attribute__ ((packed)) cairo_path_op_t; /* Don't want 32 bits if we can avoid it. */
#ifndef __GNUC__
#define __attribute__(x)
#endif
I take attention to the "__attribute__(x)". since in other header file , the "__attribute__(x)" is defined with no value, but how it take effect in the enum "cairo_path_op" define?
This is for portability reasons.
__attribute__() is a GCC extension for modifying various properties and behavior of functions, variables, types, etc.
If a non-GCC-compatible compiler tries to compile code that uses this extension, it won't able to do so and will throw a syntax error.
In order to avoid this, the author of the code makes the preprocessor replace this keyword with nothing if __GNUC__ is not defined (i. e. if the compiler is not a GCC-compatible one), so that the code builds on a bigger variety of platforms.