Commit b8c51514 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère
Browse files

Added QMovedResult for high-performance bind (no allocation, presumably no lock)

Drawbacks are:
- users must call .result() to close opened data structures
- it is more difficult to implement TResult and TImpl correctly
- TResult is not pimpled by default
parent b208801c
......@@ -109,7 +109,7 @@ private:
};
template<class TResult_, class TImpl_, BindMode Mode_>
class QScopedResult
class QScopedResult //!< Base class for TResult classes that require a single heap allocation (and lock) but are simple to implement and de facto pimpled
{
Q_DISABLE_COPY(QScopedResult)
public:
......@@ -138,6 +138,40 @@ protected:
bool m_owned = true; //!< for nested QBind only
};
//! Base class for TResult classes that can exhibit high performance (they require no heap allocation and no lock)
//! \warning
//! - users must call .result() to close opened data structures
//! - it is more difficult to implement TResult and TImpl correctly
//! - TResult is not pimpled by default
template<class TResult_, class TImpl_, BindMode Mode_>
class QMovedResult
{
Q_DISABLE_COPY(QMovedResult)
public:
Q_ENABLE_DEFAULT_MOVE(QMovedResult)
using TResult = TResult_;
using TImpl = TImpl_;
static constexpr BindMode Mode = Mode_;
operator bool() { return m; } //!< to drive QBind<TResult,T>() traversal
TImpl* operator->() { Q_ASSERT_X(m,Q_FUNC_INFO,"check operator bool() before calling operator->()"); return &m; }
// Shortcuts
/**/ Seq<TResult> sequence() { return value().sequence(); }
/**/ TResult null() { return value(). null(); }
template<typename T> TResult bind( T& t) { return value(). bind(t); } // TODO only for readers ?
template<typename T> TResult bind(const T& t) { return value(). bind(t); }
template<typename T> TResult bind( T&& t) { return value(). bind(t); }
protected:
QMovedResult( TImpl&& m) : m(m) {}
QMovedResult(const TImpl& m) : m(m) {} // for nested QBind only
Val<TResult> value() { return std::move(Val<TResult>(std::move(*static_cast<TResult*>(this)))); }
TImpl m = TImpl();
};
// //////////////////////////////////////////////////////////////////////////
// QBind<TResult,T>
......
......@@ -113,8 +113,6 @@ public:
QCborWriterImpl() = default;
QCborWriterImpl(QIODevice* io) : io(io) { Q_ASSERT(io); }
~QCborWriterImpl() { while (levels) out(); }
operator bool() { return io; } //!< to drive QBind<TResult,T>() traversal
protected:
template<class T_> friend class Val; // calls methods below
bool sequence() { levels++;
......
......@@ -74,22 +74,15 @@ struct Person
#include <type_traits>
#include <QtCore/qiodevice.h>
class TextWriterImpl;
class TextWriter : public QScopedResult<TextWriter, TextWriterImpl, BindMode::Write>
{
Q_DISABLE_COPY(TextWriter)
public:
Q_ENABLE_DEFAULT_MOVE(TextWriter)
TextWriter(QIODevice* io);
private:
friend class TextWriterImpl; // uses method below
TextWriter(TextWriterImpl* outer) : QScopedResult(outer, false) {}
};
class TextWriter;
class TextWriterImpl
{
Q_DISABLE_COPY(TextWriterImpl)
// for QScopedResult only Q_DISABLE_COPY(TextWriterImpl)
public:
TextWriterImpl(const TextWriterImpl&) = default; // for QMovedResult only
Q_ENABLE_DEFAULT_MOVE(TextWriterImpl) // for QMovedResult only
operator bool() { return io; } // for QMovedResult only
TextWriterImpl(QIODevice* io) : io(io) { Q_ASSERT(io); }
protected:
template<class T_> friend class Val; // enables calling methods below through operator->()
......@@ -99,19 +92,31 @@ protected:
bool bind(const char* s) { io->write( s ); return true; }
// Val<TResult> prevents QBind from getting out() of an outer Seq for instance
template<typename T> bool bind(const T& t) { return QBind<TextWriter,T>::bind(TextWriter(this).value(), const_cast<T&>(t)); } // t will not be modified anyway
template<typename T> bool bind( T& t) { return QBind<TextWriter,T>::bind(TextWriter(this).value(), t ); }
template<typename T> bool bind( T&& t) { return QBind<TextWriter,T>::bind(TextWriter(this).value(), t ); }
template<typename T> bool bind(const T& t);
template<typename T> bool bind( T& t);
template<typename T> bool bind( T&& t);
template<class T_> friend class Seq; // enables calling methods below through operator->()
bool item() { io->write(" "); return true; }
bool out() { io->write("]"); return true; }
private:
QIODevice* io;
QIODevice* io = nullptr;
};
class TextWriter : public QMovedResult<TextWriter, TextWriterImpl, BindMode::Write>
{
Q_DISABLE_COPY(TextWriter)
public:
Q_ENABLE_DEFAULT_MOVE(TextWriter)
TextWriter(QIODevice* io) : QMovedResult(std::move(TextWriterImpl(io))) {} // QScopedResult(new TextWriterImpl(io), true) {}
private:
friend class TextWriterImpl; // uses method below
TextWriter(const TextWriterImpl& outer) : QMovedResult(outer) {} // QScopedResult(outer, false) {}
};
TextWriter::TextWriter(QIODevice* io) : QScopedResult(new TextWriterImpl(io), true) {}
template<typename T> bool TextWriterImpl::bind(const T& t) { return QBind<TextWriter,T>::bind(TextWriter(std::move(*this)).value(), const_cast<T&>(t)); } // t will not be modified anyway
template<typename T> bool TextWriterImpl::bind( T& t) { return QBind<TextWriter,T>::bind(TextWriter(std::move(*this)).value(), t ); }
template<typename T> bool TextWriterImpl::bind( T&& t) { return QBind<TextWriter,T>::bind(TextWriter(std::move(*this)).value(), t ); }
// //////////////////////////////////////////////////////////////////////////
// Tests and Benchmarks
......@@ -132,7 +137,7 @@ TextWriter::TextWriter(QIODevice* io) : QScopedResult(new TextWriterImpl(io), tr
groupWarmup=(previousTotal==0 || abs(groupTotal-previousTotal)*100/previousTotal > 1); \
previousTotal=groupTotal; \
groupTotal=0; \
if (!previousTotal) { fprintf(results,"group "); fprintf(samples, group " --------------------\n"); } else fprintf(results,"%-10s",group);
if (!previousTotal) { fprintf(results,"group "); fprintf(samples, "group |%s\n", group); } else fprintf(results,"%-10s",group);
#define GROUP_STOP \
if (!previousTotal) fprintf(results,"|total(usecs)|variation(%%)\n"); else fprintf(results,"|%12lld|%3lld\n", groupTotal, previousTotal ? abs(groupTotal-previousTotal)*100/previousTotal : 0); \
......@@ -142,7 +147,7 @@ QElapsedTimer item;
#define START item.start(); for (int i=0; i<123; ++i)
#define STOP(label,result) { \
auto usecs=item.nsecsElapsed()/1000/123; groupTotal+=usecs; \
if (!previousTotal) { fprintf(results,"|%16s", label); fprintf(samples, "%-16s|%16s\n", label, qPrintable(result)); } else fprintf(results,"|%16lld", usecs); }
if (!previousTotal) { fprintf(results,"|%16s", label); fprintf(samples, "%-16s|%s\n", label, qPrintable(result)); } else fprintf(results,"|%16lld", usecs); }
int main()
{
......@@ -192,7 +197,7 @@ int main()
.sequence()
.bind(123)
.bind(text)
;
.result(); // automatically closes opened sequence
}
STOP("Text",QString::fromUtf8(b.buffer()));
START {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment