Here's a little something I threw together. It works by maintaining a global "stack", which means the number will still increase between function calls. If you're ok with putting in ENABLE_LOG_ERROR
, then it works as desired:
#include <iostream>
#include <string>
// appends a line number to a string
#define APPENDLINE_IMPL(x, l) x##l
#define APPENDLINE_DETAIL(x, l) APPENDLINE_IMPL(x, l)
#define APPENDLINE(x) APPENDLINE_DETAIL(x, __LINE__)
// make a unique log error object using the line number
#define LOG_ERROR(s) LogError APPENDLINE(LOGERROR)(s); (void)0
#define ENABLE_LOG_ERROR LogErrorEnabler LOGERRORENABLER;
// Logs an error
class LogError
{
public:
// log the error, increment the number
LogError(const std::string& error)
{
std::cerr << "Error #" << _errorCount << ": " << error << std::endl;
++_errorCount;
}
// going out of scope, decrement
~LogError(void)
{
--_errorCount;
}
private:
// let the enabler handle the number
friend class LogErrorEnabler;
// current error number (in function)
static unsigned _errorCount;
};
// resets & restores the error count
class LogErrorEnabler
{
public:
// reset but save the error count
LogErrorEnabler(void)
{
_oldErrorCount = LogError::_errorCount;
LogError::_errorCount = 0;
}
// reset error count (function is ending)
~LogErrorEnabler(void)
{
LogError::_errorCount = _oldErrorCount;
}
private:
// old error number
unsigned _oldErrorCount;
};
// would be in LogError.cpp
unsigned LogError::_errorCount = 0;
void foo(void)
{
ENABLE_LOG_ERROR;
LOG_ERROR("Top of foo function");
LOG_ERROR("Middle of foo function");
LOG_ERROR("Bottom of foo function");
}
void bar(void)
{
ENABLE_LOG_ERROR;
LOG_ERROR("Top of bar function");
foo();
LOG_ERROR("Bottom of bar function");
}
int main(void)
{
bar();
return 0;
}
Edit
The following code works as intended, but without the ENABLE_LOG_ERROR
requirement. :)
It works by letting the first LogErrorEnabler
do it's job, but if any are instantiated after the initial Enabler is created, they are put into a zombie state and do nothing.
The only trick was to detect when an Enabler was being created in a new scope, with an Enabler already active in the previous scope. This was done using the (non-standard but soon to be, so is supported) __FUNCTION__
macro. This macro is passed into the Enabler, upon where it can check if it's being created in a different function than the original Enabler.
Here is the code:
#include <iostream>
#include <string>
// appends a line number to a string
#define APPENDLINE_IMPL(x, l) x##l
#define APPENDLINE_DETAIL(x, l) APPENDLINE_IMPL(x, l)
#define APPENDLINE(x) APPENDLINE_DETAIL(x, __LINE__)
// make a unique log error object using the line number
#define LOG_ERROR(s) LogErrorEnabler APPENDLINE(LOGERRORENABLER)(__FUNCTION__); \
LogError APPENDLINE(LOGERROR)(s); \
(void)0
// Logs an error
class LogError
{
public:
// log the error, increment the number
LogError(const std::string& error)
{
std::cerr << "Error #" << _errorCount << ": " << error << std::endl;
++_errorCount;
}
// going out of scope, decrement
~LogError(void)
{
--_errorCount;
}
private:
// let the enabler handle the number
friend class LogErrorEnabler;
// current error number (in function)
static unsigned _errorCount;
};
// resets & restores the error count
class LogErrorEnabler
{
public:
// reset but save the error count
LogErrorEnabler(const std::string& functionName)
{
if (functionName != _currentFunctionName)
{
// entered new function
_oldErrorCount = LogError::_errorCount;
LogError::_errorCount = 0;
// store function name
_oldFunctionName = _currentFunctionName;
_currentFunctionName = functionName;
// this enabler is active
_zombie = false;
}
else
{
// make this enabler do nothing
_zombie = true;
}
}
// reset error count (function is ending)
~LogErrorEnabler(void)
{
if (!_zombie)
{
// restore old state
LogError::_errorCount = _oldErrorCount;
_currentFunctionName = _oldFunctionName;
}
}
private:
// old error number
unsigned _oldErrorCount;
// old function name
std::string _oldFunctionName;
// zombie state
bool _zombie;
// current function
static std::string _currentFunctionName;
};
// would be in LogError.cpp
unsigned LogError::_errorCount = 0;
// would be in LogErrorEnabler.cpp
std::string LogErrorEnabler::_currentFunctionName = "";
void foo(void)
{
LOG_ERROR("Top of foo function");
LOG_ERROR("Middle of foo function");
LOG_ERROR("Bottom of foo function");
}
void bar(void)
{
LOG_ERROR("Top of bar function");
foo();
LOG_ERROR("Bottom of bar function");
}
int main(void)
{
bar();
return 0;
}
Let me know if there are more modifications you require. :)