1

How can I define an integer in a header file so that each cpp file which includes the header will have static const int id=0 while giving the ability to cpps to redefine it with other value.

I tried to used weak symbol but couldn't make it work.

jackhab
  • 17,128
  • 37
  • 99
  • 136
  • 2
    You want one global integer that all the files can change, or you want each file to have its own integer that it can change and will not affect anyone else? If the latter, why do you want it in a header file at all? – Basya Aug 20 '17 at 15:12
  • You can check [this answer](https://stackoverflow.com/questions/45710667/defining-global-constants-in-c1z/45710882#45710882). – user7860670 Aug 20 '17 at 15:12
  • 1
    Placing a `static` field inside a header will make it static to the compilation unit, header file is no magic, it's just pasted into a .c file. You could, for example, `#define` a `DEFAULT_ID` *before* including the header. But I have a feeling you are doing something terribly wrong (see [XY problem](https://meta.stackexchange.com/a/66378/130298)). Is this really a single `int` field? It's common to include a header and then have a struct (or `int`, or whatever) instance either as a static (per compilation unit), or malloc'ed somewhere. Seeing this inside a header would seem very weird to me. – vgru Aug 20 '17 at 15:17
  • 3
    If you could explain what you are trying to accomplish, we might be able to suggest other ways to accomplish it, since what you are describing does not sound ideal (or maybe then we will understand better what you are really trying to do here). Also, please explain "couldn't make it work". What isn't working? – Basya Aug 20 '17 at 15:21
  • I need a module ID for logger. Each file can have its own but I don't want to make it mandatory i. e. if you didn't defined your ID, then the default value will be used. – jackhab Aug 20 '17 at 19:23
  • How does your logger work? Is it a singleton? Does each module have an instance of the logger class? Is the logger id passed as a parameter in each module's init? Also, is each module defining a class? The most likely way to handle this would be with a class variable. – Basya Aug 20 '17 at 19:56
  • 1
    What about `__FILE__`? – Ripi2 Aug 20 '17 at 19:57
  • @Ripi2 that is a cool idea if he can use a string. It looks like he wants an integer.... – Basya Aug 20 '17 at 19:59
  • @Ripi2 I check a lookup table with module ID on each log write. Comparing strings would be quite heavy operation. – jackhab Aug 21 '17 at 03:46

2 Answers2

1

If you are ok with preprocessor definitions you could do this:

// header.h
#ifndef CLASSID
#define CLASSID 0
#endif
static int id=CLASSID;

// class.cpp
#define CLASSID 1
#include "header.h"

This way a source file may override the default, but may also omit it, which is the sort of weak approach you mentioned.

yacc
  • 2,915
  • 4
  • 19
  • 33
  • Yep it works but it has a small drawback. Either I have to define it before include which looks weird or I need to undefined it first to prevent warnings which is less user friendly. – jackhab Aug 20 '17 at 19:26
0

Here's another solution that uses static variables:

// log.h
#ifndef LOG_H
#define LOG_H
#include <iostream>
#define SETLOGID(v) static logidsetter _logidsetter(_logid, v);
#define LOG(v) std::cout << "id: " << _logid << ": " << (v) << std::endl;

class logidsetter
{
public:
    logidsetter(int &id, int val)
    {
        id = val;
    }
};
static int _logid = 0;
#endif

// myclass.h
class myclass
{
    public:
    myclass();
    void run(void);
};

// myclass.cpp
#include "log.h"
#include "myclass.h"

SETLOGID(42)

myclass::myclass()
{
    LOG("myclass::cons");
}

void myclass::run(void)
{
    LOG("myclass::run");
}

// main.cpp
#include "myclass.h"
#include "log.h"

SETLOGID(1)

int main()
{
    myclass mc;
    LOG("here's main");
    mc.run();
}

The log header defines the static int _logid and provides the macro SETLOGID and the class idsetter. The cpp file may use SETLOGID to redefine the static value. This is done with an instantiation of the class idsetter along with the address of _logid and the desired value. The trick allows to bypass C++'s One Definition Rule.

The output looks like:

id: 42: myclass::cons
id: 1: here's main
id: 42: myclass::run
yacc
  • 2,915
  • 4
  • 19
  • 33
  • This does not compile because idcontainer::id will be defined multiple times in every file that includes the header. – jackhab Aug 23 '17 at 10:43
  • @jackhab I wrote this in a hurry yesterday and didn't consider that use case. But you're right, I fixed that. See my revision above. The idea basically stayed the same: Use an instantiation of a setter class to override the default value of a static that holds the id. – yacc Aug 23 '17 at 18:28