Search code examples
c++visual-studioc-preprocessorpreprocessor-directive

Can #define preprocessor directive contain if and else?


I was trying the logger code from this link, but it gives me error. How to implement a good debug/logging feature in a project

#ifndef _LOGGER_HPP_
#define _LOGGER_HPP_

#include <iostream>
#include <sstream>

/* consider adding boost thread id since we'll want to know whose writting and
 * won't want to repeat it for every single call */

/* consider adding policy class to allow users to redirect logging to specific
 * files via the command line
 */

enum loglevel_e
    {logERROR, logWARNING, logINFO, logDEBUG, logDEBUG1, logDEBUG2, logDEBUG3, logDEBUG4};

class logIt
{
public:
    logIt(loglevel_e _loglevel = logERROR) {
        _buffer << _loglevel << " :" 
            << std::string(
                _loglevel > logDEBUG 
                ? (_loglevel - logDEBUG) * 4 
                : 1
                , ' ');
    }

    template <typename T>
    logIt & operator<<(T const & value)
    {
        _buffer << value;
        return *this;
    }

    ~logIt()
    {
        _buffer << std::endl;
        // This is atomic according to the POSIX standard
        // http://www.gnu.org/s/libc/manual/html_node/Streams-and-Threads.html
        std::cerr << _buffer.str();
    }

private:
    std::ostringstream _buffer;
};

extern loglevel_e loglevel;

#define log(level) \
if (level > loglevel) ; \
else logIt(level)

#endif

More precisely, this #define is giving errors:

#define log(level) \
if (level > loglevel) ; \
else logIt(level)

The errors are Syntax error: if and Syntax error: else

But later, I noticed that if I move #include "logger.hpp" from main.h to main.cpp, the problem disappeared. Though 'main.h' is included many times in different places, it does contain '#pragma once'.

Any idea?


Solution

  • If loglevel is known at compile time you can do the following:

    template <bool>
    struct LogSystem
    {
        template <class T>
        LogSystem& operator << (const T &)
        {
            //ignore the input
            return (*this);
        }
    };
    
    template <>
    struct LogSystem <true>
    {
        template <class T>
        LogSystem& operator << (const T & v)
        {
            cout << v;
            return (*this);
        }
    };
    
    template <bool B>
    LogSystem<B>    getLog()
    {
        return LogSystem<B>();
    }
    
    #define log(level) getLog< (level <= loglevel) >()
    

    if loglevel is not known at compile time:

    class iLogSystem
    {
    public:
        virtual iLogSystem& operator << (const int &)
        {
            //empty
            return (*this);
        }
        virtual iLogSystem& operator << (const custom_type &);
        {
            return (*this);
        }
        //make functions for logging all the types you want
    };
    
    class LogSystem : public iLogSystem
    {
    public:
        virtual iLogSystem& operator << (const int & v)
        {
            cout << v;
            return (*this);
        }
        virtual iLogSystem& operator << (const custom_type &  q);
        {
            cout << q.toString();
            return (*this);
        }
        //make functions for logging all the types you want
    };
    iLogSystem& getLog(const bool l)
    {
        static LogSystem actual_log;
        static iLogSystem empty_log;
        if(l)
            return &actual_log;
        return &empty_log;
    }
    
    #define log(level) getLog( level <= loglevel )