Pergunta

my_macro << 1 << "hello world" << blah->getValue() << std::endl;

deve se expandir em:

std::ostringstream oss;
oss << 1 << "hello world" << blah->getValue() << std::endl;
ThreadSafeLogging(oss.str());
Foi útil?

Solução

#define my_macro my_stream()
class my_stream: public std::ostringstream  {
public:
    my_stream() {}
    ~my_stream() {
        ThreadSafeLogging(this->str());
    }
};
int main() {
    my_macro << 1 << "hello world" << std::endl;
}

Um temporário do tipo my_stream é criado, que é uma subclasse de ostringstream. Todas as operações para esse trabalho temporário como faria em um ostringstream.

Quando a declaração termina (ou seja, logo após o semicolon em toda a operação de impressão em Main ()), o objeto temporário sai do escopo e é destruído. o my_stream Chamadas de destruidor ThreadSafeLogging com os dados "coletados" anteriormente.

Testado (G ++).

Obrigado/créditos para dingo para apontar como simplificar a coisa toda, então não preciso da sobrecarga operator<<. Pena que os votos positivos não podem ser compartilhados.

Outras dicas

Você não poderia apenas derivar do Ostream e fornecer sua própria implementação segura para threads? Então você poderia simplesmente fazer

myCOutObject << 1 << "hello world" << blah->getValue() << std::endl;

E obtenha exatamente a mesma funcionalidade sem macros e usando C ++ corretamente?

Não. O problema é que, sem usar a sintaxe da função, uma macro é limitada a ser substituída apenas onde está.

Mas se você estava disposto a usar a sintaxe da função, poderá substituir as coisas antes e depois do ARGS.

my_macro(1 << "hello world" << blah->getValue() << std::endl);

Você poderia definir o mymacro como:

#define my_macro(args) std::ostreamstring oss; \
                       oss << args; \
                       ThreadSafeLogging(oss.str());

Dar uma olhada em Google-Glog, eles fazem isso usando um objeto temporário instanciado com um

LOG(INFO) << "log whatever" << 1;

E eles também têm outras macros interessantes, como log_if et al.

Considerando que você tem essas linhas incluídas em algum lugar do seu código, sim, é possível

#include <iostream>
#include <sstream> 

__LINE__ A macro é definida por todos os compiladores Standart. Portanto, podemos usá -lo para gerar um nome de variável que é diferente cada vez que você usa a macro :)

Aqui está uma nova versão que é vista apenas como uma instrução de uma estatização: (editado)

#define Var_(Name, Index) Name##Index
#define Var(Name, Index) Var_(Name, Index)
#define my_macro \
for (struct { int x; std::ostringstream oss; } Var(s, __LINE__) = { 0 }; \
     Var(s, __LINE__).x<2; ++Var(s, __LINE__).x)  \
    if (Var(s, __LINE__).x==1) ThreadSafeLogging(Var(s, __LINE__).oss.str()); \
    else Var(s, __LINE__).oss

// So you can use it like this 
int main() 
{ 
    if (4 != 2)
        my_macro << 4 << " hello "  << std::endl; 
    my_macro << 2 << " world !" << std::endl; 
} 

O desenvolvimento provavelmente não precisará usar esta macro duas vezes na mesma linha, porque a simplicidade do operador <<. Mas, caso você precise disso, você pode mudar o uso de __LINE__ por __COUNTER__ (que não é padrão!). Obrigado a QuuxPlusone por esta dica

Aqui está outro truque desagradável que vi em outro lugar. Ele tem uma desvantagem significativa em comparação com minha outra resposta: você não pode usá -la duas vezes no mesmo escopo, porque declara uma variável. No entanto, ainda pode ser interessante para outro casos onde você quer ter somemacro foo execute algo depois foo.

#define my_macro \
    std::ostringstream oss; \
    for (int x=0; x<2; ++x) \
        if (x==1) ThreadSafeLogging(oss.str()); \
        else oss

int main() {
    my_macro << 1 << "hello world" << std::endl;
}

A configuração de madeira que tenho é bastante semelhante:

bool ShouldLog(const char* file, size_t line, Priority prio);

class LoggerOutput : public std::stringstream {
public:
  LoggerOutput(const char* file, size_t line, Priority prio) 
  : prio(prio) 
  {
    Prefix(file, line, prio);
  }
  void Prefix(const char* file, size_t line, Priority prio);
  ~LoggerOutput() {
    Flush();
  }
  void Flush();
private:
  Priority prio;
};

#define LOG(Prio) if (!Logging::ShouldLog(__FILE__, __LINE__, Prio)) {} else Logging::LoggerOutput(__FILE__, __LINE__, Prio)

Se o seu registro estiver desativado, o ostream nunca será criado e existe pouca despesas gerais. Você pode configurar o registro do arquivo no nome do arquivo e os níveis de linha (s) ou prioridade. A função MustLog pode mudar entre invocações, para que você possa eliminar ou limitar a saída. A saída do log usa duas funções para se modificar, prefixo que adiciona um prefixo "arquivo: linha: (prio)" à linha, e flush () que os libera à saída do log como um único comando e adiciona uma nova linha a ele . Na minha implementação, sempre faz, mas você pode tornar isso condicional se ainda não estiver lá.

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