What are "extern char condition tricks"? - c++

I was reading the GCC documentation on C and C++ function attributes. In the description of the error and warning attributes, the documentation casually mentions the following "trick":
error ("message")
warning ("message")
If the error or warning attribute is used on a function declaration and a call to such a function is not eliminated through dead code elimination or other optimizations, an error or warning (respectively) that includes message is diagnosed. This is useful for compile-time checking, especially together with __builtin_constant_p and inline functions where checking the inline function arguments is not possible through extern char [(condition) ? 1 : -1]; tricks.
While it is possible to leave the function undefined and thus invoke a link failure (to define the function with a message in .gnu.warning* section), when using these attributes the problem is diagnosed earlier and with exact location of the call even in presence of inline functions or when not emitting debugging information.
There's no further explanation. Perhaps it's obvious to programmers immersed in the environment, but it's not at all obvious to me, and I could not find any explanation online. What is this technique and when might I use it?

I believe the premise is to have a compile time assert functionality. Suppose that you wrote
extern char a[(condition) ? 1 : -1];
If condition is true, nothing happens and the line compiles to nothing. The extern makes sure that a doesn't use any memory. However, if condition is false, a is declared as an array of negative length, and you get a compile time error.
You probably wrap it in a macro and have something similar to static_assert
#define STATIC_ASSERT(condition) extern char a[(condition) ? 1 : -1]

Related

C++ mark a function as experimental/not fully implemented

I have a function I will need to leave partially implemented for a variety of reasons and I want to prevent future users (read as me in the future when I have forgotten that I did this) to know the function is incomplete, buggy and untested.
Option n1 is merely adding a comment // Warning this thing is partially implemented and will break randomly
This however won't create compile time warnings so, I am not a fan.
Option n2 is to use [[deprecated("reason")]] which has the advantage of raising compile warnings but its misleading, the function wasn't deprecated it's actually the opposite of deprecation, it's a WIP and will perhaps one day be fully implemented.
Are there alternatives?
The [[deprecated]] attribute is exactly what this is for (emphasis mine) :
https://en.cppreference.com/w/cpp/language/attributes
[deprecated]
[deprecated("reason")]
indicates that the use of the name or entity declared with this attribute is allowed, but discouraged for some reason
You can still use the function, you just get a warning message that you shouldn't rely on its use.
Caveat: MSVC breaks the standard and emits a compiler error (due to SDL flag being turned on by default) instead of a warning.
The only thing in standard C++ for this is the [[deprecated("message")]] attribute.
GNU has a non-standard Function Attribute for warning messages:
warning ("message")
If this attribute is used on a function declaration and a call to such a function is not eliminated through dead code elimination or other optimizations, a warning which will include message will be diagnosed. This is useful for compile time checking, especially together with __builtin_constant_p and inline functions. While it is possible to define the function with a message in .gnu.warning* section, when using this attribute the problem will be diagnosed earlier and with exact location of the call even in presence of inline functions or when not emitting debugging information.

Warning: function 'F' is not needed and will not be emitted

Here is a little program (live on godbolt):
static void UnusedDeclaration();
static void UnusedDefinition() {}
static void Declaration();
decltype(Declaration())* global;
Ideally I would expect the following warnings, if I compile it with clang, -Wunused:
UnusedDeclaration(): It is an unused function declaration with internal linkage. So I should get a warning.
UnusedDefinition(): It is an unused function definition with internal linkage. So I should get a warning.
Declaration(): This declaration is used. So I should not get a warning.
Actually all three cases get a warning:
warning: unused function 'UnusedDeclaration'
warning: unused function 'UnusedDefinition'
warning: function 'Declaration' is not needed and will not be emitted
I have a problem with case 3. I think, the compiler should not warn me about anything, but it does.
Why do I get a warning for case 3?
function 'Declaration' is not needed – I honestly need that declaration. I use it in an unevaluated context, the compiler does not need to complain about it.
and will not be emitted – I am not sure what emitting means. I have written a separate question about it.
Is there a combination of warning flags to pass to the compiler on the command line, to achieve what I expect (warning for case 1 and 2, no warning for case 3).
I could turn off warning for case 3 with -Wunused -Wno-unneeded-internal-declaration, but I don't know if I lose some important information with it. Maybe in some other cases this warning is useful. In what exact situation does this 3rd warning come?
There's no reference to the first function, so it can be basically ignored.
The second function is "called", but as you noted, only in an unevaluated context. That means the call will never be evaluated. The compiler determines the type the function would return if it was called, but that's all. Since it's never actually called, the function itself is never needed or used as a function, so the compiler doesn't generate any code for it.
The messages are different because even though the final effect is the same, each reaches that result slightly differently, and the compiler is trying to give you the information that might be useful.

Why having assert before main() causing syntax error "error: expected ')' before numeric constant"?

Why assert is causing compilation error if I use it before main() call? It causes compilation error (Syntax error):
test.cpp:4:8: error: expected ')' before numeric constant
Note: I'm trying to understand why I'm a getting a syntax error for calling it outside main. That too related to something like "numeric constant", but not like function / macro called out side main function. If the error was simpler like function / macro called out side main, then my question wouldn't have made any sense.
#include <stdio.h>
#include <assert.h>
assert(0); //If I comment assert from here, then it compiles fine.
int main()
{
assert(0);
return 0;
}
Since all the other answers are "it didn't work because you can't do that", here's one that dives in to why the error message looks the way it does.
The short reason is compilers aren't smart, and they don't make good teachers.
When we, the experienced human programmers, look at this code
assert(0);
int main(void){}
we recognize the first line as an attempt to call a function, and use that as the starting point of an explanation about why it's invalid and what you should have written instead. There's a bit of mind-reading involved in the recognition of your intent, because you expressed that intent with incorrect code.
The compiler can't read your mind, and by definition the only way it can recognize your intent is if your code is valid. Since it doesn't understand what you're trying to do, it can't give helpful advice.
What the compiler is actually doing is just taking in your code, one token at a time, and parsing it according to a grammar. If the code is ungrammatical, there will be a point at which the next token doesn't match any grammar rules. The compiler then tells you what the offending token was. In this case the offending token was the numeric constant 0.
The tokens preceding the 0 were parsed without complaint, but that doesn't mean they were interpreted the way you intended them.
It's hard to completely analyze this specific case because <assert.h> declares assert as a macro, and I don't know what it expands to on your system. To make the explanation a little easier, let's use a normal function:
/* sample program 1 */
foo(0);
int main(void){}
The above code fails to compile. My compiler says
error: expected declaration specifiers or '...' before numeric constant
But this slightly altered version:
/* sample program 2 */
foo();
int main(void){}
compiles with only some warnings, because in old-style C, the first line is a valid declaration of a function foo, using the implicit int return type! If my intent was to somehow "call foo before main" then it's a total failure. If my intent was to only declare foo then I'm only guilty of using an obsolete language feature.
Now, knowing about the second sample, look back at the first sample. When the compiler reads the first 2 tokens, foo and (, everything is fine. The program still fits the grammar of (old-fashioned) C. Then it gets to the 0 and there's no way to proceed. So it tells me that the numeric constant is the problem.
Now what about those things it says it was "expecting"? Those are the tokens that can come next in a valid program. It suggests a declaration specifier list rather than an immediate ), because this is valid too:
foo(int);
int main(){}
Even this, using an old-style function definition, would be a possible continuation:
foo(i)
int i;
{
}
int main(){}
Bottom line: whenever there's a parse error, the compiler reports the point where your program diverged grammatically from the set of all possible valid programs. The point where the parse diverged semantically from your intent is sometimes earlier. Sometimes a lot earlier. In that case, the compiler's "expected" tokens can be quite irrelevant.
You can't call free functions (be it assert (which is usually a macro, but still ends up being code that is run in global scope like a function) or any other function) from the global scope (you can however call functions as part of initializing a global variable (in C++)). That's why.
Move the assert into main or some other function.
In the C language the code can be executed only inside the functions. So if you place any code outside the functions you will get the compile errors.
C++ allows global variables to be initialized by non constants as the constructors are called during the object creation.
main is the entry point of your program. The only code run before main is initialization code. If you want to have assert run before main, you can have a global class with assert in its constructor, like:
class assert_class {
assert_class() { assert(0); }
};
assert_class assert_here;
int main()

How can I invoke a compile-time error via a C++ macro?

The idea is to cause a compile-time error with an error message if a certain macro is invoked. Can this be done? How?
#ifdef RUBBISH_COMPILER
# define alignof(T) // what here?
#endif
const std::size_t = alignof(some_type); // invocation, causing compilation error
The invocation shall produce a sensible error message like
alignof() not available with this compiler.
In C++11,
#define DONT_INVOKE_ME static_assert(false, "Don't invoke this macro");
Historically, it was easy to cause an error, but trickier to get a message into the output. One simple trick was to create an invalid declaration, with the message in the declared name:
#define DONT_INVOKE_ME char dont_invoke_this_macro[-1];
This isn't perfect, as you can't use freeform text for the message - it must be a valid identifier. There were fancier tricks (such as those used by Boost's static assert), but they're only of historical interest these days.

C/C++ unused inline function undefined reference

Consider the following code (this is not pthread specific; other examples, such as those involving the realtime library, exhibit similar behavior):
#define _GNU_SOURCE
#include <pthread.h>
inline void foo() {
static cpu_set_t cpuset;
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}
int main(int argc, char *argv[]) { }
This is a valid program in C and in C++. So I save the contents of this to testc.c and testcpp.cpp and try to build.
When I build in C++ I get no error. When I build in C I get an undefined reference error. Now, this error occurs in -O1 and in -O3. Is there anyway to instruct gcc to do the right thing (see that foo is unused and skip the requirement for a definition of pthread_setaffinity_np)?
EDIT: I thought it was obvious from context, but the error message is:
/tmp/ccgARGVJ.o: In function `foo':
testc.c:(.text+0x17): undefined reference to `pthread_setaffinity_np'
Note that since foo isn't being referenced in the main path, g++ correctly ignores the function entirely but gcc does not.
EDIT 2: Let me try this one more time. The function foo, and the subsequent call to pthread_setaffinity_np, is unused. The main function is empty. Just look at it! Somehow, g++ figured out that foo did not need to be included, and subsequently the build process did not trip up when we intentionally omitted -lpthread (and checking the exported symbols with nm confirms that neither foo nor reference to pthread_setaffinity_np were needed). The resultant output from gcc didn't pick up on that fact.
I am asking this question because the C++ and the C frontends seem to give different results on the same input. This doesn't seem to be an ld issue prima facie because I would expect both paths to give the same linking error, which is why I emphasized that it seems to be a compiler issue. If both C++ and C gave problems, then yes I would agree that its a linking issue.
Well, apparently your program contains an error: you declare and call function pthread_setaffinity_np, but you never define it. Apparently you forgot to supply the library that contains the definition. This is an error in both C and C++.
In other words, this is not a valid program in C and in C++. It violates the One Definition Rule of C++ (and whatever the similar rule is called in C).
The rest depends on whether the compiler will catch this error and issue a diagnostic message for it. While formally the compiler is supposed to catch it, in reality linking errors are not always caught by the compilation process (in extended sense of the term, i.e. including linking as well).
Whether they are caught or not might depend on many factors. In this particular case the factor that matters is apparently the difference between the properties of inline functions of C and C++ languages. (And yes, they are really different between C and C++). I would guess that in C++ mode the compiler decided that this inline function does not need the actual body, while in C mode it decided to generate the body anyway.
So, again, if this program, somehow successfully compiles in some circumstances, it is only because you got lucky. You seem to believe that a function that is not called is supposed to be "ignored entirely". Neither C nor C++ make such guarantees. Assuming that the definition of pthread_setaffinity_np is indeed missing, your program is invalid in both C and C++. For this reason, the compiler that refused to compile it is actually the one with the correct behavior.
Taking the above into account, you might want to ask yourself whether you really care about why you got different error reports in C and C++ modes. If you do, it will require some research into the internal mechanics of that specific implementation and won't have much to do with the languages themselves.
In C, the inline keyword does not affect the linkage of the function. Thus foo has external linkage, and cannot be optimized out because it might be called from another translation unit. If the compiler/assembler put functions in their own individual sections and the linker is able to discard unneeded function sections at link time, it might be able to avoid a linking error, but to be correct, since this program references pthread_setaffinity_np, it must contain a definition for that function somewhere anyway, i.e. you must use -lpthread or equivalent.
In C++, inline functions have internal some weird pseudo-external linkage by default, so gcc optimized it out. See the comments for details.
In short, the lack of an error in certain configurations is a failure of gcc to diagnose an invalid program. It's not the behavior you should expect.
The other lesson you should take away from this is that C and C++ are nowhere near the same thing. Choose which one you're writing and stick to it! Don't try to write code that's "interchangeable" between the two or you're likely to make it subtly incorrect in both...
inline is only a suggestion, not something a compiler is obligated to listen to, so it can't assume that foo is not used in another compilation unit.
But, yeah, it would be nice to know exactly which is the undefined reference, given that you didn't post the error, and odd that it's shows up in C and not C++ compilation.
foo might not be used in your source code, but it's almost certainly referenced elsewhere in the build process and consequently it needs to be compiled.
Especially since a lot of optimization occur in the linking process, because the linker can determine that a function is "dead" and can be discarded.
If, internally, the linker decides to assemble the entire program as one pass, and then optimization in another, I would expect you to see this error (how can it assemble the whole program?)
Further, if the function is to be exported then it most certainly has to be compiled, linked, and end up in the output.
It sounds like you're relying on compiler/linker specific behavior.