{"id":462540,"date":"2025-06-09T15:00:28","date_gmt":"2025-06-09T15:00:28","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=462540"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=462540","title":{"rendered":"<span>Neat defer macro for C++17<\/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-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>Manual resource management in low level C-style C++ code might be annoying. It&#8217;s not practical to create good enough RAII wrappers for every single C API you use, but approaches with <code>goto cleanup<\/code> or loads of nested <code>if (success)<\/code> hurt readability.<\/p>\n<p>A Go-inspired <code>defer<\/code> macro to the rescue! The usage is as simple as that:<\/p>\n<pre><code class=\"cpp\">void* p = malloc(0x1000); defer [&amp;] { free(p); }; <\/code><\/pre>\n<p>The deferred lambda will be executed on scope exit, no matter how it happens: you can <code>return<\/code> from any point, <code>throw<\/code> an exception (if allowed), or even use a <code>goto<\/code> to an outer scope.<\/p>\n<p>Macro implementation is concise and relies on basic features of C++17 (Clang 5+, GCC 7+, MSVC 2017+):<\/p>\n<pre><code class=\"cpp\">#ifndef defer  template &lt;typename T&gt; struct deferrer { T f; deferrer(T f) : f(f) { }; deferrer(const deferrer&amp;) = delete; ~deferrer() { f(); } };  #define TOKEN_CONCAT_NX(a, b) a ## b #define TOKEN_CONCAT(a, b) TOKEN_CONCAT_NX(a, b) #define defer deferrer TOKEN_CONCAT(__deferred, __COUNTER__) =  #endif <\/code><\/pre>\n<p>It is <a href=\"https:\/\/clang.godbolt.org\/z\/Wo6aaG7Ev\" rel=\"noopener noreferrer nofollow\">truly zero-cost<\/a> and doesn&#8217;t rely on C runtime or standard library, so it can be used even in kernel development.<\/p>\n<h3>Let&#8217;s compare!<\/h3>\n<h4>Naive version<\/h4>\n<p>Let&#8217;s imagine a function where all successfully acquired resources are explicitly released on every error:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { HMODULE dbgdll = LoadLibraryA(\"dbghelp.dll\"); if (!dbgdll) { return false; }  auto pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll, \"MiniDumpWriteDump\"); if (!pfnMiniDumpWriteDump) { FreeLibrary(dbgdll); return false; }  HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!proc) { FreeLibrary(dbgdll); return false; }  HANDLE file = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (!file || file == INVALID_HANDLE_VALUE) { CloseHandle(proc); FreeLibrary(dbgdll); return false; }  bool result = pfnMiniDumpWriteDump(proc, pid, file, MiniDumpNormal, NULL, NULL, NULL);  CloseHandle(file); CloseHandle(proc); FreeLibrary(dbgdll);  return result; } <\/code><\/pre>\n<p>So many duplicated lines of code, so easy to make a mistake and forget to free something!<\/p>\n<h4>Classic goto cleanup<\/h4>\n<p>The same function, but in the classic <code>goto cleanup<\/code> style:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { bool result = false; HMODULE dbgdll = NULL; decltype(&amp;MiniDumpWriteDump) pfnMiniDumpWriteDump = nullptr; HANDLE proc = NULL; HANDLE file = NULL;  dbgdll = LoadLibraryA(\"dbghelp.dll\"); if (!dbgdll) { goto cleanup; }  pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll, \"MiniDumpWriteDump\"); if (!pfnMiniDumpWriteDump) { goto cleanup; }  proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!proc) { goto cleanup; }  file = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (!file || file == INVALID_HANDLE_VALUE) { goto cleanup; }  result = pfnMiniDumpWriteDump(proc, pid, file, MiniDumpNormal, NULL, NULL, NULL);  cleanup:  if (file &amp;&amp; file != INVALID_HANDLE_VALUE) { CloseHandle(file); }  if (proc) { CloseHandle(proc); }  if (dbgdll) { FreeLibrary(dbgdll); }  return result; } <\/code><\/pre>\n<p>You can&#8217;t <code>goto<\/code> through variable declarations so it requires to declare all variables in advance. It&#8217;s also a bit less effective because the <code>cleanup<\/code> part should check if every resource is valid and requires to be released, and you can accidentally forget to free something or do it in wrong order because it&#8217;s far from the code that acquires the resources so it&#8217;s harder to notice an error.<\/p>\n<h4>Nested if success<\/h4>\n<p>With the nested <code>if (success)<\/code> approach our function would become:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { HMODULE dbgdll = LoadLibraryA(\"dbghelp.dll\"); if (dbgdll) { auto pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll, \"MiniDumpWriteDump\"); if (pfnMiniDumpWriteDump) { HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (proc) { HANDLE file = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file &amp;&amp; file != INVALID_HANDLE_VALUE) { bool result = pfnMiniDumpWriteDump(proc, pid, file, MiniDumpNormal, NULL, NULL, NULL); CloseHandle(file); return result; } CloseHandle(proc); } } FreeLibrary(dbgdll); } return false; } <\/code><\/pre>\n<p>An improvement, but you&#8217;d better have a really wide monitor for this!<\/p>\n<h4>WTF std::unique_ptr<\/h4>\n<p>The same, but with taste of STL:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { std::unique_ptr&lt;std::remove_pointer_t&lt;HMODULE&gt;, decltype(&amp;FreeLibrary)&gt; dbgdll(LoadLibraryA(\"dbghelp.dll\"), &amp;FreeLibrary); if (!dbgdll) { return false; }  auto pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll.get(), \"MiniDumpWriteDump\"); if (!pfnMiniDumpWriteDump) { return false; }  std::unique_ptr&lt;std::remove_pointer_t&lt;HANDLE&gt;, decltype(&amp;CloseHandle)&gt; proc(OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid), &amp;CloseHandle); if (!proc) { return false; }  std::unique_ptr&lt;std::remove_pointer_t&lt;HANDLE&gt;, decltype(&amp;CloseHandle)&gt; file([&amp;]{ auto h = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); return (h != INVALID_HANDLE_VALUE) ? h : NULL; }(), &amp;CloseHandle); if (!file) { return false; }  return pfnMiniDumpWriteDump(proc.get(), pid, file.get(), MiniDumpNormal, NULL, NULL, NULL); } <\/code><\/pre>\n<p>STL, as usual, delivers the best WTF experience. This hacky approach is provided here for completeness&#8217; sake. Some people really use <code>std::unique_ptr<\/code> with custom deleters to manage non-pointer resources, even though template argument deduction does not help here, requiring you to specify all those verbose types every time. It has an important limitation: the resource must appear as <code>nullptr<\/code> in an invalid state, which is not always the case, and you have to deal with this somehow using additional hacks and tricks.<\/p>\n<h4>And finally, defer!<\/h4>\n<p>We can rewrite it with our <code>defer<\/code> macro this way:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { HMODULE dbgdll = LoadLibraryA(\"dbghelp.dll\"); if (!dbgdll) { return false; } defer [&amp;] { FreeLibrary(dbgdll); };  auto pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll, \"MiniDumpWriteDump\"); if (!pfnMiniDumpWriteDump) { return false; }  HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!proc) { return false; } defer [&amp;] { CloseHandle(proc); };  HANDLE file = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (!file || file == INVALID_HANDLE_VALUE) { return false; } defer [&amp;] { CloseHandle(file); };  return pfnMiniDumpWriteDump(proc, pid, file, MiniDumpNormal, NULL, NULL, NULL); } <\/code><\/pre>\n<p>This looks much better! No excessive nesting, no so much hated <code>goto<\/code>, no duplicated lines of code.<\/p>\n<h3>Why this syntax?<\/h3>\n<p>Well, what other syntax could it be? Let&#8217;s think&#8230;<\/p>\n<pre><code class=\"cpp\">defer free(p); <\/code><\/pre>\n<p>Go-like syntax. Unfortunately, it can&#8217;t be implemented as a C++ macro.<\/p>\n<pre><code class=\"cpp\">defer(free(p)); <\/code><\/pre>\n<p>Looks misleading \u2014 it seems like <code>free(p)<\/code> is called immediately, and its result is passed to defer. Also, it doesn&#8217;t allow deferring multiple lines of code, which is sometimes useful.<\/p>\n<pre><code class=\"cpp\">defer { free(p); }; <\/code><\/pre>\n<p>Better, but it doesn&#8217;t let you control whether outer variables are captured by reference or by copy, which is important in some cases.<\/p>\n<pre><code class=\"cpp\">defer [&amp;] { free(p); }; <\/code><\/pre>\n<p>Our syntax. It expects a proper lambda, providing flexibility to control whether it captures variables by reference or by copy. In fact, it can defer any callable, not just a lambda \u2014 so even the semicolon after the closing brace looks reasonable.<\/p>\n<p>There is also a <a href=\"https:\/\/open-std.org\/jtc1\/sc22\/wg14\/www\/docs\/n2895.htm\" rel=\"noopener noreferrer nofollow\">proposal<\/a> to add <code>defer<\/code> to C, and it uses exactly this syntax.<\/p>\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\/916760\/\"> https:\/\/habr.com\/ru\/articles\/916760\/<\/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-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>Manual resource management in low level C-style C++ code might be annoying. It&#8217;s not practical to create good enough RAII wrappers for every single C API you use, but approaches with <code>goto cleanup<\/code> or loads of nested <code>if (success)<\/code> hurt readability.<\/p>\n<p>A Go-inspired <code>defer<\/code> macro to the rescue! The usage is as simple as that:<\/p>\n<pre><code class=\"cpp\">void* p = malloc(0x1000); defer [&amp;] { free(p); }; <\/code><\/pre>\n<p>The deferred lambda will be executed on scope exit, no matter how it happens: you can <code>return<\/code> from any point, <code>throw<\/code> an exception (if allowed), or even use a <code>goto<\/code> to an outer scope.<\/p>\n<p>Macro implementation is concise and relies on basic features of C++17 (Clang 5+, GCC 7+, MSVC 2017+):<\/p>\n<pre><code class=\"cpp\">#ifndef defer  template &lt;typename T&gt; struct deferrer { T f; deferrer(T f) : f(f) { }; deferrer(const deferrer&amp;) = delete; ~deferrer() { f(); } };  #define TOKEN_CONCAT_NX(a, b) a ## b #define TOKEN_CONCAT(a, b) TOKEN_CONCAT_NX(a, b) #define defer deferrer TOKEN_CONCAT(__deferred, __COUNTER__) =  #endif <\/code><\/pre>\n<p>It is <a href=\"https:\/\/clang.godbolt.org\/z\/Wo6aaG7Ev\" rel=\"noopener noreferrer nofollow\">truly zero-cost<\/a> and doesn&#8217;t rely on C runtime or standard library, so it can be used even in kernel development.<\/p>\n<h3>Let&#8217;s compare!<\/h3>\n<h4>Naive version<\/h4>\n<p>Let&#8217;s imagine a function where all successfully acquired resources are explicitly released on every error:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { HMODULE dbgdll = LoadLibraryA(\"dbghelp.dll\"); if (!dbgdll) { return false; }  auto pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll, \"MiniDumpWriteDump\"); if (!pfnMiniDumpWriteDump) { FreeLibrary(dbgdll); return false; }  HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!proc) { FreeLibrary(dbgdll); return false; }  HANDLE file = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (!file || file == INVALID_HANDLE_VALUE) { CloseHandle(proc); FreeLibrary(dbgdll); return false; }  bool result = pfnMiniDumpWriteDump(proc, pid, file, MiniDumpNormal, NULL, NULL, NULL);  CloseHandle(file); CloseHandle(proc); FreeLibrary(dbgdll);  return result; } <\/code><\/pre>\n<p>So many duplicated lines of code, so easy to make a mistake and forget to free something!<\/p>\n<h4>Classic goto cleanup<\/h4>\n<p>The same function, but in the classic <code>goto cleanup<\/code> style:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { bool result = false; HMODULE dbgdll = NULL; decltype(&amp;MiniDumpWriteDump) pfnMiniDumpWriteDump = nullptr; HANDLE proc = NULL; HANDLE file = NULL;  dbgdll = LoadLibraryA(\"dbghelp.dll\"); if (!dbgdll) { goto cleanup; }  pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll, \"MiniDumpWriteDump\"); if (!pfnMiniDumpWriteDump) { goto cleanup; }  proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!proc) { goto cleanup; }  file = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (!file || file == INVALID_HANDLE_VALUE) { goto cleanup; }  result = pfnMiniDumpWriteDump(proc, pid, file, MiniDumpNormal, NULL, NULL, NULL);  cleanup:  if (file &amp;&amp; file != INVALID_HANDLE_VALUE) { CloseHandle(file); }  if (proc) { CloseHandle(proc); }  if (dbgdll) { FreeLibrary(dbgdll); }  return result; } <\/code><\/pre>\n<p>You can&#8217;t <code>goto<\/code> through variable declarations so it requires to declare all variables in advance. It&#8217;s also a bit less effective because the <code>cleanup<\/code> part should check if every resource is valid and requires to be released, and you can accidentally forget to free something or do it in wrong order because it&#8217;s far from the code that acquires the resources so it&#8217;s harder to notice an error.<\/p>\n<h4>Nested if success<\/h4>\n<p>With the nested <code>if (success)<\/code> approach our function would become:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { HMODULE dbgdll = LoadLibraryA(\"dbghelp.dll\"); if (dbgdll) { auto pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll, \"MiniDumpWriteDump\"); if (pfnMiniDumpWriteDump) { HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (proc) { HANDLE file = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file &amp;&amp; file != INVALID_HANDLE_VALUE) { bool result = pfnMiniDumpWriteDump(proc, pid, file, MiniDumpNormal, NULL, NULL, NULL); CloseHandle(file); return result; } CloseHandle(proc); } } FreeLibrary(dbgdll); } return false; } <\/code><\/pre>\n<p>An improvement, but you&#8217;d better have a really wide monitor for this!<\/p>\n<h4>WTF std::unique_ptr<\/h4>\n<p>The same, but with taste of STL:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { std::unique_ptr&lt;std::remove_pointer_t&lt;HMODULE&gt;, decltype(&amp;FreeLibrary)&gt; dbgdll(LoadLibraryA(\"dbghelp.dll\"), &amp;FreeLibrary); if (!dbgdll) { return false; }  auto pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll.get(), \"MiniDumpWriteDump\"); if (!pfnMiniDumpWriteDump) { return false; }  std::unique_ptr&lt;std::remove_pointer_t&lt;HANDLE&gt;, decltype(&amp;CloseHandle)&gt; proc(OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid), &amp;CloseHandle); if (!proc) { return false; }  std::unique_ptr&lt;std::remove_pointer_t&lt;HANDLE&gt;, decltype(&amp;CloseHandle)&gt; file([&amp;]{ auto h = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); return (h != INVALID_HANDLE_VALUE) ? h : NULL; }(), &amp;CloseHandle); if (!file) { return false; }  return pfnMiniDumpWriteDump(proc.get(), pid, file.get(), MiniDumpNormal, NULL, NULL, NULL); } <\/code><\/pre>\n<p>STL, as usual, delivers the best WTF experience. This hacky approach is provided here for completeness&#8217; sake. Some people really use <code>std::unique_ptr<\/code> with custom deleters to manage non-pointer resources, even though template argument deduction does not help here, requiring you to specify all those verbose types every time. It has an important limitation: the resource must appear as <code>nullptr<\/code> in an invalid state, which is not always the case, and you have to deal with this somehow using additional hacks and tricks.<\/p>\n<h4>And finally, defer!<\/h4>\n<p>We can rewrite it with our <code>defer<\/code> macro this way:<\/p>\n<pre><code class=\"cpp\">bool MakeDumpToFile(DWORD pid, PCWCHAR filename) { HMODULE dbgdll = LoadLibraryA(\"dbghelp.dll\"); if (!dbgdll) { return false; } defer [&amp;] { FreeLibrary(dbgdll); };  auto pfnMiniDumpWriteDump = (decltype(&amp;MiniDumpWriteDump)) GetProcAddress(dbgdll, \"MiniDumpWriteDump\"); if (!pfnMiniDumpWriteDump) { return false; }  HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!proc) { return false; } defer [&amp;] { CloseHandle(proc); };  HANDLE file = CreateFileW(filename, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (!file || file == INVALID_HANDLE_VALUE) { return false; } defer [&amp;] { CloseHandle(file); };  return pfnMiniDumpWriteDump(proc, pid, file, MiniDumpNormal, NULL, NULL, NULL); } <\/code><\/pre>\n<p>This looks much better! No excessive nesting, no so much hated <code>goto<\/code>, no duplicated lines of code.<\/p>\n<h3>Why this syntax?<\/h3>\n<p>Well, what other syntax could it be? Let&#8217;s think&#8230;<\/p>\n<pre><code class=\"cpp\">defer free(p); <\/code><\/pre>\n<p>Go-like syntax. Unfortunately, it can&#8217;t be implemented as a C++ macro.<\/p>\n<pre><code class=\"cpp\">defer(free(p)); <\/code><\/pre>\n<p>Looks misleading \u2014 it seems like <code>free(p)<\/code> is called immediately, and its result is passed to defer. Also, it doesn&#8217;t allow deferring multiple lines of code, which is sometimes useful.<\/p>\n<pre><code class=\"cpp\">defer { free(p); }; <\/code><\/pre>\n<p>Better, but it doesn&#8217;t let you control whether outer variables are captured by reference or by copy, which is important in some cases.<\/p>\n<pre><code class=\"cpp\">defer [&amp;] { free(p); }; <\/code><\/pre>\n<p>Our syntax. It expects a proper lambda, providing flexibility to control whether it captures variables by reference or by copy. In fact, it can defer any callable, not just a lambda \u2014 so even the semicolon after the closing brace looks reasonable.<\/p>\n<p>There is also a <a href=\"https:\/\/open-std.org\/jtc1\/sc22\/wg14\/www\/docs\/n2895.htm\" rel=\"noopener noreferrer nofollow\">proposal<\/a> to add <code>defer<\/code> to C, and it uses exactly this syntax.<\/p>\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\/916760\/\"> https:\/\/habr.com\/ru\/articles\/916760\/<\/a><br \/><\/br><\/br><\/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-462540","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/462540","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=462540"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/462540\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=462540"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=462540"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=462540"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}