Question

Question initiale

J'écris une classe de journalisation où l'objectif est de pouvoir le faire:

// thread one
Logger() << "Some string" << std::ios::hex << 45;
// thread two
Logger() << L"Some wide string" << std::endl;

L'en-tête de mon enregistreur ressemble actuellement à ceci:

#pragma once;
#include <ostream>    
class Logger
{
public:
    Logger();
    ~Logger();

    std::ostream* out_stream;
};

template <typename T>
Logger& operator<< (Logger& logger, T thing) {
    *logger.out_stream << thing;
    return logger;
}

Quelques notes sur cette classe:

  1. La compatibilité entre plates-formes n’est pas un problème.
  2. À l'intérieur de Logger.cpp, il existe une classe singleton qui se charge de créer le "réel". ostream.
  3. Le constructeur et le déconstructeur de Logger effectuent le verrouillage nécessaire du singleton.

J'ai trois problèmes:

  • Comment rendre l'opérateur < < fonctionner un ami ou un membre afin que je puisse définir out_stream comme privé?
  • Comment rendre l'opérateur < < fonction de travail pour les manipulateurs?
  • Comment puis-je ajouter une spécialisation pour que si T est un WCHAR * ou std :: wstring, il soit converti en char * ou std :: string avant de le transmettre à out_stream? (Je peux faire la conversion. La perte de caractères unicode élevés n’est pas un problème dans mon cas.)

Résumé des éléments appris dans les réponses:

  • Placez le modèle AVANT ami au lieu d’après.
  • std :: ios :: hex n'est pas un manipulateur. std :: hex est un manipulateur.

Résultat final

#pragma once
#include <ostream>
#include <string>

std::string ConvertWstringToString(std::wstring wstr);

class Logger
{
public:
    Logger();
    ~Logger();

    template <typename T>
    Logger& operator<< (T data) {
        *out << data;
        return *this;
    }
    Logger& operator<< (std::wstring data) {
        return *this << ConvertWstringToString(data);
    }
    Logger& operator<< (const wchar_t* data) {
        std::wstring str(data);
        return *this << str;
    }

private:
    std::ostream* out;
};
Était-ce utile?

La solution

Vous pouvez utiliser la définition d'un ami, qui définira l'opérateur dans l'espace de noms environnant de la classe, et le rendra visible uniquement par la résolution de surcharge de l'opérateur (non appelable manuellement à l'aide de la syntaxe :: operator < < ...): <

class Logger
{
public:
    Logger();
    ~Logger();

    std::ostream* out_stream;

    template <typename T>
    friend Logger& operator<< (Logger& logger, T thing) {
        *logger.out_stream << thing;
        return logger;
    }

    /* special treatment for std::wstring. just overload the operator! No need
     * to specialize it. */
    friend Logger& operator<< (Logger& logger, const std::wstring & wstr) {
        /* do something here */
    }

};

L'alternative consiste à conserver votre code tel qu'il est et à faire en sorte que l'opérateur < < Si vous modélisez un ami, vous ajoutez cette ligne à la définition de votre classe:

template <typename T>
friend Logger& operator<< (Logger& logger, T thing);

Pour le problème du manipulateur, je vais vous donner mon code que j'écris il y a quelque temps:

#include <iostream>
#include <cstdlib>
using namespace std;

template<typename Char, typename Traits = char_traits<Char> >
struct logger{
    typedef std::basic_ostream<Char, Traits> ostream_type;
    typedef ostream_type& (*manip_type)(ostream_type&);
    logger(ostream_type& os):os(os){}
    logger &operator<<(manip_type pfn) {
        if(pfn == static_cast<manip_type>(std::endl)) {
            time_t t = time(0);
            os << " --- " << ctime(&t) << pfn; 
        } else
            os << pfn;
        return *this; 
    }
    template<typename T> 
    logger &operator<<(T const& t) { 
        os << t; 
        return *this;
    }
private:        
    ostream_type & os;
};

namespace { logger<char> clogged(cout); }
int main() { clogged << "something with log functionality" << std::endl; }

};

Notez que c'est std :: hex, mais pas std :: ios :: hex . Ce dernier est utilisé comme indicateur de manipulation pour la fonction setf des flux. Notez que pour votre exemple, aucun traitement spécial des manipulateurs n’est requis. Le traitement spécial ci-dessus de std :: endl est nécessaire uniquement parce que je diffuse le temps en plus lorsque std :: endl est utilisé.

Autres conseils

Utiliser un modèle est la bonne façon de le faire, mais vous devez simplement vous assurer que le modèle se trouve dans le fichier en-tête ( logger.h , ou quoi que vous fassiez). pas dans le fichier d’implémentation ( logger.cpp ). Cela fonctionnera automatiquement pour tous les types pour lesquels opérateur < < est défini avec un std :: ostream . Cela fonctionnera aussi automatiquement avec les objets manipulateur de flux - ce ne sont en réalité que des fonctions qui prennent un paramètre std :: ostream , et opérateur < < appelle simplement la fonction sur le ostream .

Vous pouvez faire de opérateur < < une fonction d'amis comme suit:

template <typename T> friend Logger& operator<< (Logger& logger, T thing);

Les spécialisations sont faciles - utilisez simplement les spécialisations de modèles (encore une fois, dans le fichier d'en-tête):

template <typename T>
Logger& operator<< (Logger& logger, T thing) {
    *logger.out_stream << thing;
    return logger;
}

// Template specialization - the "template <>" part is necessary
template <>
Logger& operator<< (Logger& logger, const wchar_t *wstr)
{
  // convert wstr to an ANSI string and log it
}

template <>
Logger& operator<< (Logger& logger, const std::wstring & wstr)
{
  // convert wstr to an ANSI string and log it
}

Aucune déclaration d'amitié requise:

class Logger
{
public:
    Logger();
    ~Logger();

template <typename T>
inline Logger& Display(T thing)
{
   *out_stream << thing;
    return *this;
}
private:
    std::ostream* out_stream;
};

template <typename T>
Logger& operator<< (Logger& logger, T thing) 
{
    return logger.Display(thing);
}

pourquoi ne pas le faire à l’aide de la méthode printf et utiliser la méthode des paramètres multiples (avec les trois points ...). Cela vous donne encore beaucoup de pouvoir de formation et ne vous gêne pas comme lorsque vous utilisez le < <.

Par exemple:

Logger("This is my log msg %0X", 45);

Attendez deux secondes et je vais extraire un exemple de code pour vous.

Modifier:

void Logger(const char* format, ...)
{
    char szMsg[3000];

    va_list args;
    va_start( args, format );
    vsnprintf( szMsg, sizeof(szMsg) - 1, format, args );
    va_end(args);

    // code to print szMsg to a file or whatever here
}

Si vous souhaitez utiliser cette classe comme une classe et non comme une fonction autonome, vous pouvez surcharger l'opérateur de journalisation () et cela fonctionnera de la même manière

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