I'm designing a micro-framework for unit testing and want to be able to provide an ability for client to define a "test suite name". So I have the following header file called test_suite.h
:
static const char *const test_suite_name;
static inline void run_all_tests(void){
printf("Running ");
if(!test_suite_name){
printf("unnamed suite");
} else {
printf("%s suite", test_suite_name);
}
//run tests
}
The intention of this is to allow clients to "override" the test_suite_name
as follows:
#include "test_suite.h"
extern const char *const test_suite_name = "suite1";
I think the behavior of such usage is well-defined since static const char *const test_suite_name;
constitutes a tentative-definition and then extern const char *const test_suite_name = "suite1";
constitutes an external definition. There is no linkage disagreement since 6.2.2(p4)
:
For an identifier declared with the storage-class specifier
extern
in a scope in which a prior declaration of that identifier is visible,31) if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration.
I ran some experiments:
Prints the following error message:
error: redefinition of 'const char* const suite_name'
extern const char *const suite_name = "some suite";
Works completely fine with no warnings produced
gcc7.4.0
on my machine.
Produces warning:
warning: ‘test_suite_name’ initialized and declared ‘extern’
Question: Is the behavior of the code shown above well-defined?
I'm pretty sure that the behavior would be undefined if write the following:
#include "test_suite.h"
const char *const test_suite_name = "suite1"; //without extern
because of 6.2.2(p5)
(emphasize mine):
If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier
extern
. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.
So we would have linkage disagreement between static const char *const test_suite_name;
with internal linkage and const char *const test_suite_name = "suite1";
with external linkage.
extern
withstatic
is a workaround – Some Name-Wall -Wextra -pedantic
does not emit it. – Some Name-x c
to tell the compiler that the source file is written in C. 2. It seems that ideone does not expose compiler warnings, but it does mean that no warning is produced by the compiler. – cpplearnerstatic
should be fine since there is no linkage disagreement. No storage class specified compiles with no warnings even though it is UB (linkage disagreement) – Some Name