Pregunta

Habiendo implementado CLogClass para hacer un registro decente, también definí macro, pero funciona solo con un parámetro ...

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

Bueno, falla cuando se llama con más de 2 parámetros :( ... ¿Es posible evitarlo? ¿Se puede traducir a plantillas de alguna manera?

EDITAR: se introdujeron macros variables en VS 2005 (pero actualmente estoy en VS 2003 ...). ¿Algún consejo?

¿Fue útil?

Solución

Podría tener una macro MYLOG que devuelva un objeto functor personalizado que tome un número variable de argumentos.

#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;
}

Tenga en cuenta que no es tan difícil pasar a la variante de tipo seguro tocada por esta respuesta : no necesita ningún argumento variado debido al efecto de encadenamiento de 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;
}

Otros consejos

Sus preguntas en realidad apelan a dos respuestas. Desea realizar la función de registro universal, que funciona como printf pero se puede personalizar completamente. Entonces necesitas:

  • macro tomando un número variable de argumentos
  • función que toma un número variable de argumentos

Aquí está su ejemplo de código adaptado:

#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);
}

Se han introducido macros variables en C99, por lo que funcionará en compiladores compatibles con C99 o C ++ 0x. Lo probé con éxito con gcc 3.4.2 y Visual Studio 2005.

Los argumentos variables a las funciones han estado allí para siempre, así que no se preocupe por la compatibilidad aquí.

Probablemente sea posible hacerlo con alguna metaprogramación de plantillas, pero no veo el interés debido a la simplicidad del código anterior.

Como última nota, ¿por qué usar un método estático en una clase vacía en lugar de una función?

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);
        }
};

Se puede extender trivialmente para escribir marcas de tiempo, verificar el nivel de registro, escribir en el archivo, etc.

O, de manera más simple pero menos flexible:

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();
        }
};

Uso:

Log log;
void f() {
     log << "hey, my age is ", age;
}

Tendería a utilizar una función externa visible globalmente en lugar de una macro en este caso, y resolvería los puntos suspensivos en esta función utilizando una va_list. Vea mi publicación anterior para obtener un ejemplo sobre cómo lograr esto .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top