Возможно ли использовать многоточие в макросе? Можно ли преобразовать в шаблон? - PullRequest
3 голосов
/ 07 января 2009

Реализовав CLogClass для создания достойной регистрации, я также определил макрос, но он работает только с одним параметром ...

class CLogClass
{ 
public:
       static void DoLog(LPCTSTR sMessage, ...);
};
#define DebugLog(sMessage, x) ClogClass::DoLog(__FILE__, __LINE__, sMessage, x)

Что ж, происходит сбой при вызове с более чем 2 параметрами :( ... Можно ли вообще этого избежать? Может ли это быть как-то переведено в шаблоны?

РЕДАКТИРОВАТЬ: Variadic макросы были введены в VS 2005 (но я в настоящее время в VS 2003 ...). Любые советы?

Ответы [ 4 ]

5 голосов
/ 07 января 2009

Вы могли бы иметь макрос MYLOG, возвращающий пользовательский объект функтора, который принимает переменное число аргументов.

#include <string>
#include <cstdarg>

struct CLogObject {

  void operator()( const char* pFormat, ... ) const {
    printf( "[%s:%d] ", filename.c_str(), linenumber );
    va_list args;
    va_start( args, pFormat );
    vfprintf( stderr, pFormat, args );
    va_end( args );
  }

  CLogObject( std::string filename, const int linenumber )
    : filename( filename ), linenumber( linenumber )
  {}
  std::string filename;
  int linenumber;
};

#define MYLOG CLogObject( __FILE__, __LINE__ )


int _tmain(int argc, _TCHAR* argv[])
{

  MYLOG( "%s, %d", "string", 5 );
  return 0;
}

Обратите внимание, что не так сложно перейти к безопасному типу варианту, к которому приводит этот ответ : вам не нужны переменные аргументы из-за эффекта сцепления operator<<.

struct CTSLogObject {

  template< typename T >
  std::ostream& operator<<( const T& t ) const {
    return std::cout << "[" << filename << ":" << linenumber << "] ";
  }

  CTSLogObject( std::string filename, const int linenumber )
    : filename( filename ), linenumber( linenumber )
  {}
  std::string filename;
  int linenumber;
};
#define typesafelog CTSLogObject( __FILE__, __LINE__ )

int _tmain(int argc, _TCHAR* argv[])
{
  typesafelog << "typesafe" << ", " << 5 << std::endl;
  return 0;
}
3 голосов
/ 07 января 2009

Ваши вопросы на самом деле апеллируют к двум ответам. Вы хотите использовать универсальную функцию ведения журнала, которая работает как printf, но может быть полностью настроена. Итак, вам нужно:

  • макрос, принимающий переменное количество аргументов
  • функция, принимающая переменное число аргументов

Вот пример вашего кода:

#include <stdio.h>
#include <stdarg.h>


class CLogClass
{
public:
    static void DoLogWithFileLineInfo( const char * fmt, ... )
    {
        va_list ap;
        va_start( ap, fmt );
        vfprintf( stderr, fmt, ap );
        va_end( ap );
    }

};


#define MYLOG(format, ...) CLogClass::DoLogWithFileLineInfo("%s:%d " format , __FILE__, __LINE__, __VA_ARGS__)

int main()
{
    MYLOG("Hello world!\n", 3); // you need at least format + one argument to your macro
    MYLOG("%s\n", "Hello world!");
    MYLOG("%s %d\n", "Hello world!", 3);
}

Макросы Variadic были введены в C99, поэтому он будет работать на компиляторах, поддерживающих C99 или C ++ 0x. Я успешно протестировал его с gcc 3.4.2 и Visual Studio 2005.

Вариативные аргументы для функций существовали всегда, поэтому не беспокойтесь о совместимости.

Возможно, это возможно сделать с помощью некоторого шаблонного метапрограммирования, но я не вижу интереса к нему, учитывая простоту приведенного выше кода.

В качестве последнего замечания, зачем использовать статический метод в пустом классе вместо функции?

1 голос
/ 07 января 2009
class Log {
    stringstream buffer;
    public:
        class Proxy {
            public:
                Proxy(Log& p) : parent(p) {}
                template<class T>
                Proxy& operator,(T const& t) {
                    parent.buffer << t;
                    return *this;
                }
                ~Proxy() {
                    parent.buffer << endl;
                    cout << parent.buffer.str();
                    parent.buffer.str("");
                }
            private:
                CLog& parent;
        };

        template<class T>
        Proxy operator<<(T const& t) {
            buffer << t;
            return Proxy(*this);
        }
};

Может быть тривиально расширен для записи меток времени, проверки уровня логирования, записи в файл и т. Д.

Или, более просто, но менее гибко:

class Log {
    public:
        class Proxy {
            public:
                template<class T>
                Proxy& operator,(T const& t) {
                    cout << t;
                    return *this;
                }
                ~Proxy() {
                    cout << endl;
                }
        };

        template<class T>
        Proxy operator<<(T const& t) {
            cout << t;
            return Proxy();
        }
};

Использование:

Log log;
void f() {
     log << "hey, my age is ", age;
}
0 голосов
/ 07 января 2009

В этом случае я бы предпочел использовать глобально видимую внешнюю функцию, а не макрос, и разрешить многоточие в этой функции с помощью va_list. Смотрите мой предыдущий пост пример того, как этого добиться .

...