mirror of
https://github.com/Zygo/bees.git
synced 2025-05-17 21:35:45 +02:00
Some projects use only one log level, so there is no need to repeat it for every line. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
153 lines
3.4 KiB
C++
153 lines
3.4 KiB
C++
#ifndef CRUCIBLE_CHATTER_H
|
|
#define CRUCIBLE_CHATTER_H
|
|
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <typeinfo>
|
|
|
|
#include <syslog.h>
|
|
|
|
/** \brief Chatter wraps a std::ostream reference with a destructor that
|
|
writes a newline, and inserts timestamp, pid, and tid prefixes on output.
|
|
|
|
Typical usage is expressions like the following:
|
|
|
|
int six = 6, nine = 9; \n
|
|
Chatter() << "What you get when you multiply" << six
|
|
<< "by" << nine << '?'; \n
|
|
Chatter() << "forty two!";
|
|
|
|
which results in output like the following:
|
|
|
|
What you get when you multiply 6 by 9 ?\n
|
|
forty-two!
|
|
|
|
Note that newlines and timestamps are injected automatically in
|
|
the output by the Chatter destructor. You can also use std::endl
|
|
explicitly, although it will not have the effect of flushing the
|
|
buffer.
|
|
*/
|
|
|
|
namespace crucible {
|
|
using namespace std;
|
|
|
|
class Chatter {
|
|
int m_loglevel;
|
|
string m_name;
|
|
ostream &m_os;
|
|
ostringstream m_oss;
|
|
|
|
public:
|
|
Chatter(int loglevel, string name, ostream &os = cerr);
|
|
Chatter(Chatter &&c);
|
|
ostream &get_os() { return m_oss; }
|
|
|
|
template <class T> Chatter &operator<<(const T& arg);
|
|
|
|
~Chatter();
|
|
|
|
static void enable_timestamp(bool prefix_timestamp);
|
|
static void enable_level(bool prefix_level);
|
|
};
|
|
|
|
template <class Argument>
|
|
struct ChatterTraits {
|
|
Chatter &operator()(Chatter &c, const Argument &arg)
|
|
{
|
|
c.get_os() << arg;
|
|
return c;
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
Chatter &
|
|
Chatter::operator<<(const T& arg)
|
|
{
|
|
return ChatterTraits<T>()(*this, arg);
|
|
}
|
|
|
|
template <class Argument>
|
|
struct ChatterTraits<const Argument *> {
|
|
Chatter &operator()(Chatter &c, const Argument *arg)
|
|
{
|
|
if (arg) {
|
|
c.get_os() << "(pointer to " << typeid(*arg).name() << ")(" << reinterpret_cast<const void *>(arg) << ")";
|
|
} else {
|
|
c.get_os() << "(NULL pointer to " << typeid(arg).name() << ')';
|
|
}
|
|
return c;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct ChatterTraits<const char *> {
|
|
Chatter &
|
|
operator()(Chatter &c, const char *arg)
|
|
{
|
|
c.get_os() << arg;
|
|
return c;
|
|
}
|
|
};
|
|
|
|
class ChatterBox {
|
|
string m_file;
|
|
int m_line;
|
|
string m_pretty_function;
|
|
bool m_enabled;
|
|
ostream& m_os;
|
|
|
|
static set<ChatterBox*> s_boxes;
|
|
|
|
public:
|
|
ChatterBox(string file, int line, string pretty_function, ostream &os = cerr);
|
|
~ChatterBox();
|
|
|
|
template <class T> Chatter operator<<(const T &t)
|
|
{
|
|
Chatter c(LOG_NOTICE, m_pretty_function, m_os);
|
|
c << t;
|
|
return c;
|
|
}
|
|
|
|
bool enabled() const { return m_enabled; }
|
|
void set_enable(bool en);
|
|
|
|
static set<ChatterBox*>& all_boxes();
|
|
};
|
|
|
|
class ChatterUnwinder {
|
|
function<void()> m_func;
|
|
public:
|
|
ChatterUnwinder(function<void()> f);
|
|
~ChatterUnwinder();
|
|
};
|
|
};
|
|
|
|
#define CHATTER(x) do { \
|
|
using namespace crucible; \
|
|
static ChatterBox crucible_chatterbox_cb(__FILE__, __LINE__, __func__); \
|
|
if (crucible_chatterbox_cb.enabled()) { \
|
|
crucible_chatterbox_cb << x; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CHATTER_TRACE(x) do { \
|
|
using namespace crucible; \
|
|
static ChatterBox crucible_chatterbox_cb(__FILE__, __LINE__, __func__); \
|
|
if (crucible_chatterbox_cb.enabled()) { \
|
|
crucible_chatterbox_cb << __FILE__ << ":" << __LINE__ << ": " << x; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define WTF_C(x, y) x##y
|
|
#define SRSLY_WTF_C(x, y) WTF_C(x, y)
|
|
#define CHATTER_UNWIND(x) \
|
|
crucible::ChatterUnwinder SRSLY_WTF_C(chatterUnwinder_, __LINE__) ([&]() { \
|
|
CHATTER_TRACE(x); \
|
|
})
|
|
|
|
#endif // CRUCIBLE_CHATTER_H
|