[C++] Global variables in headers.

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

User avatar
Diadem
Posts: 5654
Joined: Wed Jun 11, 2008 11:03 am UTC
Location: The Netherlands

[C++] Global variables in headers.

Postby Diadem » Fri Nov 29, 2013 11:12 am UTC

I seem to have an issue in c++ that I am not quite sure how to fix.

I have a code made up of many translation units. I have a shared header file that is included in most of these translation units. I am trying to define a global variable in this header, but I can not find a way to do this.

just declaring 'int x' doesn't work - each translating unit gets its own definition of x, and the linker will throw an error over conflicting definitions.
Declaring 'static int x' doesn't work either. Now the linker won't complain, but each translation unit gets its own x, and thus the whole point of the global variable is lost.
Declaring 'extern int x' doesn't work either. The linker will complain about an unresolved external. Declaring a variable external only works if you declare it somewhere else.
At first I thought I could solve this issue by making x a static member of some class, but you still have to initialize x somewhere, and then you run into the same problem as before.

I've been searching the internet for a solution, but most people just say "declare it extern and then define it once somewhere else in your program". This is not a good solution in my case, since the program I'm working on is a dll, and thus has no fixed entry point. I'd have to define it in multiple locations leading to conflicts. Besides, as solutions go it's ugly as fuck.

It's not that I can't think of a solution to my problem. I can think of several. But I can't think of one that is simple and clean. All of them seem to require extensive modifications to the base code, which defeats the entire purpose of what I'm trying to do.

So I was wondering if anyone here had any suggestions.
It's one of those irregular verbs, isn't it? I have an independent mind, you are an eccentric, he is round the twist
- Bernard Woolley in Yes, Prime Minister

Ubik
Posts: 1016
Joined: Thu Oct 18, 2007 3:43 pm UTC

Re: [C++] Global variables in headers.

Postby Ubik » Fri Nov 29, 2013 11:35 am UTC

It's the extern way, because the header just tells that a variable by the given name exists. You don't need to define it in all of the entry point files, just in one (any) translation unit. I might be wrong about the last part, because I've not done stuff with DLLs, but I don't think the language would work well at all with DLLs otherwise.

User avatar
elliptic
Posts: 34
Joined: Tue Aug 14, 2012 2:21 pm UTC
Location: UK

Re: [C++] Global variables in headers.

Postby elliptic » Fri Nov 29, 2013 2:44 pm UTC

What Ubik said. A header file is not a place for defining stuff, it's for declaring stuff.

Pick a source file and define your variable there at file scope. Declare it as "extern" in the common header (or individually in the other source files).

It doesn't matter that there's no single entry point as your global is a static * variable hence initialised at startup before any of your code gets called. The library loader will take care of this, just as the program loader would if you were building an executable.

* static in the sense of "persistent" not "local"

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: [C++] Global variables in headers.

Postby EvanED » Fri Nov 29, 2013 6:00 pm UTC

diadem wrote:Besides, as solutions go it's ugly as fuck.
... uh, why? Is defining a function in one .cpp file and putting just a prototype in a header ugly? Why would it be for variables? (Okay, I agree a little bit... but still.)

elliptic wrote:It doesn't matter that there's no single entry point as your global is a static variable hence initialised at startup before any of your code gets called. The library loader will take care of this, just as the program loader would if you were building an executable.
Yup. But think "DLL load time" instead of "startup" to cover the more general case of DLLs that are loaded after the main program (via LoadLibrary or dlopen).

User avatar
Diadem
Posts: 5654
Joined: Wed Jun 11, 2008 11:03 am UTC
Location: The Netherlands

Re: [C++] Global variables in headers.

Postby Diadem » Tue Dec 03, 2013 8:54 am UTC

I guess I was a bit too concise with my remark about not having a single entry point. The problem is more that the code can be compiled as a dll, but also as a stand-alone exe (mainly for testing purposes). They both use 90% the same code, but upper 10% is different.

So variable definition, if it can't be done in a header, will have to be done either in both the dll and the exe parts, or within the kernel. Both solutions are ugly: Having the same code twice is ugly (and may lead to problems further down the line), and putting it inside the kernel is ... just wrong.

The third solution, and the one I'm leaning to now, is to put all the logging code (that's what I was trying to define a global variable for in the first place) in a separate module. It's the cleanest solution. But having an entire module with just 1 cpp file, and that cpp file with just 1 line of code, is still very ugly. I guess that one line may grow if I start doing more advanced stuff, but still.

The main issue I have is that I don't see why this problem exists in the first place. It seems a weird language limitation. I can think of no reason why defining things in a header would be effectively illegal. Surely it should be possible for the linker to supply a missing definition of a variable that's only declared, all the necessary information is there. So why doesn't it? There seem to be many use cases where this ability would come in very handy.
It's one of those irregular verbs, isn't it? I have an independent mind, you are an eccentric, he is round the twist
- Bernard Woolley in Yes, Prime Minister

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: [C++] Global variables in headers.

Postby korona » Tue Dec 03, 2013 9:18 am UTC

IIRC GCC allows what you want to do using __attribute__ ((common)). But that is not portable so you don't want to do that. Using common linking for global variables was standard in C but there is no (beautiful) portable way to do that in C++.

Creating a separate folder with only one cpp and one hpp file sounds like over engineering to me. Sometimes it is better to accept some short ugly lines than to invent a more complicated mechanism to achieve the same thing.

User avatar
Diadem
Posts: 5654
Joined: Wed Jun 11, 2008 11:03 am UTC
Location: The Netherlands

Re: [C++] Global variables in headers.

Postby Diadem » Tue Dec 03, 2013 9:33 am UTC

Huh, thanks korono. It seems you can do this in both gcc and visual studio with "__declspec( selectany )". We're not planning on migrating from visual studio anytime soon, so I guess this is an acceptable solution.
It's one of those irregular verbs, isn't it? I have an independent mind, you are an eccentric, he is round the twist
- Bernard Woolley in Yes, Prime Minister

User avatar
elliptic
Posts: 34
Joined: Tue Aug 14, 2012 2:21 pm UTC
Location: UK

Re: [C++] Global variables in headers.

Postby elliptic » Tue Dec 03, 2013 2:51 pm UTC

Diadem wrote:The main issue I have is that I don't see why this problem exists in the first place. It seems a weird language limitation. I can think of no reason why defining things in a header would be effectively illegal.


Because a C/C++ header isn't processed independently in the build, it's just an easy way to copy-paste a bunch of boilerplate into your source files before they get compiled.

Placing a definition in one isn't illegal per se but when the result is another definition for the same object in every source file where you #include the header, that's what's illegal.

User avatar
Diadem
Posts: 5654
Joined: Wed Jun 11, 2008 11:03 am UTC
Location: The Netherlands

Re: [C++] Global variables in headers.

Postby Diadem » Tue Dec 03, 2013 4:36 pm UTC

elliptic wrote:
Diadem wrote:The main issue I have is that I don't see why this problem exists in the first place. It seems a weird language limitation. I can think of no reason why defining things in a header would be effectively illegal.


Because a C/C++ header isn't processed independently in the build, it's just an easy way to copy-paste a bunch of boilerplate into your source files before they get compiled.

Placing a definition in one isn't illegal per se but when the result is another definition for the same object in every source file where you #include the header, that's what's illegal.

Yes, I know what a translation unit is, thank you very much. I was of course asking why having the same variable defined multiple times in different translation units is illegal. Because it doesn't have to be. There's even a compiler flag that makes it legal, as it turns out. So why is that not the default?
It's one of those irregular verbs, isn't it? I have an independent mind, you are an eccentric, he is round the twist
- Bernard Woolley in Yes, Prime Minister

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: [C++] Global variables in headers.

Postby EvanED » Tue Dec 03, 2013 6:11 pm UTC

Because history?

I don't know exactly, though I could speculate and come up with some somewhat-plausible explanations. But basically, historically the linker was extremely dumb, and in addition got very little information from the compiler. So early linkers didn't do the merging, and it's not enough of a problem for people to think is worth fixing.

Personally, I think the idea of splitting the code into its own module sounds fine. It might be a little silly, but I don't think it's bad at all (at least in comparison to the language as a whole).

heatsink
Posts: 86
Joined: Fri Jun 30, 2006 8:58 am UTC

Re: [C++] Global variables in headers.

Postby heatsink » Tue Dec 03, 2013 7:10 pm UTC

I've created a separate dynamic library just for a handful of logging-related variables and functions before. The extra build steps are a hassle, but otherwise I think it's a reasonably simple solution.

It sounds like you want weak symbols, which ELF and Mach-O formats support. If there are multiple definitions of a weak symbol, the linker will arbitrarily choose one. I don't know of a portable way to make weak symbols in C. In GCC, it's __attribute__((weak)).

User avatar
Diadem
Posts: 5654
Joined: Wed Jun 11, 2008 11:03 am UTC
Location: The Netherlands

Re: [C++] Global variables in headers.

Postby Diadem » Sun Dec 08, 2013 1:38 pm UTC

As it turns out the __declspec (selectany) option works quite well.

I now have a completely self-initializing logging header. Just include it and start logging, no initialization required at all. I'm rather happy with that.
It's one of those irregular verbs, isn't it? I have an independent mind, you are an eccentric, he is round the twist
- Bernard Woolley in Yes, Prime Minister

lgw
Posts: 437
Joined: Mon Apr 12, 2010 10:52 pm UTC

Re: [C++] Global variables in headers.

Postby lgw » Mon Dec 16, 2013 11:02 pm UTC

Diadem wrote:As it turns out the __declspec (selectany) option works quite well.

I now have a completely self-initializing logging header. Just include it and start logging, no initialization required at all. I'm rather happy with that.


Glad it's working for you - selectany is a cool feature IMO. BTW, the usual solution I've come to expect from my career is a dedicated logging module of some sort (in it's own DLL or part of some common utils DLL), as there's often a bunch of logic and storage needed there for larger projects (once you're coping with async logging from many threads with dynamic log detail settings, you might as well have a module for it).
"In no set of physics laws do you get two cats." - doogly

User avatar
Toeofdoom
The (Male) Skeleton Guitarist
Posts: 3446
Joined: Wed Jan 10, 2007 10:06 am UTC
Location: Melbourne, Australia
Contact:

Re: [C++] Global variables in headers.

Postby Toeofdoom » Thu Dec 19, 2013 1:18 pm UTC

I believe it also works to do something like this:

Code: Select all

inline Foo& MyVariable()
{
    static Foo foo = Foo();
    return foo;
}

in this case it won't be initialised on startup but the first time the function runs, but with a C++11 compliant compiler it is thread safe.

I have no idea how that interacts with DLLs, though.
Hawknc wrote:Gotta love our political choices here - you can pick the unionised socially conservative party, or the free-market even more socially conservative party. Oh who to vote for…I don't know, I think I'll just flip a coin and hope it explodes and kills me.

Website


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 8 guests