Как правильно использовать extern в пространстве имен? - PullRequest
3 голосов
/ 12 февраля 2010

Я работаю над созданием rLog для сборки в виде DLL под Windows, и я получаю неопределенные ошибки символов, связанные с некоторыми глобальными символами в пространстве имен rlog. В частности, это в 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 );
...
 };

Я предположил, что проблема заключалась в том, что 1) они не были экспортированы и 2) они не были объявлены в заголовке, чтобы другие вещи могли получить к ним доступ. Поэтому я добавил к ним __declspec (dllexport) (через макрос RLOG_DECL) и в заголовке поставил:

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

Но как бы я ни объявлял переменные в RLogChannel.cpp, я получаю ошибку переопределения, несмотря на то, что они меняются в заголовке ... Какой правильный способ сделать это? Кажется, это должно быть просто, но я не могу обернуть голову вокруг него.

Редактировать: сообщение об ошибке

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

(одинаково для всех 4 символов)

Редактировать: я не знаю, что произошло, код точно такой же, но сейчас он будет компилироваться (похоже на странность MSVC ...), к сожалению, символы все еще отображаются как неразрешенные при соединении с моей библиотекой

Ответы [ 2 ]

1 голос
/ 27 июня 2010

Я думаю, что Мэтт довольно близко. Я столкнулся с этой проблемой некоторое время, и правильные (и большинство портативных решений это:

--- (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"

Правило простое. Когда вы компилируете dll, которая предоставляет dllexport, должны совпадать как в объявлении, так и в определении символа. С другой стороны, когда внешний мир использует вашу библиотеку - он должен выглядеть как символ dllimport.

С уважением, Мацей Яблонский

1 голос
/ 12 февраля 2010

Один из способов обойти эту проблему - определить их один раз в одном месте и в заголовке. Но если вы просто перенесете все определения в заголовок, вы столкнетесь с проблемой множественных определений.

Решение этого заключается в следующем. Предполагая, что файлы называются 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"

Прелесть этого решения в том, что, поскольку определения определены в проекте только один раз, вы никогда не будете синхронизировать их друг с другом, и вам нужно всего лишь изменить их в одном месте. Представьте, если вы определили переменную как длинную, но во внешнем определении она была объявлена ​​как короткая? Вы можете получить неожиданные побочные эффекты. Подобные действия помогают предотвратить подобные проблемы.

Надеюсь, это поможет.

...