{"id":2857,"date":"2018-04-19T19:00:55","date_gmt":"2018-04-19T17:00:55","guid":{"rendered":"http:\/\/mariusbancila.ro\/blog\/?p=2857"},"modified":"2018-04-20T09:50:25","modified_gmt":"2018-04-20T07:50:25","slug":"five-examples-for-avoiding-the-preprocessor","status":"publish","type":"post","link":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/","title":{"rendered":"Five examples for avoiding the preprocessor"},"content":{"rendered":"<p>The C++ preprocessor is a text replacement tool used to transform the source code in order to produce a single text file that is then passed to the actual compiler. It has various capabilities, such as including files, conditional compilation, text macro replacement, error emitting, stringizing, or token concatenation. Often developers use the preprocessor when other alternatives are available and are more appropriate. In this article, I will show five examples of when and how you can avoid the use of the preprocessor.<br \/>\n<!--more--><\/p>\n<p>Table of contents:<\/p>\n<ol>\n<li><a href=\"#object-like-macros\">Object-like macros<\/a><\/li>\n<li><a href=\"#function-like-macros\">Function-like macros<\/a><\/li>\n<li><a href=\"#exception-handling-macros\">Exception handling macros<\/a><\/li>\n<li><a href=\"#conditional-compilation\">Conditional compilation<\/a><\/li>\n<li><a href=\"#policy-like-macros\">Policy-like macros<\/a><\/li>\n<li><a href=\"#including-files\">Bonus: including files<\/a><\/li>\n<\/ol>\n<p><a name=\"object-like-macros\"><\/a><\/p>\n<h3>Object-like macros<\/h3>\n<p>These are identifiers that are replaced with a fragment of code and are often used to give symbolic names to numerical or string literals. Here is a typical example you must have seen many times.<\/p>\n<pre class=\"lang:c++ decode:true \">#define BUFFER_SIZE 1024\r\n\r\nint main() \r\n{\r\n   char buffer[BUFFER_SIZE];\r\n}<\/pre>\n<p>Instead of being a macro, <tt>BUFFER_SIZE<\/tt> could, and should, be defined as a compile-time constant.<\/p>\n<pre class=\"lang:default decode:true \">constexpr size_t BUFFER_SIZE = 1024;<\/pre>\n<p>Notice it is declared as <tt>constexpr<\/tt> and not just <tt>const<\/tt>. The latter indicates a value that does not change, but might only be available at runtime. The former, implies constness, but is guaranteed to be available at compile-time. <tt>constexpr<\/tt> values can be used in any place where compile-time constants are expected.<\/p>\n<p>Many times object-like macros are used to define related symbolic names, such as in the following example:<\/p>\n<pre class=\"lang:c++ decode:true \">#define PERMISSION_NONE    0\r\n#define PERMISSION_READ    1\r\n#define PERMISSION_WRITE   2\r\n#define PERMISSION_ADD     4\r\n#define PERMISSION_DELETE  8\r\n\r\nvoid show_permissions(int const p)\r\n{\r\n   if(p &amp; PERMISSION_READ)\r\n      std::cout &lt;&lt; \"can read\" &lt;&lt; std::endl;\r\n   if (p &amp; PERMISSION_WRITE)\r\n      std::cout &lt;&lt; \"can write\" &lt;&lt; std::endl;\r\n   if (p &amp; PERMISSION_ADD)\r\n      std::cout &lt;&lt; \"can add\" &lt;&lt; std::endl;\r\n   if (p &amp; PERMISSION_DELETE)\r\n      std::cout &lt;&lt; \"can delete\" &lt;&lt; std::endl;\r\n}\r\n\r\nint main() \r\n{\r\n   int flags = PERMISSION_READ | PERMISSION_WRITE;\r\n\r\n   show_permissions(flags);\r\n\r\n   flags |= PERMISSION_DELETE | PERMISSION_ADD;\r\n   flags &amp;= ~PERMISSION_WRITE;\r\n\r\n   show_permissions(flags);\r\n}<\/pre>\n<p>Following the previous example, we can simply replace these with <tt>constexpr<\/tt> values (in a class or namespace scope):<\/p>\n<pre class=\"lang:c++ decode:true \">constexpr int PERMISSION_NONE   = 0;\r\nconstexpr int PERMISSION_READ   = 1;\r\nconstexpr int PERMISSION_WRITE  = 2;\r\nconstexpr int PERMISSION_ADD    = 4;\r\nconstexpr int PERMISSION_DELETE = 8;<\/pre>\n<p>However, these macros, representing bitflags here, can also be replaced with an enumerator.<\/p>\n<pre class=\"lang:c++ decode:true \">enum class permissions\r\n{\r\n   none = 0,\r\n   read = 1,\r\n   write = 2,\r\n   add = 4,\r\n   del = 8\r\n};\r\n\r\nvoid show_permissions(int const p)\r\n{\r\n   if(p &amp; static_cast&lt;int&gt;(permissions::read))\r\n      std::cout &lt;&lt; \"can read\" &lt;&lt; std::endl;\r\n   if (p &amp; static_cast&lt;int&gt;(permissions::write))\r\n      std::cout &lt;&lt; \"can write\" &lt;&lt; std::endl;\r\n   if (p &amp; static_cast&lt;int&gt;(permissions::add))\r\n      std::cout &lt;&lt; \"can add\" &lt;&lt; std::endl;\r\n   if (p &amp; static_cast&lt;int&gt;(permissions::del))\r\n      std::cout &lt;&lt; \"can delete\" &lt;&lt; std::endl;\r\n}\r\n\r\nint main() \r\n{\r\n   int flags = static_cast&lt;int&gt;(permissions::read) | \r\n               static_cast&lt;int&gt;(permissions::write);\r\n\r\n   show_permissions(flags);\r\n\r\n   flags |= static_cast&lt;int&gt;(permissions::del) | \r\n            static_cast&lt;int&gt;(permissions::add);\r\n   flags &amp;= ~static_cast&lt;int&gt;(permissions::write);\r\n\r\n   show_permissions(flags);\r\n}<\/pre>\n<p>This code is more verbose than the original one and you might be tempted to avoid writing all these explicit casts. You can actually make it as simple as the original and avoid macros, by overloading various operators for the enumerator type. The following snippet shows the completely rewritten example.<\/p>\n<pre class=\"lang:c++ decode:true \">enum class permissions\r\n{\r\n   none = 0,\r\n   read = 1,\r\n   write = 2,\r\n   add = 4,\r\n   del = 8\r\n};\r\n\r\ninline int operator |(permissions const lhv, permissions const rhv)\r\n{\r\n   return static_cast&lt;int&gt;(lhv) | static_cast&lt;int&gt;(rhv);\r\n}\r\n\r\ninline int operator &amp;(permissions const lhv, permissions const rhv)\r\n{\r\n   return static_cast&lt;int&gt;(lhv) &amp; static_cast&lt;int&gt;(rhv);\r\n}\r\n\r\ninline int operator |(int const v, permissions const p)\r\n{\r\n   return v | static_cast&lt;int&gt;(p);\r\n}\r\n\r\ninline int operator |(permissions const p, int const v)\r\n{\r\n   return v | static_cast&lt;int&gt;(p);\r\n}\r\n\r\ninline int operator &amp;(int const v, permissions const p)\r\n{\r\n   return v &amp; static_cast&lt;int&gt;(p);\r\n}\r\n\r\ninline int operator &amp;(permissions const p, int const v)\r\n{\r\n   return v &amp; static_cast&lt;int&gt;(p);\r\n}\r\n\r\ninline int operator~(permissions const p)\r\n{\r\n   return ~static_cast&lt;int&gt;(p);\r\n}\r\n\r\ninline bool operator==(int const v, permissions const p)\r\n{\r\n   return v == static_cast&lt;int&gt;(p);\r\n}\r\n\r\ninline bool operator==(permissions const p, int const v)\r\n{\r\n   return v == p;\r\n}\r\n\r\nvoid show_permissions(int const p)\r\n{\r\n   if(p &amp; permissions::read)\r\n      std::cout &lt;&lt; \"can read\" &lt;&lt; std::endl;\r\n   if (p &amp; permissions::write)\r\n      std::cout &lt;&lt; \"can write\" &lt;&lt; std::endl;\r\n   if (p &amp; permissions::add)\r\n      std::cout &lt;&lt; \"can add\" &lt;&lt; std::endl;\r\n   if (p &amp; permissions::del)\r\n      std::cout &lt;&lt; \"can delete\" &lt;&lt; std::endl;\r\n}\r\n\r\nint main() \r\n{\r\n   int flags = permissions::read | permissions::write;\r\n\r\n   show_permissions(flags);\r\n\r\n   flags |= permissions::del | permissions::add;\r\n   flags &amp;= ~permissions::write;\r\n\r\n   show_permissions(flags);\r\n}<\/pre>\n<p><a name=\"function-like-macros\"><\/a><\/p>\n<h3>Function-like macros<\/h3>\n<p>These are macros that look like functions. The macro name is followed by none, one, or more arguments in paranthesis. Most of the times these can be replaced with regular functions or function templates. Let us get back to the previous example with the permission bigflag macros and introduce a couple function-like macros for setting and testing bit flag values.<\/p>\n<pre class=\"lang:c++ decode:true \">#define PERMISSION_NONE    0\r\n#define PERMISSION_READ    1\r\n#define PERMISSION_WRITE   2\r\n#define PERMISSION_ADD     4\r\n#define PERMISSION_DELETE  8\r\n\r\n#define SETBIT(fFlag, lValue, lBits) (fFlag ? (lValue) | (lBits) : (lValue) &amp; (~lBits))\r\n#define TESTBIT(lValue, lBits)       (((lValue) &amp; (lBits)) == (lBits))\r\n\r\nvoid show_permissions(int const p)\r\n{\r\n   if (TESTBIT(p, PERMISSION_READ))\r\n      std::cout &lt;&lt; \"can read\" &lt;&lt; std::endl;\r\n   if (TESTBIT(p, PERMISSION_WRITE))\r\n      std::cout &lt;&lt; \"can write\" &lt;&lt; std::endl;\r\n   if (TESTBIT(p, PERMISSION_ADD))\r\n      std::cout &lt;&lt; \"can add\" &lt;&lt; std::endl;\r\n   if (TESTBIT(p, PERMISSION_DELETE))\r\n      std::cout &lt;&lt; \"can delete\" &lt;&lt; std::endl;\r\n}\r\n\r\nint main()\r\n{\r\n   int flags = PERMISSION_READ | PERMISSION_WRITE;\r\n\r\n   show_permissions(flags);\r\n\r\n   flags = SETBIT(true, flags, PERMISSION_DELETE);\r\n   flags = SETBIT(true, flags, PERMISSION_ADD);\r\n   flags = SETBIT(false, flags, PERMISSION_WRITE);\r\n\r\n   show_permissions(flags);\r\n}<\/pre>\n<p>The <tt>SETBIT<\/tt> and <tt>TESTBIT<\/tt> macros can be replaced with inline functions (<tt>SETBIT<\/tt> being replaced by two functions, one that sets a bit and one that resets a bit). For the following example, I assume the <tt>permissions<\/tt> scoped enum and the overloaded operators are defined as above.<\/p>\n<pre class=\"lang:c++ decode:true \">inline int set_bit(int const v, permissions const p)\r\n{\r\n   return v | p;\r\n}\r\n\r\ninline int reset_bit(int const v, permissions const p)\r\n{\r\n   return v &amp; ~p;\r\n}\r\n\r\ninline bool test_bit(int const v, permissions const p)\r\n{\r\n   return (v &amp; p) == p;\r\n}\r\n\r\nvoid show_permissions(int const p)\r\n{\r\n   if(test_bit(p, permissions::read))\r\n      std::cout &lt;&lt; \"can read\" &lt;&lt; std::endl;\r\n   if (test_bit(p, permissions::write))\r\n      std::cout &lt;&lt; \"can write\" &lt;&lt; std::endl;\r\n   if (test_bit(p, permissions::add))\r\n      std::cout &lt;&lt; \"can add\" &lt;&lt; std::endl;\r\n   if (test_bit(p, permissions::del))\r\n      std::cout &lt;&lt; \"can delete\" &lt;&lt; std::endl;\r\n}\r\n\r\nint main() \r\n{\r\n   int flags = permissions::read | permissions::write;\r\n\r\n   show_permissions(flags);\r\n\r\n   flags= set_bit(flags, permissions::del);\r\n   flags = set_bit(flags, permissions::add);\r\n   flags = reset_bit(flags, permissions::write);\r\n\r\n   show_permissions(flags);\r\n}<\/pre>\n<p><a name=\"exception-handling-macros\"><\/a><\/p>\n<h3>Exception handling macros<\/h3>\n<p>Confronted with repeated situations when they have to catch the same exeptions and handle them the same way, some developers resort to macros for avoiding repetitive code. The following is such an example.<\/p>\n<pre class=\"lang:c++ decode:true \">#define TRACE_ERR(x) std::cerr &lt;&lt; x &lt;&lt; std::endl\r\n\r\n#define TRY_CALL     try\r\n#define CATCH_CALL   catch(std::runtime_error const &amp; e) \\\r\n                     {\\\r\n                        TRACE_ERR(std::string(\"runtime error: \") + e.what());\\\r\n                     }\\\r\n                     catch (std::logic_error const &amp; e) \\\r\n                     {\\\r\n                        TRACE_ERR(std::string(\"logic error: \") + e.what());\\\r\n                     }\\\r\n                     catch (std::exception const &amp; e) \\\r\n                     {\\\r\n                        TRACE_ERR(std::string(\"exception: \") + e.what());\\\r\n                     }\\\r\n                     catch (...)\\\r\n                     {\\\r\n                        TRACE_ERR(\"unexpected error\");\\\r\n                     }\r\n\r\nvoid func_that_throws()\r\n{\r\n   throw std::runtime_error(\"an error has occurred!\");\r\n}\r\n\r\nint main()\r\n{\r\n   TRY_CALL\r\n   {\r\n      func_that_throws();\r\n   }\r\n   CATCH_CALL\r\n}<\/pre>\n<p>If you execute this program it will print <tt>runtime error: an error has occurred!<\/tt>. However, these macros are not debugable, and in practice may even be hard to write. This example can be rewritten to use a regular function as handler for multiple exceptions. The only difference in the <tt>main()<\/tt> function is an additional call for this function, <tt>error_handler()<\/tt>.<\/p>\n<pre class=\"lang:c++ decode:true \">inline void trace_error(std::string_view text)\r\n{\r\n   std::cerr &lt;&lt; text &lt;&lt; std::endl;\r\n}\r\n\r\nvoid error_handler()\r\n{\r\n   using std::string_literals;\r\n\r\n   try\r\n   {\r\n      throw;\r\n   }\r\n   catch (std::runtime_error const &amp; e)\r\n   {\r\n      trace_error(\"runtime error: \"s + e.what());\r\n   }\r\n   catch (std::logic_error const &amp; e)\r\n   {\r\n      trace_error(\"logic error: \"s + e.what());\r\n   }\r\n   catch (std::exception const &amp; e)\r\n   {\r\n      trace_error(\"exception: \"s + e.what());\r\n   }\r\n   catch (...)\r\n   {\r\n      trace_error(\"unexpected error\");\r\n   }\r\n}\r\n   \r\n\r\nvoid func_that_throws()\r\n{\r\n   throw std::runtime_error(\"an error has occurred!\");\r\n}\r\n\r\nint main()\r\n{\r\n   try\r\n   {\r\n      func_that_throws();\r\n   }\r\n   catch(...)\r\n   {\r\n      error_handler();\r\n   }\r\n}<\/pre>\n<p>The <tt>throw;<\/tt> statement without an expression rethrows the currently handled exception. (It is allowed only when an exception is being handled, otherwise <tt>std::terminate()<\/tt> will be called). It can be used to create handlers that can catch and handle multiple exception types without the need to duplicate code or resort to macros.<\/p>\n<p><a name=\"conditional-compilation\"><\/a><\/p>\n<h3>Conditional compilation<\/h3>\n<p>This is a feature of the preprocessor that selects whether to include or not a chunk of code in the final text file that will be passed to the compiler. Preprocessor conditional directives can check arithmetic expressions or whether a name is defined as a macro.<\/p>\n<p>In the following example, a message is written to the standard output stream when the program is compiled using a debug configuration and the <tt>_DEBUG<\/tt> macro is defined.<\/p>\n<pre class=\"lang:c++ decode:true \">#define TRACE(x) std::cout &lt;&lt; x &lt;&lt; std::endl\r\n\r\nint main()\r\n{\r\n#ifdef _DEBUG\r\n   TRACE(\"debug build\");\r\n#endif\r\n}<\/pre>\n<p>In C++17 this can be replaced with <tt>constexpr if<\/tt> as shown in the following example:<\/p>\n<pre class=\"lang:c++ decode:true \">#include &lt;string_view&gt;\r\n\r\ninline void trace(std::string_view text)\r\n{\r\n   std::cout &lt;&lt; text &lt;&lt; std::endl;\r\n}\r\n\r\nint main()\r\n{\r\n   if constexpr(_DEBUG)\r\n      trace(\"debug build\");\r\n}<\/pre>\n<p><a name=\"policy-like-macros\"><\/a><\/p>\n<h3>Policy-like macros<\/h3>\n<p>OK, that&#8217;s not a term you see in the literature, but I couldn&#8217;t find something better, and that looks the closest to what we have here. Let&#8217;s look at the following example and exaplain what we&#8217;re doing.<\/p>\n<p>The goal is to trace the execution of functions. We want a message to be displayed to the console when the function starts and another one when the function stops. The first message should show the function name and the current time, and the end message must show the function name, the current time and duration of the function execution. The class <tt>Tracer<\/tt> defines a conversion constructor, that prints a message to the console, and records a start time point, and a custom destructor, that computes the time since the constructor was called and prints another message to the console. Defining objects of this type at the beginning of a function will have the result that a message is printed after the function execution started and another one just before it ends. However, we only want to do that in some cases, when a particular macro name (called <tt>MONITORING<\/tt> in this example) is defined. This can be defined either in code, or passed as an argument to the compiler (like <tt>-DMONITORING<\/tt>). This goal can be achieved using macros, as in the following example:<\/p>\n<pre class=\"lang:c++ decode:true \">#include &lt;iostream&gt;\r\n#include &lt;string&gt;\r\n#include &lt;string_view&gt;\r\n#include &lt;chrono&gt;\r\n#include \"date.h\"\r\n#include &lt;ctime&gt;\r\n#include &lt;thread&gt;\r\n\r\n#define MONITOR()  Tracer tracer__LINE__(__FUNCTION__)\r\n\r\nclass Tracer\r\n{   \r\npublic:\r\n   Tracer(std::string_view function):\r\n      function_name(function),\r\n      start_time(std::chrono::system_clock::now())\r\n   {\r\n      using namespace date;\r\n      using namespace std::chrono;\r\n\r\n      std::cout &lt;&lt; \"BEGIN [\" &lt;&lt; function_name &lt;&lt; \"] at \" &lt;&lt; start_time &lt;&lt; std::endl;\r\n   }\r\n\r\n   ~Tracer()\r\n   {\r\n      using namespace date;\r\n      using namespace std::chrono;\r\n\r\n      auto end_time = std::chrono::system_clock::now();\r\n      auto diff = duration_cast&lt;milliseconds&gt;(end_time - start_time).count();\r\n\r\n      std::cout &lt;&lt; \"END   [\" &lt;&lt; function_name &lt;&lt; \"] at \" &lt;&lt; end_time \r\n                &lt;&lt; \" (duration \" &lt;&lt; diff &lt;&lt; \"ms)\" &lt;&lt; std::endl;\r\n   }\r\n\r\nprivate:\r\n   std::string                            function_name;\r\n   std::chrono::system_clock::time_point  start_time;\r\n};\r\n\r\n#ifdef MONITORING\r\n#define MONITOR_FUNCTION()    MONITOR()\r\n#else\r\n#define MONITOR_FUNCTION()\r\n#endif\r\n\r\nvoid foo()\r\n{\r\n   MONITOR_FUNCTION();\r\n\r\n   std::cout &lt;&lt; \"executing...\" &lt;&lt; std::endl;\r\n\r\n   using namespace std::chrono_literals;\r\n   std::this_thread::sleep_for(1s);\r\n}\r\n\r\nint main()\r\n{\r\n   foo();\r\n}<\/pre>\n<p>If you run this program having <tt>MONITORING<\/tt> defined, the output looks like the following:<\/p>\n<pre class=\"lang:default decode:true \">BEGIN [foo] at 2018-04-18 19:12:07.7385896\r\nexecuting...\r\nEND   [foo] at 2018-04-18 19:12:08.7475495 (duration 1008ms)<\/pre>\n<p>Should <tt>MONITORING<\/tt> not be defined, the output is simply<\/p>\n<pre class=\"lang:default decode:true \">executing...\r\n<\/pre>\n<p>Using <tt>constexpr if<\/tt> is not possible in this situation, because that would introduce an inner scope. In other words, the following example:<\/p>\n<pre class=\"lang:default decode:true \">void foo()\r\n{\r\n   if constexpr(MONITORING)\r\n      Tracer tracer(__FUNCTION__);\r\n\r\n   std::cout &lt;&lt; \"executing...\" &lt;&lt; std::endl;\r\n\r\n   using namespace std::chrono_literals;\r\n   std::this_thread::sleep_for(1s);\r\n}<\/pre>\n<p>would result in the following code being generated<\/p>\n<pre class=\"lang:default decode:true \">void foo()\r\n{\r\n   {\r\n      Tracer tracer(__FUNCTION__);\r\n   }\r\n\r\n   std::cout &lt;&lt; \"executing...\" &lt;&lt; std::endl;\r\n\r\n   using namespace std::chrono_literals;\r\n   std::this_thread::sleep_for(1s);\r\n}<\/pre>\n<p>As a result, the <tt>Tracer<\/tt> object would be created and immediatelly destroyed at the beginning of the function.<\/p>\n<p>A solution for this problem is to use <a href=\"https:\/\/en.wikipedia.org\/wiki\/Policy-based_design\">policy-based design<\/a>. We can define policies, i.e. classes, that perform or do not perform any tracing. The <tt>foo()<\/tt> function would become a function template, parameterized with the monitoring policy. Then, we can use <tt>std::conditional<\/tt> to select between policies at compile time based on a condition. That condition would be the availability of the <tt>MONITORING<\/tt> macro name. This can be passed as a compiler argument, or else it will be defined as 0 in the code. Here is how the example could look in this case:<\/p>\n<pre class=\"lang:c++ decode:true \">#ifndef MONITORING\r\n#define MONITORING 0\r\n#endif\r\n\r\nclass Tracer\r\n{   \r\npublic:\r\n   Tracer(std::string_view function):\r\n      function_name(function),\r\n      start_time(std::chrono::system_clock::now())\r\n   {\r\n      using namespace date;\r\n      using namespace std::chrono;\r\n\r\n      std::cout &lt;&lt; \"BEGIN [\" &lt;&lt; function_name &lt;&lt; \"] at \" &lt;&lt; start_time &lt;&lt; std::endl;\r\n   }\r\n\r\n   ~Tracer()\r\n   {\r\n      using namespace date;\r\n      using namespace std::chrono;\r\n\r\n      auto end_time = std::chrono::system_clock::now();\r\n      auto diff = duration_cast&lt;milliseconds&gt;(end_time - start_time).count();\r\n\r\n      std::cout &lt;&lt; \"END   [\" &lt;&lt; function_name &lt;&lt; \"] at \" &lt;&lt; end_time \r\n                &lt;&lt; \" (duration \" &lt;&lt; diff &lt;&lt; \"ms)\" &lt;&lt; std::endl;\r\n   }\r\n\r\nprivate:\r\n   std::string                            function_name;\r\n   std::chrono::system_clock::time_point  start_time;\r\n};\r\n\r\nstruct standard_monitor\r\n{\r\n   standard_monitor(std::string_view function):t(function)\r\n   {}\r\nprivate:\r\n   Tracer t;\r\n};\r\n\r\nstruct no_monitor\r\n{\r\n   no_monitor(std::string_view function) {}\r\n};\r\n\r\ntemplate &lt;typename MonitorType&gt;\r\nvoid foo()\r\n{\r\n   MonitorType mt(__FUNCTION__);\r\n\r\n   std::cout &lt;&lt; \"executing...\" &lt;&lt; std::endl;\r\n\r\n   using namespace std::chrono_literals;\r\n   std::this_thread::sleep_for(1s);\r\n}\r\n\r\nusing monitor_type = std::conditional&lt;MONITORING, standard_monitor, no_monitor&gt;::type;\r\n\r\nint main()\r\n{\r\n   foo&lt;monitor_type&gt;();\r\n}<\/pre>\n<p>We are still left with two macros: <tt>MONITORING<\/tt> to select one policy or another, and <tt>__FUNCTION__<\/tt> to get the undecorated name of the enclosing function. There is no way to replace the former for the time being, but for the latter, there is something under review in library fundamentals Technical Specification v2, called <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/experimental\/source_location\">std::experimental::source_location<\/a>. This will provide information about the source code, such as the line number and enclosing function name. Using this special built-in class, we would be able to get rid of the <tt>__FUNCTION__<\/tt> special macro as following:<\/p>\n<pre class=\"lang:c++ decode:true \">struct standard_monitor\r\n{\r\n   standard_monitor(std::experimental::source_location loc = std::experimental::source_location::current())\r\n      :t(loc.function_name())\r\n   {}\r\nprivate:\r\n   Tracer t;\r\n};\r\n\r\nstruct no_monitor\r\n{\r\n   no_monitor() {}\r\n};\r\n\r\ntemplate &lt;typename MonitorType&gt;\r\nvoid foo()\r\n{\r\n   MonitorType mt;\r\n\r\n   std::cout &lt;&lt; \"executing...\" &lt;&lt; std::endl;\r\n\r\n   using namespace std::chrono_literals;\r\n   std::this_thread::sleep_for(1s);\r\n}\r\n\r\nusing monitor_type = std::conditional&lt;MONITORING, standard_monitor, no_monitor&gt;::type;\r\n\r\nint main()\r\n{\r\n   foo&lt;monitor_type&gt;();\r\n}<\/pre>\n<p><a name=\"including-files\"><\/a><\/p>\n<h3>Bonus: including files<\/h3>\n<p>Including files is definitely the most common preprocessor functionality. Is there an alternative to it? Not yet, but one is in work. It&#8217;s called modules and a technical specification is in work. It is likely that a first version will be available in C++20. Basically, the <tt>#include<\/tt> directive for headers, will be replaced with <tt>import<\/tt> directives for modules. Here is a very simple example:<\/p>\n<pre class=\"lang:c++ decode:true \">#include &lt;iostream&gt;\r\n\r\nint main()\r\n{\r\n   std::cout &lt;&lt; \"hello, world!\" &lt;&lt; std::endl;\r\n}<\/pre>\n<p>With modules available, this can be changed as follows:<\/p>\n<pre class=\"lang:c++ decode:true \">import std.core;\r\n\r\nint main()\r\n{\r\n   std::cout &lt;&lt; \"hello, world!\" &lt;&lt; std::endl;\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>The C++ preprocessor is a text replacement tool used to transform the source code in order to produce a single text file that is then passed to the actual compiler. It has various capabilities, such as including files, conditional compilation, text macro replacement, error emitting, stringizing, or token concatenation. Often developers use the preprocessor when other alternatives are available and are more appropriate. In this article, I will show five examples of when and how you can avoid the use of the preprocessor.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[7],"tags":[451,554,553,323],"class_list":["post-2857","post","type-post","status-publish","format-standard","hentry","category-c","tag-c","tag-macros","tag-preprocessor","tag-templates"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"This article provides several examples where the C++ preprocessor can be replaced with alternative solutions.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Marius Bancila\"\/>\n\t<meta name=\"keywords\" content=\"c++,preprocessor,macros,templates\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Marius Bancila&#039;s Blog | About code. Mostly on C++\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Five examples for avoid the preprocessor\" \/>\n\t\t<meta property=\"og:description\" content=\"This article provides several examples where the C++ preprocessor can be replaced with alternative solutions.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2018-04-19T17:00:55+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2018-04-20T07:50:25+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Five examples for avoid the preprocessor\" \/>\n\t\t<meta name=\"twitter:description\" content=\"This article provides several examples where the C++ preprocessor can be replaced with alternative solutions.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/#article\",\"name\":\"Five examples for avoid the preprocessor\",\"headline\":\"Five examples for avoiding the preprocessor\",\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"},\"datePublished\":\"2018-04-19T19:00:55+02:00\",\"dateModified\":\"2018-04-20T09:50:25+02:00\",\"inLanguage\":\"en-US\",\"commentCount\":7,\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/#webpage\"},\"articleSection\":\"C++, C++, macros, preprocessor, templates\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"position\":2,\"name\":\"IT\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"position\":3,\"name\":\"Software\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"position\":4,\"name\":\"C++\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/#listItem\",\"name\":\"Five examples for avoiding the preprocessor\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/#listItem\",\"position\":5,\"name\":\"Five examples for avoiding the preprocessor\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/\",\"name\":\"Marius Bancila\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Marius Bancila\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/#webpage\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/\",\"name\":\"Five examples for avoid the preprocessor\",\"description\":\"This article provides several examples where the C++ preprocessor can be replaced with alternative solutions.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/04\\\/19\\\/five-examples-for-avoiding-the-preprocessor\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"datePublished\":\"2018-04-19T19:00:55+02:00\",\"dateModified\":\"2018-04-20T09:50:25+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Five examples for avoid the preprocessor","description":"This article provides several examples where the C++ preprocessor can be replaced with alternative solutions.","canonical_url":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/","robots":"max-image-preview:large","keywords":"c++,preprocessor,macros,templates","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/#article","name":"Five examples for avoid the preprocessor","headline":"Five examples for avoiding the preprocessor","author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"},"datePublished":"2018-04-19T19:00:55+02:00","dateModified":"2018-04-20T09:50:25+02:00","inLanguage":"en-US","commentCount":7,"mainEntityOfPage":{"@id":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/#webpage"},"isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/#webpage"},"articleSection":"C++, C++, macros, preprocessor, templates"},{"@type":"BreadcrumbList","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","position":1,"name":"Home","item":"https:\/\/mariusbancila.ro\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","position":2,"name":"IT","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","position":3,"name":"Software","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","position":4,"name":"C++","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/#listItem","name":"Five examples for avoiding the preprocessor"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/#listItem","position":5,"name":"Five examples for avoiding the preprocessor","previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"}}]},{"@type":"Organization","@id":"https:\/\/mariusbancila.ro\/blog\/#organization","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","url":"https:\/\/mariusbancila.ro\/blog\/"},{"@type":"Person","@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author","url":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/","name":"Marius Bancila","image":{"@type":"ImageObject","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g","width":96,"height":96,"caption":"Marius Bancila"}},{"@type":"WebPage","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/#webpage","url":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/","name":"Five examples for avoid the preprocessor","description":"This article provides several examples where the C++ preprocessor can be replaced with alternative solutions.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/#breadcrumblist"},"author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"creator":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"datePublished":"2018-04-19T19:00:55+02:00","dateModified":"2018-04-20T09:50:25+02:00"},{"@type":"WebSite","@id":"https:\/\/mariusbancila.ro\/blog\/#website","url":"https:\/\/mariusbancila.ro\/blog\/","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","inLanguage":"en-US","publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Marius Bancila's Blog | About code. Mostly on C++","og:type":"article","og:title":"Five examples for avoid the preprocessor","og:description":"This article provides several examples where the C++ preprocessor can be replaced with alternative solutions.","og:url":"https:\/\/mariusbancila.ro\/blog\/2018\/04\/19\/five-examples-for-avoiding-the-preprocessor\/","article:published_time":"2018-04-19T17:00:55+00:00","article:modified_time":"2018-04-20T07:50:25+00:00","twitter:card":"summary","twitter:title":"Five examples for avoid the preprocessor","twitter:description":"This article provides several examples where the C++ preprocessor can be replaced with alternative solutions."},"aioseo_meta_data":{"post_id":"2857","title":"Five examples for avoid the preprocessor","description":"This article provides several examples where the C++ preprocessor can be replaced with alternative solutions.","keywords":[{"label":"C++","value":"C++"},{"label":"preprocessor","value":"preprocessor"},{"label":"macros","value":"macros"},{"label":"templates","value":"templates"}],"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"location":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":null,"created":"2021-03-18 21:04:51","updated":"2025-12-12 07:49:13","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"author_meta":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"featured_img":null,"jetpack_publicize_connections":[],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"uagb_comment_info":7,"uagb_excerpt":"The C++ preprocessor is a text replacement tool used to transform the source code in order to produce a single text file that is then passed to the actual compiler. It has various capabilities, such as including files, conditional compilation, text macro replacement, error emitting, stringizing, or token concatenation. Often developers use the preprocessor when&hellip;","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">macros<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">preprocessor<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">templates<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">macros<\/span>","<span class=\"advgb-post-tax-term\">preprocessor<\/span>","<span class=\"advgb-post-tax-term\">templates<\/span>"]}},"comment_count":"7","relative_dates":{"created":"Posted 8 years ago","modified":"Updated 8 years ago"},"absolute_dates":{"created":"Posted on April 19, 2018","modified":"Updated on April 20, 2018"},"absolute_dates_time":{"created":"Posted on April 19, 2018 7:00 pm","modified":"Updated on April 20, 2018 9:50 am"},"featured_img_caption":"","series_order":"","jetpack_shortlink":"https:\/\/wp.me\/pYNdv-K5","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/2857","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/comments?post=2857"}],"version-history":[{"count":14,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/2857\/revisions"}],"predecessor-version":[{"id":2902,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/2857\/revisions\/2902"}],"wp:attachment":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/media?parent=2857"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/categories?post=2857"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/tags?post=2857"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}