Existe alguma maneira de escrever o seguinte como uma macro C ++?
-
25-09-2019 - |
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());
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á.