refs #624, #492 Refactor parts of CLogMessage into base class and use inheriting constructors to unify the construction idiom of CLogMessage and CStatusMessage.

This commit is contained in:
Mathew Sutcliffe
2016-03-19 23:33:37 +00:00
parent 2ce1f3da0a
commit caa01f846b
8 changed files with 230 additions and 187 deletions

View File

@@ -51,7 +51,7 @@ namespace BlackCore
Q_ASSERT_X(m_fsdTextCodec, "CNetworkVatlib", "Missing default wire text encoding");
Q_ASSERT_X(Vat_GetVersion() == VAT_LIBVATLIB_VERSION, "swift.network", "Wrong vatlib shared library installed");
Vat_SetNetworkLogHandler(SeverityError, CNetworkVatlib::networkLogHandler);
Vat_SetNetworkLogHandler(SeverityLevel::SeverityError, CNetworkVatlib::networkLogHandler);
connect(&m_processingTimer, SIGNAL(timeout()), this, SLOT(process()));
connect(&m_positionUpdateTimer, &QTimer::timeout, this, &CNetworkVatlib::sendPositionUpdate);

View File

@@ -29,7 +29,7 @@ namespace BlackCore
m_audioService(Vat_CreateAudioService()),
m_udpPort(Vat_CreateUDPAudioPort(m_audioService.data(), 0))
{
Vat_SetVoiceLogHandler(SeverityError, CVoiceVatlib::voiceLogHandler);
Vat_SetVoiceLogHandler(SeverityLevel::SeverityError, CVoiceVatlib::voiceLogHandler);
// do processing
this->startTimer(10);

View File

@@ -48,6 +48,12 @@ const QJsonValue &operator >>(const QJsonValue &json, QString &value)
return json;
}
const QJsonValue &operator >>(const QJsonValue &json, QStringList &value)
{
for (auto &&element : json.toArray()) { value << element.toString(); }
return json;
}
const QJsonValue &operator >>(const QJsonValue &json, double &value)
{
value = json.toDouble();
@@ -116,6 +122,12 @@ const QJsonValueRef &operator >>(const QJsonValueRef &json, QString &value)
return json;
}
const QJsonValueRef &operator >>(const QJsonValueRef &json, QStringList &value)
{
for (auto &&element : json.toArray()) { value << element.toString(); }
return json;
}
const QJsonValueRef &operator >>(const QJsonValueRef &json, double &value)
{
value = json.toDouble();
@@ -252,6 +264,12 @@ QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const QStrin
return json;
}
QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const QStringList &> &value)
{
json.insert(value.first, QJsonValue(QJsonArray::fromStringList(value.second)));
return json;
}
QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const double &> &value)
{
json.insert(value.first, QJsonValue(value.second));

View File

@@ -39,6 +39,7 @@ BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, qulonglon
BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, uint &value);
BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, qint16 &value);
BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, QString &value);
BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, QStringList &value);
BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, double &value);
BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, bool &value);
BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, QDateTime &value);
@@ -50,6 +51,7 @@ BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, qul
BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, uint &value);
BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, qint16 &value);
BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, QString &value);
BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, QStringList &value);
BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, double &value);
BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, bool &value);
BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, QDateTime &value);
@@ -132,6 +134,7 @@ BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair<QStr
BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const uint &> &value);
BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const qulonglong &> &value);
BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const QString &> &value);
BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const QStringList &> &value);
BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const double &> &value);
BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const bool &> &value);
BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair<QString, const QDateTime &> &value);

View File

@@ -15,53 +15,9 @@
namespace BlackMisc
{
CLogMessage &CLogMessage::debug()
{
m_severity = CStatusMessage::SeverityDebug;
return *this;
}
CLogMessage::CLogMessage() = default;
CLogMessage &CLogMessage::info(QString format)
{
m_severity = CStatusMessage::SeverityInfo;
m_message = format;
return *this;
}
CLogMessage &CLogMessage::warning(QString format)
{
m_severity = CStatusMessage::SeverityWarning;
m_message = format;
return *this;
}
CLogMessage &CLogMessage::error(QString format)
{
m_severity = CStatusMessage::SeverityError;
m_message = format;
return *this;
}
CLogMessage &CLogMessage::validationInfo(QString format)
{
m_categories.remove(CLogCategory::uncategorized());
m_categories.push_back(CLogCategory::validation());
return info(format);
}
CLogMessage &CLogMessage::validationWarning(QString format)
{
m_categories.remove(CLogCategory::uncategorized());
m_categories.push_back(CLogCategory::validation());
return warning(format);
}
CLogMessage &CLogMessage::validationError(QString format)
{
m_categories.remove(CLogCategory::uncategorized());
m_categories.push_back(CLogCategory::validation());
return error(format);
}
CLogMessage::CLogMessage(const char *file, int line, const char *function) : m_logger(file, line, function) {}
CLogMessage::operator CStatusMessage()
{
@@ -125,37 +81,6 @@ namespace BlackMisc
}
}
namespace Private
{
template <size_t... Is> QString arg(index_sequence<Is...>, const QString &format, const QStringList &args) { return format.arg(args[Is]...); }
QString arg(index_sequence<>, const QString &format, const QStringList &) { return format; }
}
QString CLogMessage::message() const
{
if (m_message.isEmpty())
{
return m_args.join(" ");
}
else
{
switch (m_args.size())
{
case 0: return Private::arg(Private::make_index_sequence<0>(), m_message, m_args);
case 1: return Private::arg(Private::make_index_sequence<1>(), m_message, m_args);
case 2: return Private::arg(Private::make_index_sequence<2>(), m_message, m_args);
case 3: return Private::arg(Private::make_index_sequence<3>(), m_message, m_args);
case 4: return Private::arg(Private::make_index_sequence<4>(), m_message, m_args);
case 5: return Private::arg(Private::make_index_sequence<5>(), m_message, m_args);
case 6: return Private::arg(Private::make_index_sequence<6>(), m_message, m_args);
case 7: return Private::arg(Private::make_index_sequence<7>(), m_message, m_args);
case 8: return Private::arg(Private::make_index_sequence<8>(), m_message, m_args);
default: qWarning("Too many arguments"); // intentional fall-through
case 9: return Private::arg(Private::make_index_sequence<9>(), m_message, m_args);
}
}
}
//! Does category contain flag?
bool hasFlag(const QString &category, const QString &flag)
{

View File

@@ -24,25 +24,6 @@
namespace BlackMisc
{
/*!
* Trait to detect whether T contains a member toQString.
*/
template <typename T>
class HasToQString
{
// http://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector
struct Fallback { int toQString; };
template <int Fallback:: *> struct int_t { typedef int type; };
template <typename U, bool = std::is_class<U>::value> struct Derived : public U, public Fallback {};
template <typename U> struct Derived<U, false> : public Fallback {};
template <typename U> static char test(typename int_t<&Derived<U>::toQString>::type);
template <typename U> static int test(...);
public:
//! True if T contains a member toQString.
static const bool value = sizeof(test<T>(0)) > 1;
};
/*!
* Helper with static methods for dealing with metadata embedded in log message category strings.
*
@@ -83,26 +64,17 @@ namespace BlackMisc
* The categories are arbitrary string tags which can be attached to the message to categorize it.
* A message can have more than one category. The categories can be used for filtering by message handlers.
*/
class BLACKMISC_EXPORT CLogMessage
class BLACKMISC_EXPORT CLogMessage : public CMessageBase<CLogMessage>
{
public:
//! Inheriting constructors.
using CMessageBase::CMessageBase;
//! Construct a message with the "uncategorized" category.
CLogMessage() {}
CLogMessage();
//! Constructor taking filename, line number, and function name, for uncategorized verbose debug messages.
CLogMessage(const char *file, int line, const char *function) : m_logger(file, line, function) {}
//! Construct a message with some specific category.
CLogMessage(const CLogCategory &category) : m_categories({ category }) {}
//! Construct a message with some specific categories.
CLogMessage(const CLogCategoryList &categories) : m_categories(categories) {}
//! Construct a message with some specific categories.
CLogMessage(const CLogCategoryList &categories, const CLogCategory &extra) : CLogMessage(categories) { m_categories.push_back(extra); }
//! Construct a message with some specific categories.
CLogMessage(const CLogCategoryList &categories, const CLogCategoryList &extra) : CLogMessage(categories) { m_categories.push_back(extra); }
CLogMessage(const char *file, int line, const char *function);
//! Destructor. This actually emits the message.
~CLogMessage();
@@ -113,48 +85,6 @@ namespace BlackMisc
//! Convert to CVariant for returning the message directly from the function which generated it.
operator CVariant();
//! Set the severity to debug.
CLogMessage &debug();
//! Set the severity to info, providing a format string.
CLogMessage &info(QString format);
//! Set the severity to warning, providing a format string.
CLogMessage &warning(QString format);
//! Set the severity to error, providing a format string.
CLogMessage &error(QString format);
//! Set the severity to info, providing a format string, and adding the validation category.
CLogMessage &validationInfo(QString format);
//! Set the severity to warning, providing a format string, and adding the validation category.
CLogMessage &validationWarning(QString format);
//! Set the severity to error, providing a format string, and adding the validation category.
CLogMessage &validationError(QString format);
//! Streaming operators.
//! \details If the format string is empty, the message will consist of all streamed values separated by spaces.
//! Otherwise, the streamed values will replace the place markers %1, %2, %3... in the format string.
//! \see QString::arg
//! @{
CLogMessage &operator <<(const QString &v) { return arg(v); }
CLogMessage &operator <<(int v) { return arg(QString::number(v)); }
CLogMessage &operator <<(uint v) { return arg(QString::number(v)); }
CLogMessage &operator <<(long v) { return arg(QString::number(v)); }
CLogMessage &operator <<(ulong v) { return arg(QString::number(v)); }
CLogMessage &operator <<(qlonglong v) { return arg(QString::number(v)); }
CLogMessage &operator <<(qulonglong v) { return arg(QString::number(v)); }
CLogMessage &operator <<(short v) { return arg(QString::number(v)); }
CLogMessage &operator <<(ushort v) { return arg(QString::number(v)); }
CLogMessage &operator <<(QChar v) { return arg(v); }
CLogMessage &operator <<(char v) { return arg(QChar(v)); }
CLogMessage &operator <<(double v) { return arg(QString::number(v)); }
template <class T, class = typename std::enable_if<HasToQString<T>::value>::type>
CLogMessage &operator <<(const T &v) { return arg(v.toQString()); }
//! @}
//! Sends a verbatim, preformatted message to the log.
static void preformatted(const CStatusMessage &statusMessage);
@@ -163,13 +93,7 @@ namespace BlackMisc
private:
QMessageLogger m_logger;
CStatusMessage::StatusSeverity m_severity = CStatusMessage::SeverityDebug;
CLogCategoryList m_categories = CLogCategoryList { CLogCategory::uncategorized() };
QString m_message;
QStringList m_args;
CLogMessage &arg(QString value) { m_args.push_back(value); return *this; }
QString message() const;
QByteArray qtCategory() const;
QDebug ostream(const QByteArray &category) const;
};

View File

@@ -18,37 +18,89 @@
namespace BlackMisc
{
namespace Private
{
namespace
{
template <size_t... Is> QString arg(index_sequence<Is...>, const QString &format, const QStringList &args) { return format.arg(args[Is]...); }
QString arg(index_sequence<>, const QString &format, const QStringList &) { return format; }
}
QString arg(const QString &format, const QStringList &args)
{
if (format.isEmpty())
{
return args.join(" ");
}
else
{
switch (args.size())
{
case 0: return arg(Private::make_index_sequence<0>(), format, args);
case 1: return arg(Private::make_index_sequence<1>(), format, args);
case 2: return arg(Private::make_index_sequence<2>(), format, args);
case 3: return arg(Private::make_index_sequence<3>(), format, args);
case 4: return arg(Private::make_index_sequence<4>(), format, args);
case 5: return arg(Private::make_index_sequence<5>(), format, args);
case 6: return arg(Private::make_index_sequence<6>(), format, args);
case 7: return arg(Private::make_index_sequence<7>(), format, args);
case 8: return arg(Private::make_index_sequence<8>(), format, args);
default: qWarning("Too many arguments to BlackMisc::Private::arg"); // intentional fall-through
case 9: return arg(Private::make_index_sequence<9>(), format, args);
}
}
}
}
// needed because these constants are odr-used (just like traditional C++98 static const)
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54483
const StatusSeverity CStatusMessage::SeverityDebug;
const StatusSeverity CStatusMessage::SeverityInfo;
const StatusSeverity CStatusMessage::SeverityWarning;
const StatusSeverity CStatusMessage::SeverityError;
CStatusMessage::CStatusMessage() = default;
CStatusMessage::CStatusMessage(const CStatusMessage &other) :
CValueObject(other),
CMessageBase(other),
ITimestampBased(other)
{
*this = other;
QReadLocker lock(&other.m_lock);
m_handledByObjects = other.m_handledByObjects;
}
CStatusMessage &CStatusMessage::operator =(const CStatusMessage &other)
{
if (this == &other) { return *this; }
static_cast<CMessageBase &>(*this) = other;
QReadLocker readLock(&other.m_lock);
auto tuple = std::make_tuple(other.m_categories, other.m_severity, other.m_message, other.m_handledByObjects);
auto handledBy = other.m_handledByObjects;
readLock.unlock(); // avoid deadlock
QWriteLocker writeLock(&this->m_lock);
std::tie(m_categories, m_severity, m_message, m_handledByObjects) = tuple;
m_handledByObjects = handledBy;
return *this;
}
CStatusMessage::CStatusMessage(const QString &message)
: m_message(message.trimmed())
{}
{
m_message = message.trimmed();
}
CStatusMessage::CStatusMessage(StatusSeverity severity, const QString &message)
: m_severity(severity), m_message(message.trimmed())
{}
: CStatusMessage(message)
{
m_severity = severity;
}
CStatusMessage::CStatusMessage(const CLogCategoryList &categories, StatusSeverity severity, const QString &message)
: m_categories(categories), m_severity(severity), m_message(message.trimmed())
{}
: CStatusMessage(severity, message)
{
m_categories = categories;
}
CStatusMessage::CStatusMessage(QtMsgType type, const QMessageLogContext &context, const QString &message)
: CStatusMessage(message.trimmed())
@@ -82,7 +134,7 @@ namespace BlackMisc
}
*o_category = category;
*o_message = this->m_message;
*o_message = this->getMessage();
switch (this->m_severity)
{
@@ -185,7 +237,7 @@ namespace BlackMisc
s.append(" when: ");
s.append(this->getFormattedUtcTimestampYmdhms());
s.append(" ").append(this->m_message);
s.append(" ").append(this->getMessage());
return s;
}
@@ -320,7 +372,7 @@ namespace BlackMisc
switch (i)
{
case IndexMessage:
return CVariant::from(this->m_message);
return CVariant::from(this->getMessage());
case IndexSeverity:
return CVariant::from(this->m_severity);
case IndexSeverityAsString:
@@ -345,6 +397,7 @@ namespace BlackMisc
{
case IndexMessage:
this->m_message = variant.value<QString>();
this->m_args.clear();
break;
case IndexSeverity:
this->m_severity = variant.value<StatusSeverity>();

View File

@@ -23,22 +23,141 @@ namespace BlackMisc
{
class CStatusException;
namespace Private
{
//! Like QString::arg() but accepts a QStringList of args.
BLACKMISC_EXPORT QString arg(const QString &format, const QStringList &args);
}
/*!
* Trait to detect whether T contains a member toQString.
*/
template <typename T>
class HasToQString
{
// http://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector
struct Fallback { int toQString; };
template <int Fallback:: *> struct int_t { using type = int; };
template <typename U, bool = std::is_class<U>::value> struct Derived : public U, public Fallback {};
template <typename U> struct Derived<U, false> : public Fallback {};
template <typename U> static char test(typename int_t<&Derived<U>::toQString>::type);
template <typename U> static int test(...);
public:
//! True if T contains a member toQString.
static const bool value = sizeof(test<T>(0)) > 1;
};
/*!
* Status severities
*/
enum StatusSeverity
{
SeverityDebug,
SeverityInfo,
SeverityWarning,
SeverityError
};
/*!
* Base class for CStatusMessage and CLogMessage.
*/
template <class Derived>
class CMessageBase
{
public:
//! Default constructor.
CMessageBase() {}
//! Construct a message with some specific category.
explicit CMessageBase(const CLogCategory &category) : m_categories({ category }) {}
//! Construct a message with some specific categories.
explicit CMessageBase(const CLogCategoryList &categories) : m_categories(categories) {}
//! Construct a message with some specific categories.
CMessageBase(const CLogCategoryList &categories, const CLogCategory &extra) : CMessageBase(categories) { m_categories.push_back(extra); }
//! Construct a message with some specific categories.
CMessageBase(const CLogCategoryList &categories, const CLogCategoryList &extra) : CMessageBase(categories) { m_categories.push_back(extra); }
//! Set the severity to debug.
Derived &debug() { return setSeverityAndMessage(SeverityDebug, ""); }
//! Set the severity to info, providing a format string.
Derived &info(QString format) { return setSeverityAndMessage(SeverityInfo, format); }
//! Set the severity to warning, providing a format string.
Derived &warning(QString format) { return setSeverityAndMessage(SeverityWarning, format); }
//! Set the severity to error, providing a format string.
Derived &error(QString format) { return setSeverityAndMessage(SeverityError, format); }
//! Set the severity to info, providing a format string, and adding the validation category.
Derived &validationInfo(QString format) { setValidation(); return setSeverityAndMessage(SeverityInfo, format); }
//! Set the severity to warning, providing a format string, and adding the validation category.
Derived &validationWarning(QString format) { setValidation(); return setSeverityAndMessage(SeverityWarning, format); }
//! Set the severity to error, providing a format string, and adding the validation category.
Derived &validationError(QString format) { setValidation(); return setSeverityAndMessage(SeverityError, format); }
//! Streaming operators.
//! \details If the format string is empty, the message will consist of all streamed values separated by spaces.
//! Otherwise, the streamed values will replace the place markers %1, %2, %3... in the format string.
//! \see QString::arg
//! @{
Derived &operator <<(const QString &v) { return arg(v); }
Derived &operator <<(int v) { return arg(QString::number(v)); }
Derived &operator <<(uint v) { return arg(QString::number(v)); }
Derived &operator <<(long v) { return arg(QString::number(v)); }
Derived &operator <<(ulong v) { return arg(QString::number(v)); }
Derived &operator <<(qlonglong v) { return arg(QString::number(v)); }
Derived &operator <<(qulonglong v) { return arg(QString::number(v)); }
Derived &operator <<(short v) { return arg(QString::number(v)); }
Derived &operator <<(ushort v) { return arg(QString::number(v)); }
Derived &operator <<(QChar v) { return arg(v); }
Derived &operator <<(char v) { return arg(QChar(v)); }
Derived &operator <<(double v) { return arg(QString::number(v)); }
template <class T, class = typename std::enable_if<HasToQString<T>::value>::type>
Derived &operator <<(const T &v) { return arg(v.toQString()); }
//! @}
private:
void setValidation() { m_categories.remove(CLogCategory::uncategorized()); m_categories.push_back(CLogCategory::validation()); }
Derived &setSeverityAndMessage(StatusSeverity s, const QString &m) { m_message = m; m_severity = s; return derived(); }
Derived &arg(QString value) { m_args.push_back(value); return derived(); }
Derived &derived() { return static_cast<Derived &>(*this); }
protected:
//! \private
//! @{
QString m_message;
QStringList m_args;
CLogCategoryList m_categories = CLogCategoryList { CLogCategory::uncategorized() };
StatusSeverity m_severity = SeverityDebug;
QString message() const { return Private::arg(m_message, m_args); }
//! @}
};
/*!
* Streamable status message, e.g. from Core -> GUI
*/
class BLACKMISC_EXPORT CStatusMessage :
public CValueObject<CStatusMessage>,
public CMessageBase<CStatusMessage>,
public ITimestampBased
{
public:
//! Status severities
enum StatusSeverity
{
SeverityDebug,
SeverityInfo,
SeverityWarning,
SeverityError
};
//! \copydoc BlackMisc::StatusSeverity
//! @{
using StatusSeverity = BlackMisc::StatusSeverity;
constexpr static auto SeverityDebug = BlackMisc::SeverityDebug;
constexpr static auto SeverityInfo = BlackMisc::SeverityInfo;
constexpr static auto SeverityWarning = BlackMisc::SeverityWarning;
constexpr static auto SeverityError = BlackMisc::SeverityError;
//! @}
//! Properties by index
enum ColumnIndex
@@ -51,8 +170,11 @@ namespace BlackMisc
IndexMessage
};
//! Inheriting constructors.
using CMessageBase::CMessageBase;
//! Constructor
CStatusMessage() = default;
CStatusMessage();
//! Copy constructor (because of mutex)
CStatusMessage(const CStatusMessage &other);
@@ -114,7 +236,7 @@ namespace BlackMisc
bool isFailure() const;
//! Message
QString getMessage() const { return this->m_message; }
QString getMessage() const { return this->message(); }
//! Prepend message
void prependMessage(const QString &msg);
@@ -123,7 +245,7 @@ namespace BlackMisc
void appendMessage(const QString &msg);
//! Message empty
bool isEmpty() const { return this->m_message.isEmpty(); }
bool isEmpty() const { return this->m_message.isEmpty() && this->m_args.isEmpty(); }
//! Returns true if this message was sent by an instance of class T.
template <class T>
@@ -201,9 +323,6 @@ namespace BlackMisc
private:
BLACK_ENABLE_TUPLE_CONVERSION(CStatusMessage)
CLogCategoryList m_categories;
StatusSeverity m_severity = SeverityDebug;
QString m_message;
mutable QVector<quintptr> m_handledByObjects;
mutable QReadWriteLock m_lock; //!< lock (because of mutable members)
};
@@ -214,6 +333,7 @@ BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CStatusMessage, (
o.m_categories,
o.m_severity,
o.m_message,
o.m_args,
o.m_timestampMSecsSinceEpoch,
attr(o.m_handledByObjects, flags < DisabledForHashing | DisabledForJson | DisabledForComparison | DisabledForMarshalling > ())
))