Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <ostream> 7 : #include <vector> 8 : 9 : namespace CaptureForError_detail { 10 : class CaptureForErrorBase { 11 : protected: 12 : CaptureForErrorBase() = default; 13 : ~CaptureForErrorBase() = default; 14 : 15 : public: 16 : CaptureForErrorBase(CaptureForErrorBase&&) = delete; 17 : CaptureForErrorBase(const CaptureForErrorBase&) = delete; 18 : CaptureForErrorBase& operator=(CaptureForErrorBase&&) = delete; 19 : CaptureForErrorBase& operator=(const CaptureForErrorBase&) = delete; 20 : 21 : virtual void print(std::ostream& stream) const = 0; 22 : }; 23 : 24 : class CaptureList { 25 : public: 26 : static CaptureList& the_list(); 27 : 28 : void register_self(const CaptureForErrorBase* capture); 29 : void deregister_self(const CaptureForErrorBase* capture); 30 : 31 : void print(std::ostream& stream); 32 : 33 : CaptureList(CaptureList&&) = delete; 34 : CaptureList(const CaptureList&) = delete; 35 : CaptureList& operator=(CaptureList&&) = delete; 36 : CaptureList& operator=(const CaptureList&) = delete; 37 : 38 : private: 39 : CaptureList(); 40 : ~CaptureList() noexcept(false); 41 : 42 : std::vector<const CaptureForErrorBase*> captures_{}; 43 : bool currently_printing_ = false; 44 : }; 45 : 46 : #pragma GCC diagnostic push 47 : #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" 48 : template <typename T> 49 : // NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor) 50 : class CaptureForError : public CaptureForErrorBase { 51 : public: 52 : CaptureForError() = delete; 53 : CaptureForError(CaptureForError&&) = delete; 54 : CaptureForError(const CaptureForError&) = delete; 55 : CaptureForError& operator=(CaptureForError&&) = delete; 56 : CaptureForError& operator=(const CaptureForError&) = delete; 57 : 58 : CaptureForError(const char* const name, const T& capture) 59 : : name_(name), capture_(capture) { 60 : CaptureList::the_list().register_self(this); 61 : } 62 : 63 : // Capturing an rvalue would lead to a dangling reference. 64 : CaptureForError(const char* /*name*/, const T&& /*capture*/) = delete; 65 : 66 : ~CaptureForError() noexcept(false) { 67 : CaptureList::the_list().deregister_self(this); 68 : } 69 : 70 : void print(std::ostream& stream) const override { 71 : stream << name_ << " = " << capture_ << "\n"; 72 : } 73 : 74 : private: 75 : const char* name_; 76 : const T& capture_; 77 : }; 78 : #pragma GCC diagnostic pop 79 : } // namespace CaptureForError_detail 80 : 81 : /// \cond 82 : #define CAPTURE_FOR_ERROR_NAME2(line) capture_for_error_impl##line 83 : #define CAPTURE_FOR_ERROR_NAME(line) CAPTURE_FOR_ERROR_NAME2(line) 84 : /// \endcond 85 : 86 : /*! 87 : * \brief Capture a variable to be printed on ERROR or ASSERT. 88 : * 89 : * The argument will be printed using its stream operator if an error 90 : * occurs before the end of the current scope. The object is captured 91 : * by reference, so it must live until the end of the current scope, 92 : * and any subsequent changes to it will be reflected in the error 93 : * message. 94 : */ 95 1 : #define CAPTURE_FOR_ERROR(var) \ 96 : const CaptureForError_detail::CaptureForError CAPTURE_FOR_ERROR_NAME( \ 97 : __LINE__)(#var, var) 98 : 99 : /*! 100 : * \brief Stream all objects currently captured by CaptureForError. 101 : */ 102 1 : void print_captures_for_error(std::ostream& stream);