summaryrefslogtreecommitdiff
path: root/doctest/parts/doctest_fwd.h
diff options
context:
space:
mode:
authoronqtam <vik.kirilov@gmail.com>2017-03-26 15:24:11 +0300
committeronqtam <vik.kirilov@gmail.com>2017-05-16 00:22:16 +0300
commita369a98ea4c34bf1e1fde65c23bea6e9bced7261 (patch)
tree1c396c4cc29fd7029f4330ec9b0cc3194a414604 /doctest/parts/doctest_fwd.h
parent21e3ec6164823fc06cce3a6ba61b9ed39482fa9d (diff)
added INFO() and CAPTURE() logging macros - they can log variables and construct a message which gets printed only when an assert in the same scope (or in a nested one) after them fails. They use lazy stringification and the stack to avoid heap allocations and unnecessary string construction for the common case where no asserts fail. fixes #48 fixes #23
Diffstat (limited to 'doctest/parts/doctest_fwd.h')
-rw-r--r--doctest/parts/doctest_fwd.h188
1 files changed, 188 insertions, 0 deletions
diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h
index b538e29..c57f40a 100644
--- a/doctest/parts/doctest_fwd.h
+++ b/doctest/parts/doctest_fwd.h
@@ -45,6 +45,8 @@
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#pragma clang diagnostic ignored "-Wweak-vtables"
#pragma clang diagnostic ignored "-Wpadded"
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
@@ -58,7 +60,9 @@
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wstrict-overflow"
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#pragma GCC diagnostic ignored "-Wmissing-declarations"
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#pragma GCC diagnostic ignored "-Winline"
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
@@ -290,6 +294,12 @@
#define DOCTEST_INTERFACE
#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+// other
+
+#ifndef DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
+#define DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK 5
+#endif // DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
+
// =================================================================================================
// == FEATURE DETECTION END ========================================================================
// =================================================================================================
@@ -815,6 +825,7 @@ namespace detail
DOCTEST_INTERFACE void fastAssertThrowIfFlagSet(int flags);
DOCTEST_INTERFACE void throwException();
DOCTEST_INTERFACE bool always_false();
+ DOCTEST_INTERFACE void my_memcpy(void* dest, void* src, int num);
struct DOCTEST_INTERFACE SubcaseSignature
{
@@ -1253,7 +1264,175 @@ namespace detail
};
DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* translateFunction);
+
+ template <bool C>
+ struct StringStreamBase
+ {
+ template <typename T>
+ static std::ostream& convert(std::ostream& stream, const T& in) {
+ stream << toString(in);
+ return stream;
+ }
+ };
+
+ template <>
+ struct StringStreamBase<true>
+ {
+ template <typename T>
+ static std::ostream& convert(std::ostream& stream, const T& in) {
+ stream << in;
+ return stream;
+ }
+ };
+
+ template <typename T>
+ struct StringStream : StringStreamBase<has_insertion_operator<T>::value>
+ {};
+
+ template <typename T>
+ std::ostream& toStream(std::ostream& stream, const DOCTEST_REF_WRAP(T) value) {
+ return StringStream<T>::convert(stream, value);
+ }
+
+ struct IContextScope {
+ virtual void build(std::ostream&) const = 0;
+ };
+
+ DOCTEST_INTERFACE void addToContexts(IContextScope* ptr);
+ DOCTEST_INTERFACE void popFromContexts();
+
+ class ContextBuilder {
+ friend class ContextScope;
+
+ struct ICapture {
+ virtual std::ostream& toStream(std::ostream&) const = 0;
+ };
+
+ template<typename T>
+ struct Capture : ICapture {
+ const T* capture;
+
+ Capture(const T* in)
+ : capture(in)
+ {}
+ virtual std::ostream& toStream(std::ostream& stream) const { // override
+ doctest::detail::toStream(stream, *capture);
+ return stream;
+ }
+ };
+
+ struct Chunk {
+ char buf[sizeof(Capture<char>)]; // place to construct a Capture<T>
+ };
+ struct Node {
+ Chunk chunk;
+ Node* next;
+ };
+
+ Chunk stackChunks[DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK];
+ int numCaptures;
+ Node* head;
+ Node* tail;
+
+ void build(std::ostream& stream) const {
+ int curr = 0;
+ // iterate over small buffer
+ while(curr < numCaptures && curr < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK)
+ reinterpret_cast<const ICapture*>(stackChunks[curr++].buf)->toStream(stream);
+ // iterate over list
+ Node* curr_elem = head;
+ while(curr < numCaptures) {
+ reinterpret_cast<const ICapture*>(curr_elem->chunk.buf)->toStream(stream);
+ curr_elem = curr_elem->next;
+ ++curr;
+ }
+ }
+
+ // steal the contents of the other - acting as a move constructor...
+ ContextBuilder(ContextBuilder& other)
+ : numCaptures(other.numCaptures)
+ , head(other.head)
+ , tail(other.tail)
+ {
+ other.numCaptures = 0;
+ other.head = 0;
+ other.tail = 0;
+ my_memcpy(stackChunks, other.stackChunks, sizeof(Chunk) * DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK);
+ }
+
+ public:
+
+ ContextBuilder()
+ : numCaptures(0)
+ , head(0)
+ , tail(0)
+ {}
+
+ template<typename T>
+ ContextBuilder& operator<<(const T& in) {
+ Capture<T> temp(&in);
+
+ // construct either on stack or on heap
+ // copy the bytes for the whole object - including the vtable because we cant construct
+ // the object directly in the buffer using placement new - need the <new> header...
+ if(numCaptures < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK) {
+ my_memcpy(stackChunks[numCaptures].buf, &temp, sizeof(Chunk));
+ } else {
+ Node* curr = new Node;
+ curr->next = 0;
+ if(tail) {
+ tail->next = curr;
+ tail = curr;
+ } else {
+ head = tail = curr;
+ }
+
+ my_memcpy(tail->chunk.buf, &temp, sizeof(Chunk));
+ }
+ ++numCaptures;
+ return *this;
+ }
+
+ ~ContextBuilder() {
+ // free the linked list - the ones on the stack are left as-is
+ // no destructors are called at all - there is no need
+ while(head) {
+ Node* next = head->next;
+ delete head;
+ head = next;
+ }
+ }
+
+#ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#ifdef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+ template<typename T>
+ ContextBuilder& operator<<(const T&&) = delete;
+#else // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+ private:
+ template<typename T>
+ ContextBuilder& operator<<(const T&&);
+#endif // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+ };
+
+ class ContextScope : public IContextScope {
+ ContextBuilder contextBuilder;
+ public:
+ ContextScope(ContextBuilder& temp)
+ : contextBuilder(temp)
+ {
+ addToContexts(this);
+ }
+
+ ~ContextScope() {
+ popFromContexts();
+ }
+
+ void build(std::ostream& stream) const {
+ contextBuilder.build(stream);
+ }
+ };
} // namespace detail
#endif // DOCTEST_CONFIG_DISABLE
@@ -1392,6 +1571,10 @@ public:
#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), signature)
+// for logging
+#define DOCTEST_INFO(x) doctest::detail::ContextScope DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_)(doctest::detail::ContextBuilder() << x)
+#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x)
+
// common code in asserts - for convenience
#define DOCTEST_ASSERT_LOG_AND_REACT(rb) \
if(rb.log()) \
@@ -1803,6 +1986,9 @@ public:
template <typename T> \
static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature)
+#define DOCTEST_INFO(x) ((void)0)
+#define DOCTEST_CAPTURE(x) ((void)0)
+
#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
#define DOCTEST_WARN(...) ((void)0)
#define DOCTEST_CHECK(...) ((void)0)
@@ -1967,6 +2153,8 @@ public:
#define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN
#define TEST_SUITE_END DOCTEST_TEST_SUITE_END
#define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR
+#define INFO DOCTEST_INFO
+#define CAPTURE DOCTEST_CAPTURE
#define WARN DOCTEST_WARN
#define WARN_FALSE DOCTEST_WARN_FALSE