Domanda

Domanda originale

Sto scrivendo una classe di registrazione in cui l'obiettivo è riuscire a farlo:

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

Attualmente la mia intestazione del Logger è simile a questa:

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

Alcune note su questa classe:

  1. La compatibilità multipiattaforma non è un problema.
  2. All'interno di Logger.cpp c'è una classe singleton che si occupa di creare il "reale" ostream.
  3. Il costruttore e il decostruttore Logger eseguono il blocco necessario del singleton.

Ho tre problemi:

  • Come posso rendere l'operatore < < funzione un amico o un membro in modo da poter impostare out_stream come privato?
  • Come posso rendere l'operatore < < funzione di lavoro per manipolatori?
  • Come posso aggiungere una specializzazione in modo che se T è un WCHAR * o std :: wstring che lo convertirà in char * o std :: string prima di passarlo a out_stream? (Posso fare la conversione. Perdere caratteri Unicode elevati non è un problema nel mio caso.)

Sintesi delle cose apprese nelle risposte:

  • Metti il ??modello PRIMA dell'amico anziché dopo.
  • std :: ios :: hex non è un manipolatore. std :: hex è un manipolatore.

Risultato finale

#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;
};
È stato utile?

Soluzione

Puoi usare la definizione di amico, che definirà l'operatore nello spazio dei nomi circostante della classe e renderlo visibile solo alla risoluzione di sovraccarico dell'operatore (non richiamabile manualmente usando la sintassi :: 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'alternativa, per mantenere il codice così com'è e rendere l'operatore < < modello amico, aggiungi questa riga nella definizione della classe:

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

Per il problema del manipolatore, ti darò il mio codice che scrivo qualche tempo fa:

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

};

Nota che è std :: hex, ma non std :: ios :: hex . Quest'ultimo è usato come flag manipolatore per la funzione setf degli stream. Si noti che, ad esempio, non è richiesto alcun trattamento speciale per i manipolatori. Il trattamento speciale di cui sopra di std :: endl è necessario solo perché eseguo lo streaming del tempo in aggiunta quando viene utilizzato std :: endl.

Altri suggerimenti

L'uso di un modello è il modo giusto per farlo, ma devi solo assicurarti che il modello sia nel file intestazione ( logger.h , o qualunque cosa tu chiamato), non nel file di implementazione ( logger.cpp ). Funzionerà automaticamente per qualsiasi tipo che abbia operatore < < definito con un std :: ostream . Funzionerà anche automaticamente con gli oggetti manipolatore di flusso - queste sono in realtà solo funzioni che accettano un parametro std :: ostream e l'operatore < < chiama semplicemente la funzione sul ostream .

Puoi rendere operator < < una funzione amico come segue:

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

Le specializzazioni sono facili: basta usare le specializzazioni dei modelli (di nuovo, nel file di intestazione):

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
}

Nessuna dichiarazione di amicizia necessaria:

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

perché non farlo nel modo printf e usare il metodo multiparametrico (con i tre punti ...). Questo ti dà ancora un sacco di potere formativo e non lo rende così disordinato come quando stai usando il < < ;.

Ad esempio:

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

Aspetta due secondi e tirerò su un esempio di codice per te.

Modifica:

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
}

Se si desidera utilizzare questa funzione come classe non autonoma, è possibile sovraccaricare l'operatore logger () e funzionerà allo stesso modo

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top