Est-ce que des ellipses d’utilisation possibles en macro? Peut-il être converti en modèle?

StackOverflow https://stackoverflow.com/questions/419559

Question

Après avoir implémenté CLogClass pour rendre la journalisation décente, j'ai également défini la macro, mais cela ne fonctionne qu'avec un seul paramètre ...

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

Eh bien, il échoue lorsqu'il est appelé avec plus de 2 paramètres :( ... Est-il possible de l'éviter? Peut-il être traduit en modèle d'une manière ou d'une autre?

EDIT: Les macros variadic ont été introduites dans VS 2005 (mais je suis actuellement dans VS 2003 ...). Des conseils?

Était-ce utile?

La solution

Vous pourriez avoir une macro MYLOG renvoyant un objet foncteur personnalisé qui prend un nombre variable d'arguments.

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

Notez qu'il n'est pas si difficile de passer à la variante sécurisée par le caractère touchée par cette réponse : vous n'avez pas besoin d'arguments variadiques en raison de l'effet d'enchaînement 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;
}

Autres conseils

Vos questions font appel à deux réponses. Vous voulez faire la fonction de journalisation universelle, qui fonctionne comme printf mais peut être entièrement personnalisée. Donc vous avez besoin de:

  • macro prenant un nombre variable d'arguments
  • fonction prenant un nombre variable d'arguments

Voici votre exemple de code adapté:

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

Des macros variadiques ont été introduits dans C99. Cela fonctionnera donc sur les compilateurs prenant en charge C99 ou C ++ 0x. Je l’ai testé avec succès avec gcc 3.4.2 et Visual Studio 2005.

Les arguments variés aux fonctions sont présents depuis toujours, alors ne vous inquiétez pas de la compatibilité ici.

Il est probablement possible de le faire avec un méta-programme de template, mais je ne vois pas l'intérêt de cela vu la simplicité du code ci-dessus.

En dernier lieu, pourquoi utiliser une méthode statique dans une classe vide au lieu d'une fonction?

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

Peut être étendu de manière triviale pour écrire des horodatages, vérifier le niveau de log, écrire dans un fichier, etc.

Ou plus simplement mais avec moins de souplesse:

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

Utilisation:

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

Dans ce cas, j'aurais tendance à utiliser une fonction extern visible globalement plutôt qu'une macro et à résoudre les points de suspension dans cette fonction à l'aide d'une liste va_list. Voir mon article précédent pour un exemple sur la manière de réaliser cela . .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top