【问题标题】:How to properly use extern in a namespace?如何在命名空间中正确使用 extern?
【发布时间】:2011-01-15 21:39:41
【问题描述】:

我正在努力让 rLog 在 Windows 下构建为 DLL,但我遇到了与 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 怪异......),不幸的是,链接到我的库时符号仍然显示为未解析

【问题讨论】:

  • 确切的错误信息是什么?
  • 在上面更新,所以它的格式比我在这里发布的要好一些。
  • 似乎有些重要的事情被忽略了。您能否提供一个简明完整的测试用例(例如,我可以按原样传递给编译器的一个文件)给出此错误?
  • 关于您的最后一次编辑:它有助于重建所有内容,因为有时旧文件会缓存在 MSVC 中。您将 RLOG_DECL 翻转为指定导入而不是在链接到您的 DLL 的代码中导出,对吗?

标签: c++ dll namespaces extern


【解决方案1】:

解决这个问题的一种方法是在一个地方和标题中定义它们一次。 但是如果你只是将所有定义转移到标题中,你最终会遇到多重定义问题。

解决方案是这样的。假设文件名为 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"

此解决方案的美妙之处在于,由于定义只在项目中定义一次,因此您永远不会让它们彼此不同步,您只需在一个地方更改它们。想象一下,如果您将变量定义为 long 但在 extern 定义中将其声明为 short?你最终可能会产生意想不到的副作用。所以这样做有助于防止这些类型的问题。

希望对您有所帮助。

【讨论】:

  • 他已经说过他在cpp文件中定义了一次并在标题中声明了它们。
  • 在 .c 文件中定义它们的正常解决方案也意味着它们只在项目中定义一次。包含该文件中的标头(带有声明)可确保您不会出现意外的不匹配,因为如果您这样做,编译器会出错。简而言之,我认为您提供的代码没有任何优势,但它的缺点是更复杂且不符合常规。
  • @gct - 检查你的 makefile 和链接器标志...也许你链接了两次?
  • 不知道是什么问题,最后我说完蛋去了静态联动
【解决方案2】:

我认为马特非常接近。我曾经遇到过这个问题并且是正确的(大多数便携式解决方案是这样的:

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

规则很简单。当您编译提供 dllexport 的 dll 时,必须在符号声明和定义中都匹配。另一方面,当外部世界使用您的库时 - 它必须显示为 dllimport 符号。

问候, 马切伊·雅布隆斯基

【讨论】:

    猜你喜欢
    • 2010-09-07
    • 1970-01-01
    • 1970-01-01
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多