views:

288

answers:

2

I'm working on getting rLog to build as a DLL under windows, and I was getting undefined symbol errors relating to some global symbols in the rlog namespace. Specifically these in RLogChannel.cpp:

namespace rlog {
...
  RLogChannel *_RLDebugChannel   = GetGlobalChannel( "debug",   Log_Debug );
  RLogChannel *_RLInfoChannel    = GetGlobalChannel( "info",    Log_Info );
  RLogChannel *_RLWarningChannel = GetGlobalChannel( "warning", Log_Warning );
  RLogChannel *_RLErrorChannel   = GetGlobalChannel( "error",   Log_Error );
...
 };

I assumed the problem was that 1) they weren't exported and 2) they weren't declared in the header so other things can access them. So I added a __declspec(dllexport) (via the RLOG_DECL macro) to them, and in the header, put:

namespace rlog {
...
  RLOG_DECL extern RLogChannel *_RLDebugChannel;
  RLOG_DECL extern RLogChannel *_RLInfoChannel;
  RLOG_DECL extern RLogChannel *_RLWarningChannel;
  RLOG_DECL extern RLogChannel *_RLErrorChannel;
...
};

But no matter how I declare the variables in RLogChannel.cpp I get a redefinition error, despite my externing them in the header... What's the right way to do this? Seems like it should be straightforward but I can't quite wrap my head around it.

Edit: error message

  Error 12  error C2086: 'rlog::RLogChannel *rlog::_RLDebugChannel' : redefinition  rlog-1.4\rlog\RLogChannel.cpp   45  rlog

(same for all 4 symbols)

Edit: I don't know what happened, the code is exactly the same before but now it will compile (feels like MSVC weirdness...), unfortunately the symbols still show up as unresolved when linking into my library

+1  A: 

One way to get around this problem is to define them once in one place and in the header. But if you just shift all the definitions to the header you'll end up with the multiple definition problem.

A solution to that is this. Assuming the files are named rlog.c & rlog.h

--- (rlog.h) ---
#ifdef RLOG_DEFINES
#define EXTERN
#else
#define EXTERN extern
#endif


namespace rlog {
...
  RLOG_DECL EXTERN RLogChannel *_RLDebugChannel;
  RLOG_DECL EXTERN RLogChannel *_RLInfoChannel;
  RLOG_DECL EXTERN RLogChannel *_RLWarningChannel;
  RLOG_DECL EXTERN RLogChannel *_RLErrorChannel;
...
};


--- (rlog.c) ---
#define RLOG_DEFINES
#include "rlog.h"

...

--- (other .c files) ---
#include "rlog.h"

The beauty of this solution is that because the definitions are only defined once in the project, you won't ever get them out of sync with each other and you only need to change them in one place. Imagine if you defined a variable as a long but in the extern definition it was declared as a short? you may end up with unexpected side effects. So doing it this way helps prevent these types of issues.

Hope that helps.

Matt H
He already said he defined them once in the cpp file and declared them in the header.
Potatoswatter
I still get multiple definition errors when I do that unfortunately...
gct
The normal solution of defining them in the .c file also means they are only ever defined once in the project. Including the header from that file (with the declarations) guarantees you don't have accidental mismatches as the compiler will give errors if you do. In short, I see no advantage in the code you give, but it has the disadvantages of being more complicated and not following normal convention.
Roger Pate
@gct - check your makefile and linker flags... maybe you're linking something twice?
Matt H
I don't know what the problem is, I eventually said screw it and went to a static linkage
gct
+1  A: 

Hi, I think Matt is quite close. I had faced that issue some time a go and the correct (and most portable solutions is this:

--- (rlog.h) ---
#ifdef RLOG_DEFINES
#define RLOG_DECL __declspec(dllexport)
#else
#define RLOG_DECL __declspec(dllimport)
#endif


namespace rlog {
...
  RLOG_DECL extern RLogChannel *_RLDebugChannel;
  RLOG_DECL extern RLogChannel *_RLInfoChannel;
  RLOG_DECL extern RLogChannel *_RLWarningChannel;
  RLOG_DECL extern RLogChannel *_RLErrorChannel;
...
};


--- (rlog.c) ---
#define RLOG_DEFINES
#include "rlog.h"

namespace rlog {
...
  __declspec(dllexport) RLogChannel *_RLDebugChannel   = GetGlobalChannel( "debug",   Log_Debug );
  __declspec(dllexport) RLogChannel *_RLInfoChannel    = GetGlobalChannel( "info",    Log_Info );
  __declspec(dllexport) RLogChannel *_RLWarningChannel = GetGlobalChannel( "warning", Log_Warning );
  __declspec(dllexport) RLogChannel *_RLErrorChannel   = GetGlobalChannel( "error",   Log_Error );
...
 };


--- (other .c files) ---
#include "rlog.h"

The rule is simple. When you compile the dll that provides dllexport must be matched in both symbol declaration and definition. On the other hand when the outside world uses your library - it must appear to it as dllimport symbol.

Regards, Maciej Jablonski