Jump to content
  • Advertisement
Sign in to follow this  
matt77hias

__declspec(selectany)

This topic is 367 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Advertisement
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?

Edited by matt77hias

Share this post


Link to post
Share on other sites

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.

Edited by AthosVG

Share this post


Link to post
Share on other sites
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?

Edited by matt77hias

Share this post


Link to post
Share on other sites
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? 

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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. :)

Share this post


Link to post
Share on other sites

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.

Edited by Zipster

Share this post


Link to post
Share on other sites
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. :)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!