__declspec(selectany)

Started by
10 comments, last by matt77hias 6 years, 5 months ago

What is the added value of adding "__declspec(selectany)" to extern const?

🧙

Advertisement

Appearently it ensures COMDAT-folding:

https://msdn.microsoft.com/en-us/library/5tkz6s71.aspx

11 hours ago, Juliean said:

Appearently it ensures COMDAT-folding:

https://msdn.microsoft.com/en-us/library/5tkz6s71.aspx

I read that but I do not understand it: "At link time, if multiple definitions of a COMDAT are seen, the linker picks one and discards the rest."

Shouldn't the extern ensure that all declarations refer to one and the same definition? How could the linker see multiple definitions, why would I specify multiple definitions?

Note that the sample containing the __declspec(selectany) puts the definitions in the header instead of the body, so there will be multiple definitions. Though, I thought one had to put the definitions of extern const in a body file for non-int types similarly for static class members?

🧙

Extern does not guarantee that at all. It only says that the definition for the given symbol can also exist in another compilation unit, which typically prevents complaints from the compiler that some variable is not initialized before usage. Extern nor static say anything about in which file either should be present. Note that the following is completely valid when written in the same file:


extern int x;

int x = 5;

What static, for a static member, does say is that initialization needs to be done outside of the class body, with the below being valid when written in the same file once again:


class Foo
{
    static int x;
};

int Foo::x = 5;

Don't forget how includes work; once preprocessing is done the above scenario will be in some file regardless ;). Extern in the end just indicates that the definition may be in some other compilation unit (static on a member too, actually). It is simply externally linked, but does not disallow you from having the definition inside the same compilation unit as one of its declarations. 

As for why you would want multiple definitions, you might want to read a little more about the entirety of the COMDAT folding story in this series, though in this specific case with __declspec(selectany), I've only seen it used in some convoluted way to mimic weak linking, which is only supported by Microsoft's linker in undocumented fashion, apparently.

1 hour ago, AthosVG said:

Extern in the end just indicates that the definition may be in some other compilation unit (static on a member too, actually). It is simply externally linked, but does not disallow you from having the definition inside the same compilation unit as one of its declarations.

But assume we have an h.hpp that contains an extern int x declaration, and two translation units corresponding to a.cpp and b.cpp which both include h.hpp, then both translation units see the same x (so not their own copy and assigning to x from one translation unit will be visible by the other)?

1 hour ago, AthosVG said:

Note that the following is completely valid when written in the same file:



extern int x;

int x = 5;

But isn't their a problem if you put the definition inside the header as well and next include that same header in at least two different translation units?

🧙

1 hour ago, AthosVG said:

does say is that initialization needs to be done outside of the class body

But I can initialize const size_t class members inside the class definition itself? Would this class member variable then always be inlined? And if this is the case what would happen if I want to retrieve the address of the class member variable? 

🧙

6 minutes ago, matt77hias said:

But assume we have an h.hpp that contains an extern int x declaration, and two translation units corresponding to a.cpp and b.cpp which both include h.hpp, then both translation units see the same x (so not their own copy and assigning to x from one translation unit will be visible by the other)?

Yes they seem the same x. If x is defined as 5 in a.cpp, b.cpp will see it as being 5. If some function in b.cpp assigns the value 10 to x, then a.cpp will see x as being 10. 

Note how in this example, the declaration doesn't need to be marked as extern, assuming you aren't defining it in a.cpp or b.cpp. After all, in each compilation unit you are referring to the same x anyway; the one declared in h.hpp. What extern allows you to do is declare the variable x that is defined, somewhere, some place, but not necessarily in this compilation unit. The same goes for functions and their definitions, although these are implicitly extern. Perhaps it is easier to illustrate with functions.

Say you have this in a.cpp


int func();

int func2()
{
	return func();
}

and this in b.cpp


int func()
{
	return 0;
}

This will compile and link fine, because for the first line of a.cpp, it is already clear that this is a declaration and not a definition. For a variable, this can be considered ambiguous, hence you mark it with extern to indicate that it is merely a declaration. It is defined later, i.e., the symbol's definition is provided somewhere else, externally

I hope this clarifies that extern is basically just a declaration of the variable and that you can later define it, like with any function.

 

11 minutes ago, matt77hias said:

But I can initialize const size_t class members inside the class definition itself? Would this class member variable then always be inlined? And if this is the case what would happen if I want to retrieve the address of the class member variable? 

Yes, for all non-static data members, you can have a constant expression as in-class initializer. For const static data members too, but I believe only the built-in types (int, char, short etc.); others need to be defined out-of-line, but I'm not up to date on the exact rules on this one.

As for inlining, I'll base my answer of the inlining of functions, for which compilers also emit a non-inline version to take the address of, so: yes, but the variable also still exists despite the inlining's substitution. However, I'm not a 100% sure whether that would work the same.

26 minutes ago, AthosVG said:

I hope this clarifies that extern is basically just a declaration of the variable and that you can later define it, like with any function.

Thanks for the clarifications and the analogies between functions and variables. :)

🧙

Take a look at this for a quick refresher on linkage in C++. It's a good reference that covers all the relevant details, including how the compiler determines default linkage in the absence of a storage class specifier and the difference between a declaration versus a definition for both functions and variables.

14 hours ago, matt77hias said:

How could the linker see multiple definitions, why would I specify multiple definitions?

Perhaps you wouldn't, but there are a handful of constructs in C++ that produce multiple definitions as a matter of course, and they need special mechanisms (such as COMDAT) to support them properly. See vague linking for some additional insight and information (I also found Where's The Template? to be a rather interesting read on the related template instantiation problem).

On 11/16/2017 at 11:13 AM, matt77hias said:

What is the added value of adding "__declspec(selectany)" to extern const?

One thing it does is allow read-only global data items to participate in COMDAT folding and elimination. So if you had two (or more) read-only global variables with the same value:


__declspec(selectany) extern const int x = 5;
__declspec(selectany) extern const int y = 5;
__declspec(selectany) extern const int z = 5;

And you used the /OPT:ICF option, those variables would be collapsed into one in the final EXE/DLL, saving space. The /OPT:REF option would also allow the linker to eliminate unreferenced global variables completely, although be careful when doing this on objects since the initialization code will be removed as well!

I suppose a secondary "benefit" is being able to define variables in header files, since the linker will just pick one instead of complaining, but I wouldn't use it for that reason alone.

13 hours ago, Zipster said:

Take a look at this for a quick refresher on linkage in C++. It's a good reference that covers all the relevant details, including how the compiler determines default linkage in the absence of a storage class specifier and the difference between a declaration versus a definition for both functions and variables.

Very informative post. Didn't know the anonymous namespace vs static and int x vs extern int x{} constructs. Thank you very much for pointing these out. :)

🧙

This topic is closed to new replies.

Advertisement