Pergunta

Pergunta original

Estou escrevendo uma aula de madeira onde o objetivo é poder fazer isso:

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

Atualmente, meu cabeçalho de madeireiro se parece mais disso:

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

Algumas notas sobre esta aula:

  1. A compatibilidade cruzada da plataforma não é um problema.
  2. Dentro do Logger.cpp, há uma aula de singleton que cuida da criação do Ostream "real".
  3. O construtor e o desconstrutor do logger realizam o bloqueio necessário do singleton.

Eu tenho três problemas:

  • Como faço para fazer do operador << funcione um amigo ou membro para que eu possa definir o Out_stream como privado?
  • Como faço para fazer o operador << função funcionar para manipuladores?
  • Como posso adicionar uma especialização para que, se t for um wchar* ou std :: wstring, ele o converterá para char* ou std :: string antes de passá -lo para out_stream? (Eu posso fazer a conversão. Perder altos caracteres Unicode não é um problema no meu caso.)

Resumo das coisas aprendidas nas respostas:

  • Coloque o modelo antes do amigo em vez de depois.
  • STD :: iOS :: Hex não é um manipulador. std :: hex é um 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;
};
Foi útil?

Solução

Você pode usar a definição de amizade, que definirá o operador no espaço de nome circundante da classe e tornará visível apenas para a resolução de sobrecarga do operador (não é chamável manualmente usando o :: operador << ... sintaxe):

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

};

A alternativa, para manter seu código como está e apenas fazer do operador << modelo de um amigo, você adiciona essa linha à sua definição de classe:

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

Para o problema do manipulador, vou apenas dar meu código que escrevo há algum tempo:

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

};

Observe que é std :: hex, mas não std :: iOS :: Hex. O último é usado como uma bandeira de manipulador para o setf função dos fluxos. Observe que, para o seu exemplo, não é necessário tratamento especial de manipuladores. O tratamento especial acima do STD :: ENDL é necessário apenas porque eu transmito o tempo além de STD :: ENDL é usado.

Outras dicas

Usar um modelo é a maneira certa de fazê -lo, mas você só precisa garantir que o modelo esteja no cabeçalho Arquivo (logger.h, ou o que você chamou), não No arquivo de implementação (logger.cpp). Isso funcionará automaticamente para qualquer tipo que tenha operator << definido com um std::ostream. Também funcionará automaticamente com objetos de manipulador de fluxo - essas são realmente apenas funções que tomam um std::ostream parâmetro e operator << apenas chama a função no ostream.

Você pode fazer operator << uma função de amigo da seguinte maneira:

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

Especializações são fáceis - basta usar as especializações do modelo (novamente, no arquivo de cabeçalho):

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
}

Nenhuma declaração de amizade necessária:

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

Por que não fazer isso da maneira Printf e usar o método multi parâmetro (com os três pontos ...). Isso ainda oferece muito poder de formação e não faz com que isso seja confuso como quando você está usando o <<.

Por exemplo:

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

Espere em dois segundos e puxei uma amostra de código para você.

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
}

Se você quiser usar isso como uma classe, não uma função independente, você pode sobrecarregar o operador de logger () e funcionará da mesma forma

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top