gct
gct

Reputation: 14563

How to properly use extern in a namespace?

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

Upvotes: 3

Views: 6658

Answers (2)

user377178
user377178

Reputation: 2411

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

Upvotes: 1

hookenz
hookenz

Reputation: 38879

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.

Upvotes: 1

Related Questions