代码之家  ›  专栏  ›  技术社区  ›  ollo

未定义对Logger::getInstance()的引用-但仅在某些情况下

  •  1
  • ollo  · 技术社区  · 10 年前

    我使用的是log4cplus(从当前git master编译而来),但链接器出现了未定义的引用错误。但是,会出现这些错误 仅在某些班级 .

    一般来说,每个类都有以下形式:

    收割台(.h)

    // ...
    #include <log4cplus/loggingmacros.h>
    // ...    
    // namespace(s)
    
    class Example
    {
    public:
        // ...
    private:
        // ...
        static const log4cplus::Logger logger;
    };
    

    源(.cpp)

    // includes
    
    // namespace(s)
    
    // implementations
    
    const log4cplus::Logger Example::logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("Example"));
    

    用法

    记录器在类中使用如下:

    LOG4CPLUS_WARN(logger,  "Ha, ha – whatever you try I wont work!");
    

    编译工作时,链接器会引发未定义的引用错误

    log4cplus::Logger::getInstance(std::string const&)
    

    log4cplus::detail::macro_forced_log(log4cplus::Logger const&, int, std::string const&, char const*, int, char const*)
    

    对于某些课程。我已经从工作类中复制了记录器部分:相同的结果。

    用类成员替换静态记录器也不起作用 getInstance() 找不到。

    解决此问题的方法是改用根记录器;这将编译/链接(尽管 getRoot() 是同一类的一部分!?):

    const log4cplus::Logger Example::logger = log4cplus::Logger::getRoot();
    

    但还有未定义的引用错误

    log4cplus::detail::macro_forced_log(...)
    

    为了确保没有错别字,我使用了这些宏进行声明/定义:

    #define LOG_DECL(name)      static const log4cplus::Logger logger
    #define LOG_DEF(name)       const log4cplus::Logger name::logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT(#name))
    

    同样的结果是,那些以前有效的也有效,而那些没有。


    更多信息:

    1. Log4cplus使用-log4cplusSU链接(也使用log4cplusS测试),并从其git master编译
    2. 使用GCC 4.9 C++11
    3. 用于构建项目的Eclipse CDT
    4. Log4cplus在主
    5. 所有类都使用相同的编译器和标志进行编译
    6. 完全清洁和建设项目
    7. 所有文件都以相同的方式编译/链接
    8. 常量/非常量无效

    的结果 nm <NAME>.o | grep -i log4cplus :

    工作对象

                     U _ZN9log4cplus6detail16macro_forced_logERKNS_6LoggerEiRKSbIwSt11char_traitsIwESaIwEEPKciSB_
    0000000000000000 W _ZN9log4cplus6detail17macros_get_loggerERKNS_6LoggerE
                     U _ZN9log4cplus6detail18get_macro_body_ossEv
                     U _ZN9log4cplus6Logger11getInstanceERKSbIwSt11char_traitsIwESaIwEE
                     U _ZN9log4cplus6LoggerC1ERKS0_
                     U _ZN9log4cplus6LoggerD1Ev
    00000000000002c8 r _ZN9log4cplusL13ALL_LOG_LEVELE
    00000000000002ac r _ZN9log4cplusL13OFF_LOG_LEVELE
    00000000000002bc r _ZN9log4cplusL14INFO_LOG_LEVELE
    00000000000002b8 r _ZN9log4cplusL14WARN_LOG_LEVELE
    00000000000002c0 r _ZN9log4cplusL15DEBUG_LOG_LEVELE
    00000000000002b4 r _ZN9log4cplusL15ERROR_LOG_LEVELE
    00000000000002b0 r _ZN9log4cplusL15FATAL_LOG_LEVELE
    00000000000002c4 r _ZN9log4cplusL15TRACE_LOG_LEVELE
    00000000000002cc r _ZN9log4cplusL17NOT_SET_LOG_LEVELE
                     U _ZNK9log4cplus6Logger12isEnabledForEi
    

    对于未定义的引用:

                     U _ZN9log4cplus6detail16macro_forced_logERKNS_6LoggerEiRKSsPKciS7_
    0000000000000000 W _ZN9log4cplus6detail17macros_get_loggerERKNS_6LoggerE
                     U _ZN9log4cplus6detail18get_macro_body_ossEv
                     U _ZN9log4cplus6Logger11getInstanceERKSs
                     U _ZN9log4cplus6LoggerC1ERKS0_
                     U _ZN9log4cplus6LoggerD1Ev
    00000000000001ec r _ZN9log4cplusL13ALL_LOG_LEVELE
    00000000000001d0 r _ZN9log4cplusL13OFF_LOG_LEVELE
    00000000000001e0 r _ZN9log4cplusL14INFO_LOG_LEVELE
    00000000000001dc r _ZN9log4cplusL14WARN_LOG_LEVELE
    00000000000001e4 r _ZN9log4cplusL15DEBUG_LOG_LEVELE
    00000000000001d8 r _ZN9log4cplusL15ERROR_LOG_LEVELE
    00000000000001d4 r _ZN9log4cplusL15FATAL_LOG_LEVELE
    00000000000001e8 r _ZN9log4cplusL15TRACE_LOG_LEVELE
    00000000000001f0 r _ZN9log4cplusL17NOT_SET_LOG_LEVELE
                     U _ZNK9log4cplus6Logger12isEnabledForEi
    

    失败的最小类

    //////////////////////// Header ////////////////////////
    namespace so {
    namespace example {
    
    class FailingExample
    {
    public:
        FailingExample(other_ns::Config* config, other_ns::Command* cmd);
        bool updateData(uint8_t* dataPtr, uint32_t dataSize);
        virtual ~FailingExample();
    
    private:
        static const log4cplus::Logger logger;
    };
    }}
    
    
    //////////////////////// Source ////////////////////////
    namespace so {
    namespace example {
    
    FailingExample::FailingExample(other_ns::Config* config, other_ns::Command* cmd)
    {
    }
    
    bool FailingExample::updateData(uint8_t* dataPtr, uint32_t dataSize)
    {
        return true;
    }
    
    FailingExample::~FailingExample()
    {
    }
    
    // Undefined reference to getInstance() here
    const log4cplus::Logger FailingExample::logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("FailingExample"));
    
    }}
    
    1 回复  |  直到 10 年前
        1
  •  1
  •   ollo    10 年前

    经过一段时间的查找,我找到了原因和解决方案。

    Log4cplus使用 char / std::string wchar / std::wstring ,取决于 UNICODE 是否已定义。

    例如。 LOG4CPLUS_TEXT() 宏处理这些情况时,它将字符串强制转换为正确的字符串类型。

    问题

    尽管log4cplus是使用Unicode编译的(IDE显示了正确的预处理器分支),但链接器未能链接正确的 getInstance() 或其他方法-有时,在某些文件上!

    有时他成功了(使用 std::wstring 版本),然后他失败了;这个 std::字符串 找不到版本,但实际上不应该使用它。

    在没有Unicode支持的情况下编译log4cplus也不起作用(只是类型相反的问题…)

    原因

    项目中使用的第三方库在其标题丛林深处有这样的代码:

    ...
    #ifdef ...
    #define UNICODE
    ...
    

    (注: UNICODE码 未由项目定义)

    所以库定义了 UNICODE码 在头中的某个位置-如果发生这种情况,log4cplus将使用 wstring 方法,否则 string .

    这也解释了为什么有些文件正常工作,有些文件失败-这取决于它包含的头文件(以及这些头文件自己包含的内容…等等…)

    如果 UNICODE码 使用 wstring(字符串) ,如果不使用 一串 -由log4cplus类型、方法和宏使用(如上面的log4cplus_TEXT())。如果这个行为在另一个库的某个深处被某个标志改变了,那就太好了。。。

    解决方案

    我只需要添加一个 -DUNICODE 到编译器标志,它正在工作!Log4cplus始终使用 wchar公司 - / wstring(字符串) -版本现在!

    尽管如此,未来第三方库将被替换,可能会回到非unicode,但这是另一回事。