Commit 071fe4a0e5c7e6abd6725552d1bf0b6119bce1c9
Committed by
GitHub
Merge pull request #985 from m-holger/members
Change JSONHandler::m to std::unique_ptr and declare Members in implementation file
Showing
4 changed files
with
77 additions
and
72 deletions
README-maintainer.md
| ... | ... | @@ -235,16 +235,26 @@ Building docs from pull requests is also enabled. |
| 235 | 235 | // README-maintainer |
| 236 | 236 | ``` |
| 237 | 237 | |
| 238 | -* Put private member variables in std::shared_ptr<Members> for all | |
| 239 | - public classes. Remember to use QPDF_DLL on ~Members(). Exception: | |
| 240 | - indirection through std::shared_ptr<Members> is expensive, so don't | |
| 241 | - do it for classes that are copied a lot, like QPDFObjectHandle and | |
| 242 | - QPDFObject. It may be possible to declare | |
| 243 | - std::shared_ptr<Members> m_ph; | |
| 244 | - Member* m; | |
| 245 | - with m = m_ph.get(), and then indirect through m in | |
| 246 | - performance-critical settings, though in 2022, std::shared_ptr is | |
| 247 | - sufficiently performant that this may not be worth it. | |
| 238 | +* Put private member variables in std::unique_ptr<Members> for all | |
| 239 | + public classes. Forward declare Members in the header file and define | |
| 240 | + Members in the implementation file. One of the major benefits of | |
| 241 | + defining Members in the implementation file is that it makes it easier | |
| 242 | + to use private classes as data members and simplifies the include order. | |
| 243 | + Remember that Members must be fully defined before the destructor of the | |
| 244 | + main class. For an example of this pattern see class JSONHandler. | |
| 245 | + | |
| 246 | + Exception: indirection through std::unique_ptr<Members> incurs an overhead, | |
| 247 | + so don't do it for: | |
| 248 | + * (especially private) classes that are copied a lot, like QPDFObjectHandle | |
| 249 | + and QPDFObject. | |
| 250 | + * classes that are a shared pointer to another class, such as QPDFObjectHandle | |
| 251 | + or JSON. | |
| 252 | + | |
| 253 | + For exported classes that do not use the member pattern for performance | |
| 254 | + reasons it is worth considering adding a std::unique_ptr to an empty Members | |
| 255 | + class initialized to nullptr to give the flexibility to add data members | |
| 256 | + without breaking the ABI. | |
| 257 | + | |
| 248 | 258 | |
| 249 | 259 | * Traversal of objects is expensive. It's worth adding some complexity |
| 250 | 260 | to avoid needless traversals of objects. | ... | ... |
libqpdf/JSONHandler.cc
| ... | ... | @@ -4,11 +4,48 @@ |
| 4 | 4 | #include <qpdf/QTC.hh> |
| 5 | 5 | #include <qpdf/QUtil.hh> |
| 6 | 6 | |
| 7 | +struct Handlers | |
| 8 | +{ | |
| 9 | + Handlers() = default; | |
| 10 | + | |
| 11 | + JSONHandler::json_handler_t any_handler{nullptr}; | |
| 12 | + JSONHandler::void_handler_t null_handler{nullptr}; | |
| 13 | + JSONHandler::string_handler_t string_handler{nullptr}; | |
| 14 | + JSONHandler::string_handler_t number_handler{nullptr}; | |
| 15 | + JSONHandler::bool_handler_t bool_handler{nullptr}; | |
| 16 | + JSONHandler::json_handler_t dict_start_handler{nullptr}; | |
| 17 | + JSONHandler::void_handler_t dict_end_handler{nullptr}; | |
| 18 | + JSONHandler::json_handler_t array_start_handler{nullptr}; | |
| 19 | + JSONHandler::void_handler_t array_end_handler{nullptr}; | |
| 20 | + JSONHandler::void_handler_t final_handler{nullptr}; | |
| 21 | + std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers; | |
| 22 | + std::shared_ptr<JSONHandler> fallback_dict_handler; | |
| 23 | + std::shared_ptr<JSONHandler> array_item_handler; | |
| 24 | +}; | |
| 25 | + | |
| 26 | +class JSONHandler::Members | |
| 27 | +{ | |
| 28 | + friend class JSONHandler; | |
| 29 | + | |
| 30 | + public: | |
| 31 | + ~Members() = default; | |
| 32 | + | |
| 33 | + private: | |
| 34 | + Members() = default; | |
| 35 | + Members(Members const&) = delete; | |
| 36 | + | |
| 37 | + Handlers h; | |
| 38 | +}; | |
| 39 | + | |
| 7 | 40 | JSONHandler::JSONHandler() : |
| 8 | 41 | m(new Members()) |
| 9 | 42 | { |
| 10 | 43 | } |
| 11 | 44 | |
| 45 | +JSONHandler::~JSONHandler() | |
| 46 | +{ | |
| 47 | +} | |
| 48 | + | |
| 12 | 49 | void |
| 13 | 50 | JSONHandler::usage(std::string const& msg) |
| 14 | 51 | { |
| ... | ... | @@ -80,24 +117,24 @@ JSONHandler::handle(std::string const& path, JSON j) |
| 80 | 117 | m->h.any_handler(path, j); |
| 81 | 118 | return; |
| 82 | 119 | } |
| 83 | - bool handled = false; | |
| 120 | + | |
| 84 | 121 | bool bvalue = false; |
| 85 | 122 | std::string s_value; |
| 86 | 123 | if (m->h.null_handler && j.isNull()) { |
| 87 | 124 | m->h.null_handler(path); |
| 88 | - handled = true; | |
| 125 | + return; | |
| 89 | 126 | } |
| 90 | 127 | if (m->h.string_handler && j.getString(s_value)) { |
| 91 | 128 | m->h.string_handler(path, s_value); |
| 92 | - handled = true; | |
| 129 | + return; | |
| 93 | 130 | } |
| 94 | 131 | if (m->h.number_handler && j.getNumber(s_value)) { |
| 95 | 132 | m->h.number_handler(path, s_value); |
| 96 | - handled = true; | |
| 133 | + return; | |
| 97 | 134 | } |
| 98 | 135 | if (m->h.bool_handler && j.getBool(bvalue)) { |
| 99 | 136 | m->h.bool_handler(path, bvalue); |
| 100 | - handled = true; | |
| 137 | + return; | |
| 101 | 138 | } |
| 102 | 139 | if (m->h.dict_start_handler && j.isDictionary()) { |
| 103 | 140 | m->h.dict_start_handler(path, j); |
| ... | ... | @@ -119,7 +156,7 @@ JSONHandler::handle(std::string const& path, JSON j) |
| 119 | 156 | } |
| 120 | 157 | }); |
| 121 | 158 | m->h.dict_end_handler(path); |
| 122 | - handled = true; | |
| 159 | + return; | |
| 123 | 160 | } |
| 124 | 161 | if (m->h.array_start_handler && j.isArray()) { |
| 125 | 162 | m->h.array_start_handler(path, j); |
| ... | ... | @@ -129,15 +166,13 @@ JSONHandler::handle(std::string const& path, JSON j) |
| 129 | 166 | ++i; |
| 130 | 167 | }); |
| 131 | 168 | m->h.array_end_handler(path); |
| 132 | - handled = true; | |
| 169 | + return; | |
| 133 | 170 | } |
| 134 | 171 | |
| 135 | - if (!handled) { | |
| 136 | - // It would be nice to include information about what type the object was and what types | |
| 137 | - // were allowed, but we're relying on schema validation to make sure input is properly | |
| 138 | - // structured before calling the handlers. It would be different if this code were trying to | |
| 139 | - // be part of a general-purpose JSON package. | |
| 140 | - QTC::TC("libtests", "JSONHandler unhandled value"); | |
| 141 | - usage("JSON handler: value at " + path + " is not of expected type"); | |
| 142 | - } | |
| 172 | + // It would be nice to include information about what type the object was and what types were | |
| 173 | + // allowed, but we're relying on schema validation to make sure input is properly structured | |
| 174 | + // before calling the handlers. It would be different if this code were trying to be part of a | |
| 175 | + // general-purpose JSON package. | |
| 176 | + QTC::TC("libtests", "JSONHandler unhandled value"); | |
| 177 | + usage("JSON handler: value at " + path + " is not of expected type"); | |
| 143 | 178 | } | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -1482,7 +1482,8 @@ QPDF::readObjectAtOffset( |
| 1482 | 1482 | |
| 1483 | 1483 | // Special case: if offset is 0, just return null. Some PDF writers, in particular |
| 1484 | 1484 | // "Mac OS X 10.7.5 Quartz PDFContext", may store deleted objects in the xref table as |
| 1485 | - // "0000000000 00000 n", which is not correct, but it won't hurt anything for to ignore these. | |
| 1485 | + // "0000000000 00000 n", which is not correct, but it won't hurt anything for us to ignore | |
| 1486 | + // these. | |
| 1486 | 1487 | if (offset == 0) { |
| 1487 | 1488 | QTC::TC("qpdf", "QPDF bogus 0 offset", 0); |
| 1488 | 1489 | warn(damagedPDF(0, "object has offset 0")); | ... | ... |
libqpdf/qpdf/JSONHandler.hh
| ... | ... | @@ -16,7 +16,7 @@ class JSONHandler |
| 16 | 16 | public: |
| 17 | 17 | // A QPDFUsage exception is thrown if there are any errors validating the JSON object. |
| 18 | 18 | JSONHandler(); |
| 19 | - ~JSONHandler() = default; | |
| 19 | + ~JSONHandler(); | |
| 20 | 20 | |
| 21 | 21 | // Based on the type of handler, expect the object to be of a certain type. QPDFUsage is thrown |
| 22 | 22 | // otherwise. Multiple handlers may be registered, which allows the object to be of various |
| ... | ... | @@ -53,51 +53,10 @@ class JSONHandler |
| 53 | 53 | |
| 54 | 54 | static void usage(std::string const& msg); |
| 55 | 55 | |
| 56 | - struct Handlers | |
| 57 | - { | |
| 58 | - Handlers() : | |
| 59 | - any_handler(nullptr), | |
| 60 | - null_handler(nullptr), | |
| 61 | - string_handler(nullptr), | |
| 62 | - number_handler(nullptr), | |
| 63 | - bool_handler(nullptr), | |
| 64 | - dict_start_handler(nullptr), | |
| 65 | - dict_end_handler(nullptr), | |
| 66 | - array_start_handler(nullptr), | |
| 67 | - array_end_handler(nullptr), | |
| 68 | - final_handler(nullptr) | |
| 69 | - { | |
| 70 | - } | |
| 71 | - | |
| 72 | - json_handler_t any_handler; | |
| 73 | - void_handler_t null_handler; | |
| 74 | - string_handler_t string_handler; | |
| 75 | - string_handler_t number_handler; | |
| 76 | - bool_handler_t bool_handler; | |
| 77 | - json_handler_t dict_start_handler; | |
| 78 | - void_handler_t dict_end_handler; | |
| 79 | - json_handler_t array_start_handler; | |
| 80 | - void_handler_t array_end_handler; | |
| 81 | - void_handler_t final_handler; | |
| 82 | - std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers; | |
| 83 | - std::shared_ptr<JSONHandler> fallback_dict_handler; | |
| 84 | - std::shared_ptr<JSONHandler> array_item_handler; | |
| 85 | - }; | |
| 86 | - | |
| 87 | - class Members | |
| 88 | - { | |
| 89 | - friend class JSONHandler; | |
| 90 | - | |
| 91 | - public: | |
| 92 | - ~Members() = default; | |
| 93 | - | |
| 94 | - private: | |
| 95 | - Members() = default; | |
| 96 | - Members(Members const&) = delete; | |
| 97 | - | |
| 98 | - Handlers h; | |
| 99 | - }; | |
| 100 | - std::shared_ptr<Members> m; | |
| 56 | + | |
| 57 | + class Members; | |
| 58 | + | |
| 59 | + std::unique_ptr<Members> m; | |
| 101 | 60 | }; |
| 102 | 61 | |
| 103 | 62 | #endif // JSONHANDLER_HH | ... | ... |