2
votes

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:

  1. https://coliru.stacked-crooked.com:

Prints the following error message:

error: redefinition of 'const char* const suite_name'
 extern const char *const suite_name = "some suite";

DEMO

  1. https://ideone.com/:

Works completely fine with no warnings produced

DEMO

  1. 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.

1
You might want to take a look at the discussion here: gcc.gnu.org/bugzilla/show_bug.cgi?id=45977Stephan Schlecht
@StephanSchlecht Nice catch. The issue is about exactly the case I'm considering. Probably replacing extern with static is a workaroundSome Name
@StephanSchlecht But ommiting storage class specifier entirely is UB. Is there a way to produce a warning in such a case? -Wall -Wextra -pedantic does not emit it.Some Name
1. The coliru DEMO probably needs a -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.cpplearner
@StephanSchlecht Yes, static 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

1 Answers

2
votes

Use of static

You may actually be able to use static instead of external. Making a quick test with gcc under Ubuntu:

#include "test_suite.h"

static const char *const test_suite_name = "huhu";

int main() {
      run_all_tests();
      return 0;
}

If I compile with:

gcc -Wall -Wpedantic -Wextra mytest.c -o mytest

it gives as output:

Running huhu suite

Omitting static

If you accidentally forget to specify static it should give a compile time error then. So if I change this line to:

const char *const test_suite_name = "huhu";

and try to compile it like this:

gcc -Wall -Wpedantic -Wextra mytest2.c -o mytest2

this error message will be displayed:

mytest2.c:3:19: error: non-static declaration of ‘test_suite_name’ follows static declaration
 const char *const test_suite_name = "huhu";
                   ^~~~~~~~~~~~~~~
In file included from mytest2.c:1:
test_suite.h:3:26: note: previous declaration of ‘test_suite_name’ was here
 static const char *const test_suite_name;

Since it is an error it is also output if you compile with:

gcc mytest2.c -o mytest2

Screenshot of Error Message

Screenshot with Error Message