Reputation: 14563
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
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
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