2008-11-14 18 views
5

pregunta originalCómo sobrecargar el operador << que no tomar o devolver ostream

Estoy escribí una clase de registro en el que el objetivo es ser capaz de hacer esto:

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

Actualmente mi cabecera Logger es como la siguiente:

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

Algunas notas acerca de esta clase:

  1. La compatibilidad multiplataforma no es un problema.
  2. Dentro de Logger.cpp hay una clase singleton que se encarga de crear el ostream "real".
  3. El constructor y el deconstructor de Logger realizan el bloqueo necesario del singleton.

que tienen tres problemas:

  • ¿Cómo hago que el operador < < función de un amigo o miembro para poder establecer flujo_out como privada?
  • ¿Cómo hago que la función del operador < < funcione para manipuladores?
  • ¿Cómo puedo agregar una especialización para que si T es un WCHAR * o std :: wstring, lo convierta en char * o std :: string antes de pasarlo a out_stream? (Puedo hacer la conversión La pérdida de caracteres Unicode alta no es un problema en mi caso..)

Resumen de lo aprendido en las respuestas: plantilla

  • puesto delante de amigo en lugar de después.
  • std :: ios :: hex no es un manipulador. std :: hex es un manipulador.

resultado 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; 
}; 
+0

Tu resultado es incorrecto. no se permiten especializaciones en el alcance de la clase :) simplemente sobrecargúelos (omita la plantilla <> parte) Se requiere en la respuesta de Adam (que los especializa en el ámbito del espacio de nombres) ya que de lo contrario las (entonces funciones normales del operador) ya no son amigas. –

+0

Curiosamente, funcionó con ellos allí. Pero, para ser correcto, los eliminé de todos modos. ¡Gracias! –

Respuesta

7

Se puede utilizar definición, que definirá el operador en el espacio de nombres de la clase rodea, y que sea visible sólo para resolución de la sobrecarga de operadores (no exigible de forma manual mediante el operador :: < < ... sintaxis):

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 */ 
    } 

}; 

La alternativa, para mantener su código tal como es y simplemente hacer que el operador < < plantilla de un amigo, deberá añadir esta línea en su definición de clase:

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

Para el problema de manipulador, voy a dar que mi código que escribo hace algún tiempo:

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

};

Tenga en cuenta que es std :: hex, pero no std :: ios :: hex. Este último se usa como un indicador de manipulador para la función setf de transmisiones. Tenga en cuenta que, para su ejemplo, sin embargo, no se requiere un tratamiento especial de manipuladores. El tratamiento especial anterior de std :: endl solo es necesario porque transfiero el tiempo además cuando se usa std :: endl.

+0

Argh! No es de extrañar que obtuviera 204845 en el registro cuando lo hice. Parece que los manipuladores funcionan como están. ¡Gracias! –

0

por qué no hacerlo de la manera printf y utilizar el método de parámetros múltiples (con los tres puntos ...). Esto todavía le da mucho poder de formateo y no lo hace tan desordenado como cuando está usando el < <.

Por ejemplo:

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

Cuelgue en dos segundos y malos tire hacia arriba de un ejemplo de código para usted.

Editar:

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 desea utilizar esto como una clase no es una función independiente que podría sobrecargar el operador registrador() y funcionará de la misma amigo

+0

Sugerencia más excelente! Hay dos razones por las que quiero usar el formato <<. Una es que prefiero la legibilidad de encadenar << 's juntos. La otra es que quiero evitar la creación de muchos búfers de caracteres. –

+0

Hay una serie de razones para no usar la forma varargs en el código C++, en particular, que elimina la seguridad del tipo, establece un acoplamiento estrecho entre el llamador/destinatario y el comportamiento indefinido de los UDT (aunque todavía están permitidos). – twokats

+0

Es bueno saberlo. Nunca fue fan de << en C++ – Lodle

2

Uso de una plantilla es la forma correcta de hacerlo, pero sólo hay que asegurarse de que la plantilla está en la archivo cabecera (logger.h, o lo que se llama), no en el archivo de implementación (logger.cpp) . Esto funcionará automáticamente para cualquier tipo que tenga operator << definido con std::ostream. También funcionará automáticamente con los objetos del manipulador de flujo: en realidad, son funciones que toman un parámetro std::ostream, y operator << llama a la función en el ostream.

Puede hacer operator << un amigo función de la siguiente manera:

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

especializaciones son fáciles - sólo tiene que utilizar especializaciones de plantilla (de nuevo, en el archivo de cabecera):

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 
} 
2

Sin Declarar la amistad sea necesario:

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