Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

easily throw away c++ call completely

Tags:

c++

I'm trying to implement logging which produce no overhead when not needed (i.e. no method call should be performed at all). I want NO overhead because it's low-latency code. I just added #define ENABLE_LOGS to my header class and now it looks like that (you can ignore details)

#pragma once

#include <string>
#include <fstream>

#define ENABLE_LOGS

namespace fastNative {

    class Logger
    {
    public:
        Logger(std::string name_, std::string fileName, bool append = false);
        ~Logger(void);
        void Error(std::string message, ...);
        void Debug(std::string message, ...);
        void DebugConsole(std::string message, ...);
        void Flush();
        static Logger errorsLogger;
        static Logger infoLogger;
    private:
        FILE* logFile;
        bool debugEnabled;
    };

}

Every time I need to use some method I should surround it like that:

#ifdef ENABLE_LOGS
    logger.Debug("seq=%6d len=%4d", seq, length_);
#endif

It's error-phrone (i can forgot to surround) and makes code dirty. Can I fix my code somehow not to use #ifdef every time?

In C# I like Conditional I guess I need something like that for c++.

like image 289
Oleg Vazhnev Avatar asked Jul 15 '13 21:07

Oleg Vazhnev


3 Answers

First of all it would make sense to have a look to see what's out there already. This is a common problem and many people will have solved it before. E.g., see stackoverflow question C++ logging framework suggestions, and Dr Dobbs A Highly Configurable Logging Framework In C++.

If you do roll your own, you should get some good ideas from having done this. There are several approaches I've used in the past. One is to make the statement itself conditionally defined

#ifdef ENABLE_LOGS
#define LOG(a,b,c) logger.Debug(a, b, c)
#else
#define LOG(a,b,c)
#endif

Another approach is to conditionally define the logging class itself. The non-logging version has everything as empty statements, and you rely on the compiler optimizing everything out.

#ifdef ENABLE_LOGS

class Logger
{
public:
    Logger(std::string name_, std::string fileName, bool append = false);
    ~Logger(void);
    void Error(std::string message, ...);
    void Debug(std::string message, ...);
    void DebugConsole(std::string message, ...);
    void Flush();
    static Logger errorsLogger;
    static Logger infoLogger;
private:
    FILE* logFile;
    bool debugEnabled;
};

#else

class Logger
{
public:
    Logger(std::string name_, std::string fileName, bool append = false) {}
    ~Logger(void) {}
    void Error(std::string message, ...) {}
    void Debug(std::string message, ...) {}
    void DebugConsole(std::string message, ...) {}
    void Flush() {}
};

#endif

You could put your Logger implementation for ENABLE_LOGS in a cpp file under control of the macro. One issue with this approach is that you would want to be sure to define the interface so the compiler could optimize everything out. So, e.g., use a C-string parameter type (const char*). In any case const std::string& is preferable to std::string (the latter ensures there's a string copy every time there's a call).

Finally if you go for the first approach, you should encapsulate everything in do() { ... } while(0) in order to ensure that you don't get bizarre behavior when you use your macro where a compound statement might be expected.

like image 104
TooTone Avatar answered Nov 02 '22 08:11

TooTone


There is one way (the way llvm does) to do this using macros.

#ifdef ENABLE_LOGS
#define DEBUG(ARG) do { ARG; } while(0)
#else
#define DEBUG(ARG)
#endif

Then use it as:

DEBUG(logger.Debug("seq=%6d len=%4d", seq, length_););
like image 7
A. K. Avatar answered Nov 02 '22 08:11

A. K.


What I often see, is to use the #define to actually define the log calls, eg:

#define LOG_DEBUG(msg) logger.Debug(msg);

But you want to wrap the defines in a block that enables or disables your logging:

#ifdef ENABLE_LOGS
#define LOG_DEBUG(msg) logger.Debug(msg);
#else
#define LOG_DEBUG(msg)
#endif

You can call LOG_DEBUG anywhere in your code. If the logging is disabled, calling LOG_DEBUG ends up as a blank line in your final code.

like image 3
Lochemage Avatar answered Nov 02 '22 08:11

Lochemage