выходной класс условной отладки с шаблонным оператором < - PullRequest
3 голосов
/ 08 сентября 2010

Я пытаюсь создать простой qDebug-подобный класс, который я могу использовать для вывода отладочных сообщений в режиме отладки, в зависимости от некоторого уровня отладки, передаваемого при вызове приложения. Мне понравилась простота использования класса QDebug (который можно использовать как std :: cerr и исчезнет при компиляции в режиме выпуска). У меня есть это до сих пор:

#ifdef DEBUG
    static int CAKE_DebugLevel;

    class Debug
    {
        Debug( int level ) : m_output( level <= CAKE_DebugLevel ) {}

        template<typename T>
        Debug& operator<<( T )
        {
            if( m_output )
            {
                std::cout << T;
                return *this;
            }
            else
                return *this;
        }
    private:
        bool m_output;
    };
#else // release mode compile
    #define Debug nullstream
#endif // DEBUG

Я думаю, что nullstream -то лучше всего подойдет для определения режима релиза:

class nullstream{};

template <typename T>
nullstream& operator<<(nullstream& ns, T)
{
    return ns;
}

В основном у меня на данный момент:

#include "Debug.h"

#include <iostream>

int main()
{
    Debug(0) << "Running debug version of CAKE." << std::endl;
}

Что приводит к следующей ошибке с gcc 4.5.1:

В функции-члене 'Debug & CAKE_Debug :: operator << (T)': ожидаемое первичное выражение перед ';' токен (указывает на линию <code>std::cout << T;)

В функции 'int main (int, char **, char **)': ожидаемый безусловный идентификатор перед токеном << << * </p>

Я уверен, что это что-то простое, но вся информация о шаблонах, которую я нашел, ничего не дает. Спасибо за любую помощь!

Если вам нужна дополнительная информация, пожалуйста, спросите.

Ответы [ 7 ]

6 голосов
/ 08 сентября 2010

Ваш класс nullstream не имеет интегрального конструктора.Это означает, что когда вы #define Debug nullstream, компилятор не может распознать Debug (0) - это не имеет смысла.Debug не является макросом, который принимает аргументы, и если вы замените его на nullstream, у nullstream не будет конструктора, который принимает аргументы.#define используется таким образом, о, так неправильно.У вас должно быть что-то вроде этого:

#ifdef _DEBUG
static int CAKE_Debuglevel;
#endif

class Debug
{
    Debug( int level ) 
#ifdef _DEBUG
    : m_output( level <= CAKE_DebugLevel ) 
#endif
        {}

    template<typename T>
    Debug& operator<<( T t)
    {
        #ifdef _DEBUG
        if( m_output )
        {
            std::cout << t;
            return *this;
        }
        else
        #endif
            return *this;
    }
private:
#ifdef _DEBUG
    bool m_output;
#endif
};

Теперь ваш класс действительно будет выглядеть и действовать одинаково в любой среде, но выводит только если определен _DEBUG.Я также исправил ошибку, когда вы пытались вывести тип.

5 голосов
/ 08 сентября 2010

Другие указали на ошибку с обычными объектами.

template<typename T> 
Debug& operator<<(T const& value)
{
    if ( m_output )  
    {  
        std::cout << value;  
    }  
    return *this;  
}

Но вам также нужен способ вывода std :: endl и других манипуляторов.Поскольку шаблон выше не будет обрабатывать их правильно.Для этого вам понадобится оператор, который обрабатывает функторы, которые управляют потоками (то есть всеми iomanipators (например, std :: endl)).

// Use a typedef to make the code readable.
// This is a function pointer that takes a stream as input and returns the stream.
// This covers functions like std::endl
typedef std::ostream& (*STRFUNC)(std::ostream&);

D& operator<<(STRFUNC func)  // Inside the class
{
    if ( m_output )  
    {  
        // Apply the function
        func(std::cout);
    }
    // But return the debug object
    return *this;
}

Реализация, которая обрабатывает как нормальные, так и широкие потоки:

#include <iostream>

#if defined(_DEBUG)
#define DEBUG_LOG_TEST_CONDITION        output
#define DEBUG_LOG_DEBUG_TEST_LEVEL(v)   (v) <= DebugCake::DebugLevel
#else
#define DEBUG_LOG_TEST_CONDITION        false
#define DEBUG_LOG_DEBUG_TEST_LEVEL(v)   false
#endif

template<typename C,typename T = std::char_traits<C> >
struct DInfo
{
    typedef std::basic_ostream<C,T>& (*StrFunc)(std::basic_ostream<C,T>&);
    static std::basic_ostream<C,T>& getDefault();
};

struct DebugCake
{
    static int DebugLevel;
};
template<typename C,typename T = std::char_traits<C> >
struct D
{

    D(int level)
        :stream(DInfo<C,T>::getDefault())
        ,output( DEBUG_LOG_DEBUG_TEST_LEVEL(level) )
    {}
    D(int level, std::basic_ostream<C,T>& s)
        :stream(s)
        ,output( DEBUG_LOG_DEBUG_TEST_LEVEL(level) )
    {}

    template<typename V>
    D& operator<<(V const& value)
    {
        // In release this is optimised away as it is always false
        if (DEBUG_LOG_TEST_CONDITION)
        {
            stream << value;
        }
        return *this;
    }

    D& operator<<(typename DInfo<C,T>::StrFunc func)
    {
        // In release this is optimised away as it is always false
        if (DEBUG_LOG_TEST_CONDITION)
        {
            func(stream);
        }
        return *this;
    }
    private:
       std::basic_ostream<C,T>&  stream;
       bool                      output;

};

template<>
std::ostream&  DInfo<char,std::char_traits<char>       >::getDefault()
{return std::cout; }

template<>
std::wostream& DInfo<wchar_t,std::char_traits<wchar_t> >::getDefault()
{return std::wcout; }

typedef D<char>    Debug;
typedef D<wchar_t> WDebug;
int DebugCake::DebugLevel = 4;


int main()
{
    Debug   debug(1);

    debug << "Plop" << std::endl;

    WDebug  debugWide(2);
    debugWide << L"WIDE" << std::endl;
}
2 голосов
/ 08 сентября 2010

Ошибка здесь:

template<typename T>
Debug& operator<<( T )
{
    if ( m_output )
    {
        std::cout << T;
        return *this;
    }
    else
        return *this;
}

Вы не можете вывести тип, вы должны вывести объект. Правильный код:

template<typename T>
Debug& operator<<( T value)
{
    if ( m_output )
    {
        std::cout << value;
    }
    return *this;
}
2 голосов
/ 08 сентября 2010

Вы должны написать что-то вроде

operator<<(const T &t)

и использовать t вместо T (это тип) внутри этого метода.

1 голос
/ 08 сентября 2010
template<typename T>
Debug& operator<<( T )

Эта подпись неверна;у вас есть тип для аргумента <<, но нет имени для него.Затем вы захотите использовать имя переменной вместо T в std::cout << T;

0 голосов
/ 08 сентября 2010

... что приведет к проблеме l / rvalue.Просто следуйте шаблону std::cout, если вам нужен глобальный объект вывода Debug.В противном случае, скажите:

Debug tDbg(tLevel);
tDbg << "Message" << std::endl;
0 голосов
/ 08 сентября 2010

Проблема с использованием cout << T; уже была указана (вам нужно указать экземпляр объекта, тогда как T - это тип).

Когда вы исправите это, вы столкнетесь как минимум с еще одной проблемой: Debug(0) создает временный объект. Чтобы передать это как параметр в nullstream operator<<, ему нужно будет взять nullstream const & вместо nullstream & в качестве параметра.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...