{"id":403358,"date":"2024-06-29T17:04:45","date_gmt":"2024-06-29T17:04:45","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=403358"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T17:04:45","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=403358","title":{"rendered":"<span>Checking a Header-Only C++ Library Collection (awesome-hpp)<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/141\/300\/658\/141300658daf1fe0d2203c866e74a463.png\" alt=\"PVS-Studio and Awesome hpp\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/141\/300\/658\/141300658daf1fe0d2203c866e74a463.png\"\/><\/div>\n<p>  Somehow, we&#8217;ve happened to check most of the libraries making up a collection called &#171;Awesome hpp&#187;. These are small header-only projects in C++. Hopefully, the information about the bugs we&#8217;ve found will help make the libraries better. We&#8217;ll also be happy to see the developers use PVS-Studio on a regular basis thanks to the free-license option we provide.<br \/>  <a name=\"habracut\"><\/a><br \/>  What follows is an overview of bugs found in the different libraries on the curated list of awesome header-only C++ libraries: <a href=\"https:\/\/github.com\/p-ranav\/awesome-hpp\">awesome-hpp<\/a>.<\/p>\n<p>  I learned about this list from the &#171;<a href=\"https:\/\/cppcast.com\/telephony-dave-hagedorn\/\">Cross Platform Mobile Telephony<\/a>&#187; podcast. While we&#8217;re at it, I recommend that all C++ programmers check out <a href=\"https:\/\/cppcast.com\/\">CppCast<\/a>. It is the first podcast for C++ developers by C++ developers!<\/p>\n<p>  Despite the large number of projects making up the list, bugs were few. There are three reasons for that:<\/p>\n<ul>\n<li>The projects are tiny. Many literally consist of just a header file.<\/li>\n<li>We decided to skip some of the projects as they were too troublesome to compile.<\/li>\n<li>Often, you can&#8217;t figure out whether template classes\/functions have bugs in them until you have them instantiated. Consequently, many of the bugs can be detected only in a real project that makes an active use of a library. As for our analysis method, we simply included the headers into an empty .cpp file and ran the check, which drastically limited its effectiveness.<\/li>\n<\/ul>\n<p>  Even so, we&#8217;ve gathered enough warnings to write the article you are reading right now and a couple of additional ones.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">A note for my teammates :).<\/b>                         <\/p>\n<div class=\"spoiler_text\">When I do something, I like to set and achieve a number of goals at once, and I urge you to follow my example. After learning about the awesome-hpp collection, I managed to accomplish the following useful tasks:  <\/p>\n<ul>\n<li>set a task for our team to include into our test database a number of nice small modern projects based on the C++11, C++14, and C++17 standards;<\/li>\n<li>wrote the article &#171;<a href=\"https:\/\/www.viva64.com\/en\/b\/0762\/\">Why it is important to apply static analysis for open libraries that you add to your project<\/a>&#171;;<\/li>\n<li>wrote the article &#171;<a href=\"https:\/\/www.viva64.com\/en\/b\/0761\/\">Why code reviews are good, but not enough<\/a>&#171;;<\/li>\n<li>wrote this article;<\/li>\n<li>outlined a future post about argument evaluation order (see CSV Parser);<\/li>\n<li>figured out a way to find out which of my teammates read my posts carefully. If you have read this, drop by and get a prize \u2013 a chocolate bar or something like that :);<\/li>\n<li>demonstrated with this example how to accomplish several tasks.<\/li>\n<\/ul>\n<\/div><\/div>\n<p>  <b>A note for library developers.<\/b> You can use PVS-Studio to check open-source projects for free. To get a free license to use with your open-source project, please fill in this <a href=\"https:\/\/www.viva64.com\/en\/open-source-license\/\">form<\/a>.<\/p>\n<p>  Okay, let&#8217;s move on to our overview.<\/p>\n<h2>Bugs found<\/h2>\n<p>  <\/p>\n<h3>iutest<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/srz-zumix\/iutest\">iutest<\/a> library:<\/p>\n<blockquote><p>iutest is framework for writing C++ tests.<\/p><\/blockquote>\n<pre><code class=\"cpp\">template&lt;typename Event> pool_handler&lt;Event> &amp; assure() {   ....   return static_cast&lt;pool_handler&lt;Event> &amp;>(it == pools.cend() ?     *pools.emplace_back(new pool_handler&lt;Event>{}) : **it);   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v1023\/\">V1023<\/a> A pointer without owner is added to the &#8216;pools&#8217; container by the &#8217;emplace_back&#8217; method. A memory leak will occur in case of an exception. entt.hpp 17114<\/p>\n<p>  This code may end up with a memory leak. If the container needs reallocation and fails to allocate storage for a new array, it will throw an exception and the pointer will be lost.<\/p>\n<p>  Well, when found in tests, this kind of bug isn&#8217;t very critical and likely to occur, but I still had to mention it for educational purposes :).<\/p>\n<p>  Fixed code:<\/p>\n<pre><code class=\"cpp\">pools.emplace_back(std::make_unique&lt;pool_handler&lt;Event>>{})<\/code><\/pre>\n<p>  Another problem spot: V1023 A pointer without owner is added to the &#8216;pools&#8217; container by the &#8217;emplace_back&#8217; method. A memory leak will occur in case of an exception. entt.hpp 17407<\/p>\n<h3>jsoncons<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/danielaparker\/jsoncons\">jsoncons<\/a> library:<\/p>\n<blockquote><p>A C++, header-only library for constructing JSON and JSON-like data formats, with JSON Pointer, JSON Patch, JSONPath, JMESPath, CSV, MessagePack, CBOR, BSON, UBJSON.<\/p><\/blockquote>\n<p><b>Bug 1<\/b><\/p>\n<pre><code class=\"cpp\">static constexpr uint64_t basic_type_bits = sizeof(uint64_t) * 8;  uint64_t* data()  {   return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; }  basic_bigint&amp; operator&lt;&lt;=( uint64_t k ) {   size_type q = (size_type)(k \/ basic_type_bits);   ....   if ( k )  \/\/ 0 &lt; k &lt; basic_type_bits:   {     uint64_t k1 = basic_type_bits - k;     uint64_t mask = (1 &lt;&lt; k) - 1;             \/\/ &lt;=     ....     data()[i] |= (data()[i-1] >> k1) &amp; mask;     ....   }   reduce();   return *this; }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v629\/\">V629<\/a> Consider inspecting the &#8216;1 &lt;&lt; k&#8217; expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. bigint.hpp 744<\/p>\n<p>  This bug was already discussed in detail in the article &#171;<a href=\"https:\/\/www.viva64.com\/en\/b\/0762\/\">Why it is important to apply static analysis for open libraries that you add to your project<\/a>&#171;. In a nutshell, to get correct values of the mask, you should write the following:<\/p>\n<pre><code class=\"cpp\">uint64_t mask = (static_cast&lt;uint64_t>(1) &lt;&lt; k) - 1;<\/code><\/pre>\n<p>  Here&#8217;s an alternative version:<\/p>\n<pre><code class=\"cpp\">uint64_t mask = (1ull &lt;&lt; k) - 1;<\/code><\/pre>\n<p>  Another similar bug was found here: <a href=\"https:\/\/www.viva64.com\/en\/w\/v629\/\">V629<\/a> Consider inspecting the &#8216;1 &lt;&lt; k&#8217; expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. bigint.hpp 779<\/p>\n<p>  <b>Bug 2<\/b><\/p>\n<pre><code class=\"cpp\">template &lt;class CharT = typename std::iterator_traits&lt;Iterator>::value_type> typename std::enable_if&lt;sizeof(CharT) == sizeof(uint16_t)>::type  next() UNICONS_NOEXCEPT {     begin_ += length_;     if (begin_ != last_)     {         if (begin_ != last_)         {   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v571\/\">V571<\/a> Recurring check. The &#8216;if (begin_ != last_)&#8217; condition was already verified in line 1138. unicode_traits.hpp 1140<\/p>\n<p>  This is a strange duplicate check. I suspect the second condition contains a typo and was meant to check some other value.<\/p>\n<h3>clipp<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/muellan\/clipp\">clipp<\/a> library:<\/p>\n<blockquote><p>clipp \u2014 command line interfaces for modern C++. Easy to use, powerful and expressive command line argument handling for C++11\/14\/17 contained in a single header file.<\/p><\/blockquote>\n<pre><code class=\"cpp\">inline bool fwd_to_unsigned_int(const char*&amp; s) {   if(!s) return false;   for(; std::isspace(*s); ++s);   if(!s[0] || s[0] == '-') return false;   if(s[0] == '-') return false;   return true; }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;s[0] == &#8216;-&#187; is always false. clipp.h 303<\/p>\n<p>  This one isn&#8217;t actually a bug \u2013 just redundant code. The element is checked twice for being the minus character.<\/p>\n<h3>SimpleIni<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/brofield\/simpleini\">SimpleIni<\/a> library:<\/p>\n<blockquote><p>A cross-platform library that provides a simple API to read and write INI-style configuration files. It supports data files in ASCII, MBCS and Unicode.<\/p><\/blockquote>\n<pre><code class=\"cpp\">#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) &amp;&amp; !defined(_linux))<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v1040\/\">V1040<\/a> Possible typo in the spelling of a pre-defined macro name. The &#8216;_linux&#8217; macro is similar to &#8216;__linux&#8217;. SimpleIni.h 2923<\/p>\n<p>  It seems an underscore is missing in the <i>_linux<\/i> macro&#8217;s name: <i>__linux<\/i>. Anyway, this macro is deprecated in POSIX, so you should use <i>__linux__<\/i> instead.<\/p>\n<h3>CSV Parser<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/vincentlaucsb\/csv-parser\">CSV Parser<\/a> library:<\/p>\n<blockquote><p>A modern C++ library for reading, writing, and analyzing CSV (and similar) files.<\/p><\/blockquote>\n<pre><code class=\"cpp\">CSV_INLINE void CSVReader::read_csv(const size_t&amp; bytes) {   const size_t BUFFER_UPPER_LIMIT = std::min(bytes, (size_t)1000000);   std::unique_ptr&lt;char[]> buffer(new char[BUFFER_UPPER_LIMIT]);   auto * HEDLEY_RESTRICT line_buffer = buffer.get();   line_buffer[0] = '\\0';   ....   this->feed_state->feed_buffer.push_back(     std::make_pair&lt;>(std::move(buffer), line_buffer - buffer.get())); \/\/ &lt;=   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v769\/\">V769<\/a> The &#8216;buffer.get()&#8217; pointer in the &#8216;line_buffer \u2014 buffer.get()&#8217; expression equals nullptr. The resulting value is senseless and it should not be used. csv.hpp 4957<\/p>\n<p>  It&#8217;s an interesting case, which calls for careful investigation, so I decided to write a small separate post about it. Besides, while experimenting with similar code, I discovered a flaw in the code of PVS-Studio itself :). Because of that flaw, the analyzer will sometimes keep silent when it must issue a warning.<\/p>\n<p>  In a nutshell, whether or not this code will work depends on the order of argument evaluation \u2013 and that depends on the compiler.<\/p>\n<h3>PPrint<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/p-ranav\/pprint\">PPrint<\/a> library:<\/p>\n<blockquote><p>Pretty Printer for Modern C++.<\/p><\/blockquote>\n<pre><code class=\"cpp\">template &lt;typename Container> typename std::enable_if&lt;......>::type print_internal(......) {   ....   for (size_t i = 1; i &lt; value.size() - 1; i++) {     print_internal(value[i], indent + indent_, \"\", level + 1);     if (is_container&lt;T>::value == false)       print_internal_without_quotes(\", \", 0, \"\\n\");     else       print_internal_without_quotes(\", \", 0, \"\\n\");   }   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v523\/\">V523<\/a> The &#8216;then&#8217; statement is equivalent to the &#8216;else&#8217; statement. pprint.hpp 715<\/p>\n<p>  It&#8217;s very strange to have the same logic executed regardless of the check&#8217;s result. No clarifying comment is provided either. It looks very much like a copy-paste mistake.<\/p>\n<p>  Similar warnings:<\/p>\n<ul>\n<li>V523 The &#8216;then&#8217; statement is equivalent to the &#8216;else&#8217; statement. pprint.hpp 780<\/li>\n<li>V523 The &#8216;then&#8217; statement is equivalent to the &#8216;else&#8217; statement. pprint.hpp 851<\/li>\n<li>V523 The &#8216;then&#8217; statement is equivalent to the &#8216;else&#8217; statement. pprint.hpp 927<\/li>\n<li>V523 The &#8216;then&#8217; statement is equivalent to the &#8216;else&#8217; statement. pprint.hpp 1012<\/li>\n<\/ul>\n<p>  <\/p>\n<h3>Strf<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/robhz786\/strf\">Strf<\/a> library:<\/p>\n<blockquote><p>A fast C++ formatting library that supports encoding conversion.<\/p><\/blockquote>\n<p><b>Bug 1<\/b><\/p>\n<pre><code class=\"cpp\">template &lt;int Base> class numpunct: private strf::digits_grouping {   ....   constexpr STRF_HD numpunct&amp; operator=(const numpunct&amp; other) noexcept   {     strf::digits_grouping::operator=(other);     decimal_point_ = other.decimal_point_;     thousands_sep_ = other.thousands_sep_;   }   .... };<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v591\/\">V591<\/a> Non-void function should return a value. numpunct.hpp 402<\/p>\n<p>  The &#171;return *this;&#187; statement is missing at the end.<\/p>\n<p>  <b>Bug 2 (of the same nature)<\/b><\/p>\n<pre><code class=\"cpp\">template &lt;int Base> class no_grouping final {   constexpr STRF_HD no_grouping&amp; operator=(const no_grouping&amp; other) noexcept   {     decimal_point_ = other.decimal_point_;   }   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v591\/\">V591<\/a> Non-void function should return a value. numpunct.hpp 528.<\/p>\n<h3>Indicators<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/p-ranav\/indicators\">Indicators<\/a> library:<\/p>\n<blockquote><p>Activity Indicators for Modern C++.<\/p><\/blockquote>\n<pre><code class=\"cpp\">static inline void move_up(int lines) { move(0, -lines); } static inline void move_down(int lines) { move(0, -lines); }   \/\/ &lt;= static inline void move_right(int cols) { move(cols, 0); } static inline void move_left(int cols) { move(-cols, 0); }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v524\/\">V524<\/a> It is odd that the body of &#8216;move_down&#8217; function is fully equivalent to the body of &#8216;move_up&#8217; function. indicators.hpp 983<\/p>\n<p>  I&#8217;m not sure this one is an error, but the code is highly suspicious. The developer must have copied the <i>move_up<\/i> function and changed the copy&#8217;s name to <i>move_down<\/i> but forgotten to delete the minus character. In any case, this snippet needs checking.<\/p>\n<p>  <b>Note.<\/b> If the code is correct, you should understand that it&#8217;s going to confuse not only static analyzers but third-party developers as well, who may want to use or develop it. Make sure you leave a comment to clarify your point.<\/p>\n<h3>manif<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/artivis\/manif\">manif<\/a> library:<\/p>\n<blockquote><p>manif is a header-only C++11 Lie theory library for state-estimation targeted at robotics applications.<\/p><\/blockquote>\n<pre><code class=\"cpp\">template &lt;typename _Derived> typename LieGroupBase&lt;_Derived>::Scalar* LieGroupBase&lt;_Derived>::data() {   return derived().coeffs().data(); }  template &lt;typename _Derived> const typename LieGroupBase&lt;_Derived>::Scalar* LieGroupBase&lt;_Derived>::data() const {   derived().coeffs().data(); \/\/ &lt;= }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v591\/\">V591<\/a> Non-void function should return a value. lie_group_base.h 347<\/p>\n<p>  The non-constant function is implemented properly, while the constant one is not. I wonder how it came to be so\u2026<\/p>\n<h3>FakeIt<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/eranpeer\/FakeIt\">FakeIt<\/a> library:<\/p>\n<blockquote><p>FakeIt is a simple mocking framework for C++. It supports GCC, Clang and MS Visual C++. FakeIt is written in C++11 and can be used for testing both C++11 and C++ projects.<\/p><\/blockquote>\n<pre><code class=\"cpp\">template&lt;typename ... arglist> struct ArgumentsMatcherInvocationMatcher :          public ActualInvocation&lt;arglist...>::Matcher {   ....   template&lt;typename A>   void operator()(int index, A &amp;actualArg) {       TypedMatcher&lt;typename naked_type&lt;A>::type> *matcher =         dynamic_cast&lt;TypedMatcher&lt;typename naked_type&lt;A>::type> *>(           _matchers[index]);       if (_matching)         _matching = matcher->matches(actualArg);   }   ....   const std::vector&lt;Destructible *> _matchers; };<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v522\/\">V522<\/a> There might be dereferencing of a potential null pointer &#8216;matcher&#8217;. fakeit.hpp 6720<\/p>\n<p>  The <i>matcher<\/i> pointer is initialized with the value returned by <i>dynamic_cast<\/i>. Yet that operator can return <i>nullptr,<\/i> which is very likely. Otherwise, use the more efficient <i>static_cast <\/i>instead of <i>dynamic_cast<\/i>. <\/p>\n<p>  I suspect the condition contains a typo and was actually meant to look like this:<\/p>\n<pre><code class=\"cpp\">if (matcher)   _matching = matcher->matches(actualArg);<\/code><\/pre>\n<p>  <\/p>\n<h3>GuiLite<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/idea4good\/GuiLite\">GuiLite<\/a> library:<\/p>\n<blockquote><p>The smallest header-only GUI library(4 KLOC) for all platforms.<\/p><\/blockquote>\n<pre><code class=\"cpp\">#define CORRECT(x, high_limit, low_limit)  {\\   x = (x > high_limit) ? high_limit : x;\\   x = (x &lt; low_limit) ? low_limit : x;\\ }while(0)  void refresh_wave(unsigned char frame) {   ....   CORRECT(y_min, m_wave_bottom, m_wave_top);   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v529\/\">V529<\/a> Odd semicolon &#8216;;&#8217; after &#8216;while&#8217; operator. GuiLite.h 3413<\/p>\n<p>  This error in the macro doesn&#8217;t cause any specific problem, but it&#8217;s still an error, so I included it.<\/p>\n<p>  The macro was meant to use the classic pattern <i>do {\u2026 } while(&#8230;.)<\/i>. This allows executing several operations in one block while enabling you to write a nice semicolon after the macro as if it were a function call.<\/p>\n<p>  This macro, however, lacks the <i>do <\/i>keyword. As a result, it splits into two parts, so to speak: a block of code and an empty, never-running <i>while (0); <\/i>loop.<\/p>\n<p>  But why is it bad, actually?<\/p>\n<p>  Well, for one thing, you can&#8217;t use this macro in constructs like this:<\/p>\n<pre><code class=\"cpp\">if (A)   CORRECT(y_min, m_wave_bottom, m_wave_top); else   Foo();<\/code><\/pre>\n<p>  This code won&#8217;t compile as it will expand into the following:<\/p>\n<pre><code class=\"cpp\">if (A)   { ..... } while(0); else   Foo();<\/code><\/pre>\n<p>  I think you&#8217;ll agree that it&#8217;s better to find and fix defects like that while the library is still in development than after the release. To ensure this, use static analysis :).<\/p>\n<p>  <a href=\"https:\/\/viva64.com\/en\/pvs-studio-download\/?utm_source=habr&amp;utm_medium=banner&amp;utm_campaign=0770_check_awesome_hpp\"><\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/42b\/757\/14b\/42b75714b45fa154c5497bdb32b2c568.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/42b\/757\/14b\/42b75714b45fa154c5497bdb32b2c568.png\"\/><\/div>\n<p><\/a>  <\/p>\n<h3>PpluX<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/pplux\/px\">PpluX<\/a> library:<\/p>\n<blockquote><p>Single header C++ Libraries for Thread Scheduling, Rendering, and so on&#8230;<\/p><\/blockquote>\n<pre><code class=\"cpp\">struct DisplayList {   DisplayList&amp; operator=(DisplayList &amp;&amp;d) {     data_ = d.data_;     d.data_ = nullptr;   }   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v591\/\">V591<\/a> Non-void function should return a value. px_render.h 398<\/p>\n<h3>Universal<\/h3>\n<p>  Brief description of the Universal library:<\/p>\n<blockquote><p>The goal of Universal Numbers, or unums, is to replace IEEE floating-point with a number system that is more efficient and mathematically consistent in concurrent execution environments.<\/p><\/blockquote>\n<p><b>Bug 1<\/b><\/p>\n<pre><code class=\"cpp\">template&lt;typename Scalar> vector&lt;Scalar> operator*(double scalar, const vector&lt;Scalar>&amp; v) {   vector&lt;Scalar> scaledVector(v);   scaledVector *= scalar;   return v; }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v1001\/\">V1001<\/a> The &#8216;scaledVector&#8217; variable is assigned but is not used by the end of the function. vector.hpp 124<\/p>\n<p>  This is a typo. The function must return the new <i>scaledVector<\/i> vector rather than the original <i>v<\/i> vector.<\/p>\n<p>  Another similar typo: V1001 The &#8216;normalizedVector&#8217; variable is assigned but is not used by the end of the function. vector.hpp 131<\/p>\n<p>  <b>Bug 2<\/b><\/p>\n<pre><code class=\"cpp\">template&lt;typename Scalar> class matrix {   ....   matrix&amp; diagonal() {   }   .... };<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v591\/\">V591<\/a> Non-void function should return a value. matrix.hpp 109<\/p>\n<p>  <b>Bug 3<\/b><\/p>\n<pre><code class=\"cpp\">template&lt;size_t fbits, size_t abits> void module_subtract_BROKEN(   const value&lt;fbits>&amp; lhs, const value&lt;fbits>&amp; rhs, value&lt;abits + 1>&amp; result) {   if (lhs.isinf() || rhs.isinf()) {     result.setinf();     return;   }   int lhs_scale = lhs.scale(),       rhs_scale = rhs.scale(),       scale_of_result = std::max(lhs_scale, rhs_scale);    \/\/ align the fractions   bitblock&lt;abits> r1 =     lhs.template nshift&lt;abits>(lhs_scale - scale_of_result + 3);   bitblock&lt;abits> r2 =     rhs.template nshift&lt;abits>(rhs_scale - scale_of_result + 3);   bool r1_sign = lhs.sign(), r2_sign = rhs.sign();   \/\/bool signs_are_equal = r1_sign == r2_sign;    if (r1_sign) r1 = twos_complement(r1);   if (r1_sign) r2 = twos_complement(r2);  \/\/ &lt;=    .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v581\/\">V581<\/a> The conditional expressions of the &#8216;if&#8217; statements situated alongside each other are identical. Check lines: 789, 790. value.hpp 790<\/p>\n<p>  It&#8217;s a classic copy-paste bug. The programmer cloned the following line:<\/p>\n<pre><code class=\"cpp\">if (r1_sign) r1 = twos_complement(r1);<\/code><\/pre>\n<p>  Changed <i>r1<\/i> to <i>r2<\/i>:<\/p>\n<pre><code class=\"cpp\">if (r1_sign) r2 = twos_complement(r2);<\/code><\/pre>\n<p>  But forgot to change <i>r1_sign<\/i>. Here&#8217;s the correct version:<\/p>\n<pre><code class=\"cpp\">if (r2_sign) r2 = twos_complement(r2);<\/code><\/pre>\n<p>  <\/p>\n<h3>Chobo Single-Header Libraries<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/Chobolabs\/chobo-shl\">Chobo Single-Header Libraries<\/a> library:<\/p>\n<blockquote><p>A collection of small single-header C++11 libraries by Chobolabs.<\/p><\/blockquote>\n<p><b>Bug 1<\/b><\/p>\n<pre><code class=\"cpp\">template &lt;typename T, typename U, typename Alloc = std::allocator&lt;T>> class vector_view {   ....   vector_view&amp; operator=(vector_view&amp;&amp; other)   {     m_vector = std::move(other.m_vector);   }   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v591\/\">V591<\/a> Non-void function should return a value. vector_view.hpp 163<\/p>\n<p>  <b>Bug 2<\/b><\/p>\n<pre><code class=\"cpp\">template &lt;typename UAlloc> vector_view&amp; operator=(const std::vector&lt;U, UAlloc>&amp; other) {   size_type n = other.size();   resize(n);   for (size_type i = 0; i &lt; n; ++i)   {     this->at(i) = other[i];   } }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v591\/\">V591<\/a> Non-void function should return a value. vector_view.hpp 184<\/p>\n<h3>PGM-index<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/gvinciguerra\/PGM-index\">PGM-index<\/a> library:<\/p>\n<blockquote><p>The Piecewise Geometric Model index (PGM-index) is a data structure that enables fast lookup, predecessor, range searches and updates in arrays of billions of items using orders of magnitude less space than traditional indexes while providing the same worst-case query time guarantees.<\/p><\/blockquote>\n<p><b>Bug 1<\/b><\/p>\n<pre><code class=\"cpp\">char* str_from_errno() { #ifdef MSVC_COMPILER   #pragma warning(disable:4996)   return strerror(errno); #pragma warning(default:4996) #else   return strerror(errno); #endif }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v665\/\">V665<\/a> Possibly, the usage of &#8216;#pragma warning(default: X)&#8217; is incorrect in this context. The &#8216;#pragma warning(push\/pop)&#8217; should be used instead. Check lines: 9170, 9172. sdsl.hpp 9172<\/p>\n<p>  This code temporarily disables a compiler warning but does that in an incorrect way. Such mistakes can be tolerated in user code, but certainly not in header-only libraries.<\/p>\n<p>  <b>Bug 2<\/b><\/p>\n<pre><code class=\"cpp\">template&lt;class t_int_vec> t_int_vec rnd_positions(uint8_t log_s, uint64_t&amp; mask,                         uint64_t mod=0, uint64_t seed=17) {   mask = (1&lt;&lt;log_s)-1;         \/\/ &lt;=   t_int_vec rands(1&lt;&lt;log_s ,0);   set_random_bits(rands, seed);   if (mod > 0) {     util::mod(rands, mod);   }   return rands; }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v629\/\">V629<\/a> Consider inspecting the &#8216;1 &lt;&lt; log_s&#8217; expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. sdsl.hpp 1350<\/p>\n<p>  Here&#8217;s one correct version:<\/p>\n<pre><code class=\"cpp\">mask = ((uint64_t)(1)&lt;&lt;log_s)-1;<\/code><\/pre>\n<p>  <\/p>\n<h3>Hnswlib<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/nmslib\/hnswlib\">Hnswlib<\/a> library:<\/p>\n<blockquote><p>Header-only C++ HNSW implementation with python bindings. Paper&#8217;s code for the HNSW 200M SIFT experiment.<\/p><\/blockquote>\n<pre><code class=\"cpp\">template&lt;typename dist_t> class BruteforceSearch : public AlgorithmInterface&lt;dist_t> { public:   BruteforceSearch(SpaceInterface &lt;dist_t> *s, size_t maxElements) {     maxelements_ = maxElements;     data_size_ = s->get_data_size();     fstdistfunc_ = s->get_dist_func();     dist_func_param_ = s->get_dist_func_param();     size_per_element_ = data_size_ + sizeof(labeltype);     data_ = (char *) malloc(maxElements * size_per_element_);     if (data_ == nullptr)       std::runtime_error(         \"Not enough memory: BruteforceSearch failed to allocate data\");     cur_element_count = 0;   }   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v596\/\">V596<\/a> The object was created but it is not being used. The &#8216;throw&#8217; keyword could be missing: throw runtime_error(FOO); bruteforce.h 26<\/p>\n<p>  The <i>throw <\/i>operator is missing before <i>std::runtime_error<\/i>.<\/p>\n<p>  A similar problem: <a href=\"https:\/\/www.viva64.com\/en\/w\/v596\/\">V596<\/a> The object was created but it is not being used. The &#8216;throw&#8217; keyword could be missing: throw runtime_error(FOO); bruteforce.h 161<\/p>\n<h3>tiny-dnn<\/h3>\n<p>  Brief description of the <a href=\"https:\/\/github.com\/tiny-dnn\/tiny-dnn\">tiny-dnn<\/a> library:<\/p>\n<blockquote><p>tiny-dnn is a C++14 implementation of deep learning. It is suitable for deep learning on limited computational resource, embedded systems and IoT devices.<\/p><\/blockquote>\n<p><b>Bug 1<\/b><\/p>\n<pre><code class=\"cpp\">class nn_error : public std::exception {  public:   explicit nn_error(const std::string &amp;msg) : msg_(msg) {}   const char *what() const throw() override { return msg_.c_str(); }   private:   std::string msg_; };  inline Device::Device(device_t type, const int platform_id, const int device_id)   : type_(type),     has_clcuda_api_(true),     platform_id_(platform_id),     device_id_(device_id) {   .... #else   nn_error(\"TinyDNN has not been compiled with OpenCL or CUDA support.\"); #endif }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v596\/\">V596<\/a> The object was created but it is not being used. The &#8216;throw&#8217; keyword could be missing: throw nn_error(FOO); device.h 68<\/p>\n<p>  <i>nn_error<\/i> is not an exception-throwing function but simply a class, and the correct way of using it is as follows:<\/p>\n<pre><code class=\"cpp\">throw nn_error(\"TinyDNN has not been compiled with OpenCL or CUDA support.\");<\/code><\/pre>\n<p>  Here&#8217;s another case of improper use of this class: V596 The object was created but it is not being used. The &#8216;throw&#8217; keyword could be missing: throw nn_error(FOO); conv2d_op_opencl.h 136<\/p>\n<p>  <b>Bug 2<\/b><\/p>\n<pre><code class=\"cpp\">inline std::string format_str(const char *fmt, ...) {   static char buf[2048];  #ifdef _MSC_VER #pragma warning(disable : 4996) #endif   va_list args;   va_start(args, fmt);   vsnprintf(buf, sizeof(buf), fmt, args);   va_end(args); #ifdef _MSC_VER #pragma warning(default : 4996) #endif   return std::string(buf); }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v665\/\">V665<\/a> Possibly, the usage of &#8216;#pragma warning(default: X)&#8217; is incorrect in this context. The &#8216;#pragma warning(push\/pop)&#8217; should be used instead. Check lines: 139, 146. util.h 146<\/p>\n<h3>Dlib<\/h3>\n<p>  Brief description of the <a href=\"http:\/\/dlib.net\/\">Dlib<\/a> library:<\/p>\n<blockquote><p>Dlib is a modern C++ toolkit containing machine learning algorithms and tools for creating complex software in C++ to solve real world problems.<\/p><\/blockquote>\n<p><b>Bug 1<\/b><\/p>\n<p>  To keep things interesting, I dare you to find the bug on your own.<\/p>\n<pre><code class=\"cpp\">class bdf_parser { public:    enum bdf_enums   {     NO_KEYWORD = 0,     STARTFONT = 1,     FONTBOUNDINGBOX = 2,     DWIDTH = 4,     DEFAULT_CHAR = 8,     CHARS = 16,     STARTCHAR = 32,     ENCODING = 64,     BBX = 128,     BITMAP = 256,     ENDCHAR = 512,     ENDFONT = 1024   };   ....   bool parse_header( header_info&amp; info )   {     ....     while ( 1 )     {       res = find_keywords( find | stop );       if ( res &amp; FONTBOUNDINGBOX )       {           in_ >> info.FBBx >> info.FBBy >> info.Xoff >> info.Yoff;           if ( in_.fail() )               return false;    \/\/ parse_error           find &amp;= ~FONTBOUNDINGBOX;           continue;       }       if ( res &amp; DWIDTH )       {           in_ >> info.dwx0 >> info.dwy0;           if ( in_.fail() )               return false;    \/\/ parse_error           find &amp;= ~DWIDTH;           info.has_global_dw = true;           continue;       }       if ( res &amp; DEFAULT_CHAR )       {           in_ >> info.default_char;           if ( in_.fail() )               return false;    \/\/ parse_error           find &amp;= ~DEFAULT_CHAR;           continue;       }       if ( res &amp; NO_KEYWORD )           return false;    \/\/ parse_error: unexpected EOF       break;     }   .... };<\/code><\/pre>\n<p>  Any luck?<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/d90\/7c0\/3c7\/d907c03c7735437aa5447be2729dcad3.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/d90\/7c0\/3c7\/d907c03c7735437aa5447be2729dcad3.png\"\/><\/div>\n<p>  Here it is:<\/p>\n<pre><code class=\"cpp\">if ( res &amp; NO_KEYWORD )<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v616\/\">V616<\/a> The &#8216;NO_KEYWORD&#8217; named constant with the value of 0 is used in the bitwise operation. fonts.cpp 288<\/p>\n<p>  The value of the named constant <i>NO_KEYWORD<\/i> is 0. Therefore, the condition doesn&#8217;t make sense. This is what the correct check should look like:<\/p>\n<pre><code class=\"cpp\">if ( res == NO_KEYWORD )<\/code><\/pre>\n<p>  Another incorrect check: V616 The &#8216;NO_KEYWORD&#8217; named constant with the value of 0 is used in the bitwise operation. fonts.cpp 334<\/p>\n<p>  <b>Bug 2<\/b><\/p>\n<pre><code class=\"cpp\">void set(std::vector&lt;tensor*> items) {   ....   epa.emplace_back(new enable_peer_access(*g[0], *g[i]));   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v1023\/\">V1023<\/a> A pointer without owner is added to the &#8216;epa&#8217; container by the &#8217;emplace_back&#8217; method. A memory leak will occur in case of an exception. tensor_tools.h 1665<\/p>\n<p>  To figure out what&#8217;s wrong here, see the <a href=\"https:\/\/www.viva64.com\/en\/w\/v1023\/\">V1023<\/a> documentation.<\/p>\n<p>  <b>Bug 3<\/b><\/p>\n<pre><code class=\"cpp\">template &lt;     typename detection_type,      typename label_type      > bool is_track_association_problem (   const std::vector&lt;     std::vector&lt;labeled_detection&lt;detection_type,label_type> > >&amp; samples ) {   if (samples.size() == 0)     return false;    unsigned long num_nonzero_elements = 0;   for (unsigned long i = 0; i &lt; samples.size(); ++i)   {     if (samples.size() > 0)       ++num_nonzero_elements;   }   if (num_nonzero_elements &lt; 2)     return false;   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;samples.size() > 0&#8217; is always true. svm.h 360<\/p>\n<p>  It&#8217;s a very, very strange piece of code! If the loop starts, then the <i>(samples.size() > 0)<\/i> condition is always true. And that means the loop can be simplified:<\/p>\n<pre><code class=\"cpp\">for (unsigned long i = 0; i &lt; samples.size(); ++i) {   ++num_nonzero_elements; }<\/code><\/pre>\n<p>  But now it becomes clear that you don&#8217;t need the loop at all. The snippet could be rewritten in a much simpler and more efficient way:<\/p>\n<pre><code class=\"cpp\">unsigned long num_nonzero_elements = samples.size();<\/code><\/pre>\n<p>  But was it really the developer&#8217;s intention? This code certainly needs close inspection.<\/p>\n<p>  <b>Bug 4<\/b><\/p>\n<pre><code class=\"cpp\">class console_progress_indicator {   ....   double seen_first_val;   .... };  bool console_progress_indicator::print_status (   double cur, bool always_print) {   ....   if (!seen_first_val)   {     start_time = cur_time;     last_time = cur_time;     first_val = cur;     seen_first_val = true;  \/\/ &lt;=     return false;   }   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v601\/\">V601<\/a> The bool type is implicitly cast to the double type. console_progress_indicator.h 136<\/p>\n<p>  The value <i>true<\/i> is stored to a class member of type <i>double<\/i>. Hmm\u2026<\/p>\n<p>  <b>Bug 5<\/b><\/p>\n<pre><code class=\"cpp\">void file::init(const std::string&amp; name) {   ....   WIN32_FIND_DATAA data;   HANDLE ffind = FindFirstFileA(state.full_name.c_str(), &amp;data);   if (ffind == INVALID_HANDLE_VALUE ||       (data.dwFileAttributes&amp;FILE_ATTRIBUTE_DIRECTORY) != 0)   {     throw file_not_found(\"Unable to find file \" + name);                   }   else   {     ....   }  }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v773\/\">V773<\/a> The exception was thrown without closing the file referenced by the &#8216;ffind&#8217; handle. A resource leak is possible. dir_nav_kernel_1.cpp 60<\/p>\n<p>  An exception is thrown if the directory is found. But how about closing the file handle?<\/p>\n<p>  <b>Bug 6<\/b><\/p>\n<p>  Another extremely strange spot.<\/p>\n<pre><code class=\"cpp\">inline double poly_min_extrap(double f0, double d0,                               double x1, double f_x1,                               double x2, double f_x2) {   ....   matrix&lt;double,2,2> m;   matrix&lt;double,2,1> v;    const double aa2 = x2*x2;   const double aa1 = x1*x1;   m =  aa2,       -aa1,       -aa2*x2, aa1*x1;      v = f_x1 - f0 - d0*x1,       f_x2 - f0 - d0*x2;   .... }<\/code><\/pre>\n<p>  PVS-Studio diagnostic message: <a href=\"https:\/\/www.viva64.com\/en\/w\/v521\/\">V521<\/a> Such expressions using the &#8216;,&#8217; operator are dangerous. Make sure the expression is correct. optimization_line_search.h 211<\/p>\n<p>  The plan was to have the matrices initialized. But all those <i>aa2<\/i>, <i>f_x1<\/i>, <i>d0<\/i>, etc. are simply variables of type <i>double<\/i>. And that means the commas don&#8217;t separate the arguments, which are used to form the matrices, but simply act as the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Comma_operator\">comma operator<\/a>, which returns the value of the right operand.<\/p>\n<h2>Conclusion<\/h2>\n<p>  At the beginning of this article, I gave you an example of how you can pursue several goals at once. Using a static analyzer is, too, beneficial for several reasons:<\/p>\n<ul>\n<li>It helps you improve your qualification. You learn lots of new and useful things while handling analyzer warnings. For example, see <a href=\"https:\/\/www.viva64.com\/en\/w\/v597\/\">memset<\/a>, <a href=\"https:\/\/www.viva64.com\/en\/w\/v665\/\">#pragma warning<\/a>, <a href=\"https:\/\/www.viva64.com\/en\/w\/v1023\/\">emplace_back<\/a>, <a href=\"https:\/\/www.viva64.com\/en\/w\/v1032\/\">strictly aligned<\/a>.<\/li>\n<li>Typos, mistakes, and potential vulnerabilities are detected at earlier development stages. <\/li>\n<li>The code gradually grows more high-quality, simple, and clear.<\/li>\n<li>You can feel proud and boast of using modern technologies in software development :). Well, it&#8217;s only half-joke. Static analysis does give you a significant competitive edge.<\/li>\n<\/ul>\n<p>  The only question left is how to get started with static analysis, integrate it smoothly, and use it properly. The following articles will give you all the answers you need:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.viva64.com\/en\/b\/0633\/\">How to quickly check out interesting warnings given by the PVS-Studio analyzer for C and C++ code?<\/a><\/li>\n<li><a href=\"https:\/\/www.viva64.com\/en\/b\/0743\/\">How to introduce a static code analyzer in a legacy project and not to discourage the team<\/a>.<\/li>\n<li><a href=\"https:\/\/habr.com\/en\/post\/440610\/\"> Introduce Static Analysis in the Process, Don&#8217;t Just Search for Bugs with It<\/a>.<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/524560\/\"> https:\/\/habr.com\/ru\/articles\/524560\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/141\/300\/658\/141300658daf1fe0d2203c866e74a463.png\" alt=\"PVS-Studio and Awesome hpp\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/141\/300\/658\/141300658daf1fe0d2203c866e74a463.png\"\/><\/div>\n<p>  Somehow, we&#8217;ve happened to check most of the libraries making up a collection called &#171;Awesome hpp&#187;. These are small header-only projects in C++. Hopefully, the information about the bugs we&#8217;ve found will help make the libraries better. We&#8217;ll also be happy to see the developers use PVS-Studio on a regular basis thanks to the free-license option we provide.  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-403358","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/403358","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=403358"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/403358\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=403358"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=403358"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=403358"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}