6

I've a single header requirement on a code, which means there should be no splitting of declarations and definitions into separate header and source files. I've implemented it properly and it works as intended for my use case, where this header file was meant to be included in only single source file.

Now when it comes to it's use in multiple source files(where more than one .cpp includes it), it will fail with linker error along the lines that some variables are being redeclared. That is because I've the code like -

#ifndef HEADER_HPP
#define HEADER_HPP

....

std::streambuf const *R_coutbuf = std::cout.rdbuf();
std::streambuf const *R_cerrbuf = std::cerr.rdbuf();
std::streambuf const *R_clogbuf = std::clog.rdbuf();

void doSomething(){
    [uses if(R_coutbuf) and others]
}

....

#endif HEADER_HPP

Now the best solution would be to declare those vars in header file and define/assign them in single cpp file, but as I said I want to be able to do this with a single header file. Which brings to the problem that if multiple source files will include it, there will be redeclarations.

So far I'm not sure how should I be able to do this but I've two ideas -

#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#else
#define EXTERN extern int
#endif /* DEFINE_VARIABLES */

EXTERN global_variable = something;

I'm not so sure about it, would this even work?

And the second way I thought about is to put it in an anonymous namespace, am trying this one and so far its building successfully -

#ifndef HEADER_HPP
#define HEADER_HPP

....

namespace R {

    namespace {
        std::streambuf const *R_coutbuf = std::cout.rdbuf();
        std::streambuf const *R_cerrbuf = std::cerr.rdbuf();
        std::streambuf const *R_clogbuf = std::clog.rdbuf();
    }
    void doSomething(){
        [uses if(R_coutbuf) and others]
    }
}

....

#endif HEADER_HPP

Is there any other way I could achieve this? Are there any problems with either of the ways I described above.

Abhinav Gauniyal
  • 7,034
  • 7
  • 50
  • 93
  • 1
    If you're not sure whether something "will work", or not, it seems it would be faster to just try it and see, instead of waiting for someone on the intertubes to figure it out for you. – Sam Varshavchik Sep 12 '16 at 13:01
  • 1
    I think the `DEFINE_VARIABLES`/`EXTERN` approach would work as long as you define the 1st macro in __exactly__ one _.cpp_ file, __before__ including _header.hpp_. Note: change your define to `#define EXTERN extern` as you might need variables other than `int`. – CristiFati Sep 12 '16 at 13:01
  • Interesting question but... if you want that only a cpp file include the header file... why don't abolish the header file and write its content in the initial part of the cpp file? – max66 Sep 12 '16 at 13:03
  • If you want to have them "global" anyway in the header file, you can wrap them in a singleton. While we are at it `static` may also help you here. – Hayt Sep 12 '16 at 13:04
  • @SamVarshavchik I'm currently trying out those methods but as I know of cpp, there might be certain shortcomings or bugs with either of the approach that I might not know, so I posted this here :) – Abhinav Gauniyal Sep 12 '16 at 13:10
  • @max66 multiple cpp files can include that header. That's the problem because now every cpp file that includes it contains definitions of those 3 variables, which will lead to linker errors. Hope I managed to explain it this time. – Abhinav Gauniyal Sep 12 '16 at 13:12
  • @Hayt why would I ever use `singleton` here? anyways singleton would already contain `static` member so just using plain `static` var would be better. – Abhinav Gauniyal Sep 12 '16 at 13:13
  • There is a difference between a static member and a static global variable. singleton would you make you only have one instance of the objects in the link-output and you don't need any "guards". When you use static on global variables you have to be aware, that each cpp (compilation unit) has it's own instance. So when you modify one of the variables it will not carry over to another cpp file. – Hayt Sep 12 '16 at 13:16
  • Using preprocessor tricks (e.g. your first approach) will not help as the preprocessing step is completely independent for each translation unit (i.e. .cpp file). The second approach would kinda work, but it will create separate copies of your variables in every .cpp. Which is probably ok in this specific case. – Ap31 Sep 12 '16 at 13:21
  • @Ap31 'kinda work' in the sense that it would create separate copies for each file (which I'm fine with) or are there any other shortcomings too :/ – Abhinav Gauniyal Sep 12 '16 at 13:29
  • These are your only two options. **(1)** You can split declaration and definition, putting the former into the header and the latter into exactly one source file. You could try and achieve that with clever macro tricks, though I don't quite see the point. **(2)** You can give your variables internal linkage (by marking them `static` or putting them into unnamed namespace), which would let you put definitions into the header but would result in every source that includes that header to get its own separate copy of those variables. Choose your poison. – Igor Tandetnik Sep 12 '16 at 14:16
  • Your second example will still produce linker errors for the functions. If you put the functions into the anonymous namespace too, you will get lots of redundant object code, and of course highly surprising behavior if you reassign those variables (since there are multiple copies). If you make the functions inline instead, you violate the ODR and get undefined behavior. Chris Dew's answer is the best choice. – Sebastian Redl Sep 12 '16 at 14:30
  • 2
    Unnamed `namespace` will generate 1 copy per translation unit. i.e. If you declare 1 integer in unnamed, then 10 such integers will be generated for 10 .cpp files. Do you intend that? Better to have internal linkage using function's `static` as suggested in 1 answer. – iammilind Sep 12 '16 at 14:34

2 Answers2

5

You could make your variable a local static variable in a function:

inline std::streambuf const*& R_coutbuf() {
    static std::streambuf const* b = std::cout.rdbuf();
    return b;
}
Chris Drew
  • 14,926
  • 3
  • 34
  • 54
  • this gets instantiated the first time it is called and returns same value each time then? I will have to provide an `init()` function call before to initialize these variables, right? and which must be called once in only 1 cpp or can be called once in my own header? – Abhinav Gauniyal Sep 12 '16 at 15:18
  • 1
    It will get instantiated the first time it is called, yes. I don't see why you need an `init()` function, the initialization is there already. It will not necessarily return the same value each time. I've intentionally returned a reference to the pointer so you can reseat it if you want to. – Chris Drew Sep 12 '16 at 15:20
  • I misunderstood it a little bit, glad to know it is lazy initialization. – Abhinav Gauniyal Sep 19 '16 at 08:33
  • great answer, I wanted to whine a bit about the fact that language doesn't support any kind of "inline variables" for that purpose, and then I found out that c++17 introduces exactly that – Ap31 Sep 22 '16 at 15:34
1

Are there any problems with either of the ways I described above.

Macro trick

  • pros
    • Produces minimal number of variables in object files. See the con of anonymous namespace.
  • cons
    • Very fragile. When including the header, a programmer must know whether any of other existing sources include the variable definitions. This does not scale well when the number of source files increases

Internal linkage (anonymous namespace)

  • pros
    • Doesn't have the disadvantage of the macro trick.
  • cons
    • Each object file that includes the header will have their own copy of the variables. I'm not certain, but the variables are const and all will have the same value at run time, the linker may be able to throw away the duplicates. In any case, the difference is only a few bytes worth for your program. Modifying individual variables as opposed to modifying a shared variable would change the meaning of the program.

For comparison, let us consider separate source file option, which you have ruled out.

  • pros
    • Produces minimal number of variables in object files, like the macro trick.
    • Doesn't have the disadvantage of the macro trick.
  • cons
    • None (unless you consider having a separate source file a con by itself).

Is there any other way I could achieve this?

Not really. You can use static keyword instead of anonymous namespace to declare internal linkage.


PS. If you define any non-template functions in the header, they must be declared inline. Your anonymous namespace example fails to do this.

eerorika
  • 232,697
  • 12
  • 197
  • 326