Question

I tried to encapsulate using log4cxx in our code by using #define directives and macros. The code compiles but when running I get Access violation, because I believe the stream object is not correctly initialized.

The header file which tries to make log4cxx pluggable looks like this:

#ifdef USE_LOG4CXX
#include "log4cxx/logger.h"

#define LOG_DEBUG(msg) LOG4CXX_DEBUG(logger, msg)
#define LOG_INFO(msg) LOG4CXX_INFO(logger, msg)
#define LOG_WARN(msg) LOG4CXX_WARN(logger, msg)
#define LOG_ERROR(msg) LOG4CXX_ERROR(logger, msg)

#define LOGGER_DECL static const log4cxx::LoggerPtr logger
#define LOGGER_INIT(source,name) const log4cxx::LoggerPtr source::logger = log4cxx::Logger::getLogger(#name); 
#else  // use some other log method, e.g. stdout which worked fine

The .cpp file does the logging like this:

LOG_INFO("state(): " << old_state << " ==> " << new_state );

The preprocessor expanded the .cpp file to:

{ if (logger->isInfoEnabled()) { ::log4cxx::helpers::MessageBuffer oss_; logger->forcedLog(::log4cxx::Level::getInfo(), oss_.str(oss_ << "state(): " << state_ << " ==> " << new_state), ::log4cxx::spi::LocationInfo("c:\\dev\\ezx-capi\\iserver-api\\iserver_client.cpp",  __FUNCSIG__  , 190)); }};

The data type of old_state and new_state are int.

When running, the application fails on:

std::basic_ostream<char>& operator<<(CharMessageBuffer& os, const V& val) {
   return ((std::basic_ostream<char>&) os) << val;
}

In the debugger, the problem looks like the CharMessageBuffer object has a std::basic_streambuf member which is not initialized, and so when it goes to append the value, it dies. (I'm not sure about that explanation though.)

Drilling all the way down, it dies in std::basic_ostream:

_Myt& __CLR_OR_THIS_CALL operator<<(int _Val)
    {   // insert an int
    ios_base::iostate _State = ios_base::goodbit;
    const sentry _Ok(*this);  // dies right here, in the constructor

Anyway, I realize this has something to do with how I am using a macro to invoke LOG4CXX. (When I had the USE_LOG4FXX not defined, all the log statements were going to std::cout and it worked fine.)

UPDATE

One other piece of information - it seems that this is only failing when I invoke the logging from within a static library. If I use the same (or similar) macro from an EXE project, it doesn't fail at all. So I can't seem to replicate this problem in some kind of separate test application.

Was it helpful?

Solution

The problem was caused by the way the enum was interpreted. The compiler invoked the template << operator, instead of the version of the << operator which took an int type. What was even stranger (to me), was that a test app I wrote to see whether the enum data type was the problem worked without issue, i.e.:

ezx::net::client_state::state mystate = ezx::net::client_state::connecting;
LOG_INFO("this should show new state" << mystate);

This did not throw any error, and took a different code path than the same code above.

The conclusion I reached was that the log4cxx implementation of this operator is fragile, in that it will compile fine, but then fail unexpectedly at runtime, depending on whether the datatype is dispatched to the correct version of the operator or not.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top