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);
|