<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Alexander's Programming Tips]]></title><description><![CDATA[I'm a C++ and Rust developer interested in systems and GUI programming, modern programming techniques, and evolution of safe, reliable development practices.]]></description><link>https://blog.shaduri.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 19 Apr 2026 14:17:55 GMT</lastBuildDate><atom:link href="https://blog.shaduri.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Writing a Compile-Time CSV Parser in C++, Part 2: The Parser]]></title><description><![CDATA[Introduction
In part 1 of this article I described the constexpr feature, a C++ facility to perform compile-time evaluation.
In this part, I describe the how to write a library for compile-time (and runtime) parsing of CSV files called Csv::Parser:
h...]]></description><link>https://blog.shaduri.dev/writing-a-compile-time-csv-parser-in-c-part-2-the-parser</link><guid isPermaLink="true">https://blog.shaduri.dev/writing-a-compile-time-csv-parser-in-c-part-2-the-parser</guid><category><![CDATA[C++]]></category><category><![CDATA[compiler]]></category><dc:creator><![CDATA[Alexander Shaduri]]></dc:creator><pubDate>Mon, 15 Feb 2021 15:18:04 GMT</pubDate><content:encoded><![CDATA[<h1 id="introduction">Introduction</h1>
<p>In <a target="_blank" href="https://ashaduri.hashnode.dev/writing-a-compile-time-csv-parser-in-c-part-1-constexpr-overview">part 1</a> of this article I described the <code>constexpr</code> feature, a C++ facility to perform compile-time evaluation.</p>
<p>In this part, I describe the how to write a library for compile-time (and runtime) parsing of CSV files called <a target="_blank" href="https://github.com/ashaduri/csv-parser">Csv::Parser</a>:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/ashaduri/csv-parser">https://github.com/ashaduri/csv-parser</a></div>
<h1 id="writing-a-csv-parser">Writing a CSV Parser</h1>
<h3 id="why-a-compile-time-parser">Why a Compile-Time Parser?</h3>
<p>When I started writing the CSV parser library, I was focused on implementing a runtime parser in modern C++. However, while working on the code, I realized that almost all of the code I was writing could also be used in <code>constexpr</code> context!</p>
<p>Some of the benefits of having a compile-time parser are:</p>
<ul>
<li>You can embed the CSV data in your program and have the values immediately available at runtime without additional computational overhead.</li>
<li>You can perform tests on the embedded data, verifying its integrity at compile-time.</li>
<li>You can test the parser code at compile-time without running any tests.</li>
<li>Due to <code>constexpr</code> nature of the parser, you can still use it at runtime.</li>
</ul>
<h3 id="the-csv-format">The CSV Format</h3>
<p>CSV  (comma-separated values) format, while very common, has quite a few variations. The format is formally documented in <a target="_blank" href="https://www.ietf.org/rfc/rfc4180.txt">RFC 4180</a>. With many variations available, the RFC states that when processing CSV files,</p>
<blockquote>
<p>Implementors should "be conservative in what you do, be liberal in what you accept from others".</p>
</blockquote>
<p>The general format is defined as follows:</p>
<ul>
<li><p>The record lines are delimited by a line break (CRLF). Implementations may use other line break formats (LF, CR). The line break on the last line is optional. Example:</p>
<pre><code class="lang-CSV">aaa,bbb,ccc CRLF
zzz,yyy,xxx CRLF
</code></pre>
</li>
<li><p>Fields are separated by commas. There is no trailing comma on a record line. Space is considered a part of a field. Each line must contain the same number of fields.</p>
</li>
<li><p>Each field <em>may</em> be enclosed by double-quotes. The enclosing double-quotes are mandatory if the field contains line breaks, commas, or double-quotes.</p>
</li>
<li><p>Double-quotes are escaped by repeating them:</p>
<pre><code class="lang-CSV">"this field has "" double "" quotes",second field CRLF
"this field has , comma and CRLF
newline","last field"
</code></pre>
</li>
</ul>
<p>Our parser fully supports the RFC 4180, while accepting other line break formats, ignoring whitespace around quoted values, and allowing escaped quotes inside unquoted fields. This is done to ensure the ability to load files from different implementations.</p>
<h3 id="how-to-represent-input-and-output-data">How to Represent Input and Output Data</h3>
<p>C++17 added a new type <code>std::string_view</code> which refers to a constant contiguous sequence of characters. A typical implementation holds only two members: <code>const char*</code> (a pointer to the start of a data chunk) and a size. Plainly speaking, it's a section of a string-like sequence without mandatory terminating null. Luckily for us, <code>std::string_view</code> fully supports <code>constexpr</code>!</p>
<p>Unlike <code>std::string</code>, <code>std::string_view</code> does not own the data it points to, so great care is required to ensure that the pointed data is not destroyed while operating on <code>std::string_view</code>. Of course, in compile-time context, we can point <code>std::string_view</code> to a static data (embedded in binary file) so the input data lifetime is no longer an issue.</p>
<p>Having the <code>std::string_view</code> facility, we can define the input:</p>
<pre><code class="lang-C++"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>::string_view_literals;

<span class="hljs-comment">// The "sv" user-defined literal specifies a string_view</span>
<span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> input = <span class="hljs-string">"ab,cd\r\nef,gh"</span>sv;
</code></pre>
<p>But how do we represent the individual cells in parser output? We cannot use <code>constexpr std::string</code> in C++17 and it would also be inefficient to store all these strings again. Once again, we can use a collection of <code>std::string_view</code> objects pointing to the original input!</p>
<p>Depending on whether we want the result at compile-time or runtime, we can use two-dimensional either <code>std::array</code>, or <code>std::vector</code> as output.</p>
<p>Notice that I didn't mention what happens when we have to collapse duplicate double-quotes inside the field data. I will discuss a method of dealing with this issue later in the article.</p>
<h3 id="lets-start-with-tests">Let's Start With Tests</h3>
<p>Starting with a "write test first" approach, we should define how we would like to use the CSV parser in compile-time and runtime contexts.</p>
<p>Note that I chose to use "a collection of columns" style of representing the 2D data (as opposed to a "a collection of rows"), due to the fact that large numeric CSV data is usually represented this way.</p>
<p>Keeping <code>constexpr</code> specifics in mind, a simple compile-time interface could look like:</p>
<pre><code class="lang-C++"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>::string_view_literals;

<span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> data = <span class="hljs-string">"ab,cd\r\nef,gh"</span> sv;
<span class="hljs-keyword">constexpr</span> <span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> columns = <span class="hljs-number">2</span>, rows = <span class="hljs-number">2</span>;

<span class="hljs-keyword">constexpr</span> Parser parser;
<span class="hljs-comment">// parse into std::array&lt;std::array&lt;std::string_view, rows&gt;, columns&gt;</span>
<span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> matrix = parser.parseTo2DArray&lt;columns, rows&gt;(data);

<span class="hljs-comment">// Verify the data at compile time.</span>
<span class="hljs-keyword">static_assert</span>(matrix[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>] == <span class="hljs-string">"ab"</span>sv);
<span class="hljs-comment">// ...</span>
</code></pre>
<p>On the other hand, at runtime we should be able to use dynamic features as well:</p>
<pre><code class="lang-C++"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>::string_view_literals;

<span class="hljs-keyword">auto</span> data = <span class="hljs-string">"ab,cd\r\nef,gh"</span> sv;

<span class="hljs-comment">// Let "cell_refs" be a vector of columns.</span>
<span class="hljs-comment">// After parsing, each element will be referencing</span>
<span class="hljs-comment">// a part of the original data.</span>
<span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;<span class="hljs-built_in">std</span>::string_view&gt;&gt; cell_refs;

Parser parser;

<span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// parseTo() throws ParseError on error.</span>
    parser.parseTo(data, cell_refs);
}
<span class="hljs-keyword">catch</span>(ParseError&amp; ex) {
    <span class="hljs-comment">// ... handle the error</span>
}

assert(cell_refs[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>] == <span class="hljs-string">"ab"</span>sv);  <span class="hljs-comment">// Runtime assertion</span>
</code></pre>
<h3 id="the-parser-function-interface">The Parser Function Interface</h3>
<p>Since we need the parser to be able to operate on both <code>std::vector</code> (at runtime) and <code>std::array</code> (at compile-time), we begin by writing a generic <code>parse()</code> function that can be used in both contexts. To do this, we can use a callback to store the parsed results.</p>
<pre><code class="lang-C++"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Parser</span> {</span>
    <span class="hljs-keyword">public</span>:

    <span class="hljs-comment">/// Parse CSV string data and store the results using a callback</span>
    <span class="hljs-comment">/// function.</span>
    <span class="hljs-comment">/// Callback function signature:</span>
    <span class="hljs-comment">/// void func(std::size_t row, std::size_t column, std::string_view cell_data).</span>
    <span class="hljs-comment">/// \throws ParseError</span>
    <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> StoreCellFunction&gt;
    <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">void</span> <span class="hljs-title">parse</span><span class="hljs-params">(<span class="hljs-built_in">std</span>::string_view data, StoreCellFunction func)</span> <span class="hljs-keyword">const</span>
    </span>{
        <span class="hljs-comment">// Iterate over data character by character,</span>
        <span class="hljs-comment">// invoking func(row, column, cell_data) each time a cell</span>
        <span class="hljs-comment">// is fully parsed.</span>
    }

    <span class="hljs-comment">// ...</span>
};
</code></pre>
<p>A few benefits of this approach are:</p>
<ul>
<li><code>parse()</code> does not depend on any particular output container type.</li>
<li>By throwing an exception on error, we make sure that in compile-time context, parse error triggers a compile-time error if the input data is invalid.</li>
</ul>
<p>The actual <code>parse()</code> function implementation can be seen in 
<a target="_blank" href="https://github.com/ashaduri/csv-parser/blob/main/csv_parser/csv_parser.h">csv_parser.h at GutHub</a></p>
<h3 id="implementing-parseto2darray-and-parseto">Implementing <code>parseTo2DArray()</code> and <code>parseTo()</code></h3>
<p>Now that we have a <code>constexpr parse()</code> function, we can use it to define user-friendly methods.</p>
<p>A simple <code>Parser::parseTo2DArray()</code> implementation would look like:</p>
<pre><code class="lang-C++"><span class="hljs-comment">/// Parse CSV string into an array of columns.</span>
<span class="hljs-comment">/// This method wraps parseTo() to simplify compile-time parsing.</span>
<span class="hljs-comment">/// \return std::array&lt;std::array&lt;std::string_view, rows&gt;, columns&gt;</span>
<span class="hljs-comment">/// \throws ParseError</span>
<span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> columns, <span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> rows&gt;
<span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> <span class="hljs-title">Parser::parseTo2DArray</span><span class="hljs-params">(<span class="hljs-built_in">std</span>::string_view data)</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">array</span>&lt;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">array</span>&lt;<span class="hljs-built_in">std</span>::string_view, rows&gt;, columns&gt; matrix;

    parse(data,
        [&amp;matrix](<span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> row, <span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> column,
            <span class="hljs-built_in">std</span>::string_view cell_data) <span class="hljs-keyword">constexpr</span>
        {
            matrix[column][row] = cell_data;
        }
    );

    <span class="hljs-keyword">return</span> matrix;
}
</code></pre>
<p>Note that we cannot pass <code>columns</code> and <code>rows</code> as ordinary arguments. Even though we're in <code>constexpr</code> context, <code>constexpr</code> functions can still be called at runtime and passing runtime values as (compile-time) template arguments to <code>std::array</code> is an error.</p>
<p>As for dynamic version, <code>Parser::parseTo()</code> function accepting various container types would look like:</p>
<pre><code class="lang-C++"><span class="hljs-comment">/// Parse CSV string into a vector of columns.</span>
<span class="hljs-comment">/// \tparam Vector2D accepts 2D containers such as</span>
<span class="hljs-comment">/// std::vector&lt;std::vector&lt;std::string_view&gt;&gt;.</span>
<span class="hljs-comment">/// \throws ParseError</span>
<span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> Vector2D&gt;
<span class="hljs-keyword">void</span> <span class="hljs-title">Parser::parseTo</span><span class="hljs-params">(<span class="hljs-built_in">std</span>::string_view data, Vector2D&amp; matrix)</span> <span class="hljs-keyword">const</span>
</span>{
    Vector2D parsed_matrix;

    parse(data,
        [&amp;](<span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> row, <span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> column,
            <span class="hljs-built_in">std</span>::string_view cell_data)
        {
            <span class="hljs-keyword">if</span> (parsed_matrix.size() &lt; (column + <span class="hljs-number">1</span>)) {
                parsed_matrix.resize(column + <span class="hljs-number">1</span>);
            }
            <span class="hljs-keyword">if</span> (parsed_matrix[column].size() &lt; (row + <span class="hljs-number">1</span>)) {
                parsed_matrix[column].resize(row + <span class="hljs-number">1</span>);
            }
            parsed_matrix[column][row] = cell_data;
        }
    );

    <span class="hljs-comment">// To avoid partial result, only touch the "matrix" parameter</span>
    <span class="hljs-comment">// after everything is parsed.</span>
    <span class="hljs-built_in">std</span>::swap(matrix, parsed_matrix);
}
</code></pre>
<p>Notice the exception safety - we avoid touching <code>matrix</code> unless all the data is parsed.</p>
<h3 id="escaped-double-quotes">Escaped Double-Quotes</h3>
<p>I already discussed how we could use <code>std::string_view</code> to point to the original data. However not every field is simply a substring of the original CSV data, as the CSV format has a feature of escaping double-quotes by repeating them.</p>
<p>In runtime environment we could easily solve this problem by introducing a cleanup function like <code>std::string collapseQuotes(std::string_view view)</code>. However, at least for now, <code>std::string</code> is not usable in <code>constexpr</code> world. A simple solution for this is to introduce a new <code>CellStringBuffer&lt;BufferSize&gt;</code> class, a fixed-size buffer to contain the cleaned-up cell value:</p>
<pre><code class="lang-C++"><span class="hljs-comment">/// A helper class for compile-time buffer construction</span>
<span class="hljs-comment">/// and string cleanup</span>
<span class="hljs-keyword">template</span>&lt;<span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> Size&gt;
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CellStringBuffer</span> {</span>
    <span class="hljs-keyword">public</span>:

        <span class="hljs-comment">/// Constructor. Creates a buffer with cleaned-up</span>
        <span class="hljs-comment">/// cell contents.</span>
        <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">explicit</span> <span class="hljs-title">CellStringBuffer</span><span class="hljs-params">(<span class="hljs-built_in">std</span>::string_view cell)</span>
                : <span class="hljs-title">buffer_</span><span class="hljs-params">(prepareBuffer(cell))</span>
        </span>{ }


        <span class="hljs-comment">/// Return string_view to a stored buffer.</span>
        <span class="hljs-comment">/// The returned view has collapsed consecutive</span>
        <span class="hljs-comment">/// double-quotes.</span>
        <span class="hljs-comment">/// \throw std::out_of_range if buffer is of insufficient size</span>
        [[nodiscard]] <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-built_in">std</span>::string_view <span class="hljs-title">getStringView</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
        </span>{
            <span class="hljs-keyword">return</span> {buffer_.buffer.data(), buffer_.size};
        }


    <span class="hljs-keyword">private</span>:

        <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Buffer</span> {</span>
            <span class="hljs-built_in">std</span>::<span class="hljs-built_in">array</span>&lt;<span class="hljs-keyword">char</span>, Size&gt; buffer = { };
            <span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> size = <span class="hljs-number">0</span>;
        };


        <span class="hljs-comment">/// Create a buffer object with cleaned-up input in it</span>
        <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">static</span> Buffer <span class="hljs-title">prepareBuffer</span><span class="hljs-params">(<span class="hljs-built_in">std</span>::string_view input)</span>
        </span>{
            <span class="hljs-keyword">if</span> (Size &lt; input.size()) {
                <span class="hljs-keyword">throw</span> <span class="hljs-built_in">std</span>::logic_error(<span class="hljs-string">"Insufficient buffer size"</span>);
            }
            <span class="hljs-built_in">std</span>::<span class="hljs-built_in">array</span>&lt;<span class="hljs-keyword">char</span>, Size&gt; buffer = { };
            <span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> output_pos = <span class="hljs-number">0</span>;
            <span class="hljs-keyword">for</span> (<span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> input_pos = <span class="hljs-number">0</span>; input_pos &lt; <span class="hljs-built_in">std</span>::min(Size, input.size()); ++input_pos) {
                <span class="hljs-keyword">char</span> c = input[input_pos];
                buffer[output_pos] = c;
                ++output_pos;
                <span class="hljs-keyword">if</span> (c == <span class="hljs-string">'\"'</span> &amp;&amp; (input_pos + <span class="hljs-number">1</span>) &lt; input.size() &amp;&amp; input[input_pos + <span class="hljs-number">1</span>] == <span class="hljs-string">'\"'</span>) {
                    ++input_pos;
                }
            }
            <span class="hljs-keyword">return</span> {buffer, output_pos};
        }


        Buffer buffer_;
};
</code></pre>
<p>Notice that we have to store the cleaned up string length because it may be shorter than the buffer size. Having this data, we can easily construct <code>std::string_view</code> pointing to the buffer contents.</p>
<p>To create a better interface, we can wrap the <code>std::string_view</code>s returned by <code>parseTo2DVector()</code> into a new <code>CellStringReference</code> class and store it in <code>std::array&lt;std::array&lt;CellStringReference, rows&gt;, columns&gt;</code> instead.</p>
<p>The <code>CellStringReference</code> class would look like this:</p>
<pre><code class="lang-C++"><span class="hljs-comment">/// A value of a cell, referencing the data in original CSV text.</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CellStringReference</span> {</span>
    <span class="hljs-keyword">public</span>:

        <span class="hljs-comment">/// Constructor</span>
        <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-title">CellStringReference</span><span class="hljs-params">()</span> </span>= <span class="hljs-keyword">default</span>;

        <span class="hljs-comment">/// Constructor</span>
        <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-title">CellStringReference</span><span class="hljs-params">(<span class="hljs-built_in">std</span>::string_view cell)</span> <span class="hljs-keyword">noexcept</span>
                : <span class="hljs-title">value_</span><span class="hljs-params">(cell)</span>
        </span>{ }

        <span class="hljs-comment">/// Get stored cell reference as string_view.</span>
        <span class="hljs-comment">/// The returned data may contain escaped consecutive</span>
        <span class="hljs-comment">/// double-quotes.</span>
        [[nodiscard]] <span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-built_in">std</span>::string_view <span class="hljs-title">getOriginalStringView</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">noexcept</span>
        </span>{
            <span class="hljs-keyword">return</span> value_;
        }

        <span class="hljs-comment">/// Get a string buffer with collapsed consecutive</span>
        <span class="hljs-comment">/// double-quotes.</span>
        <span class="hljs-comment">/// This function is useful in constexpr context to</span>
        <span class="hljs-comment">/// retrieve cleaned-up cell data.</span>
        <span class="hljs-comment">/// \tparam BufferSize buffer size, has to be at least</span>
        <span class="hljs-comment">/// getOriginalStringView().size().</span>
        <span class="hljs-comment">/// \throw std::logic_error if BufferSize is too small.</span>
        <span class="hljs-keyword">template</span>&lt;<span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> BufferSize&gt;
        [[nodiscard]] <span class="hljs-function"><span class="hljs-keyword">constexpr</span> CellStringBuffer&lt;BufferSize&gt; <span class="hljs-title">getCleanStringBuffer</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
        </span>{
            <span class="hljs-keyword">return</span> CellStringBuffer&lt;BufferSize&gt;(value_);
        }


    <span class="hljs-keyword">private</span>:

        <span class="hljs-comment">/// Stored data</span>
        <span class="hljs-built_in">std</span>::string_view value_;

};
</code></pre>
<p>Putting it all together, we can now remove the escaped quotes at compile-time:</p>
<pre><code class="lang-C++"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>::string_view_literals;

<span class="hljs-keyword">constexpr</span> <span class="hljs-built_in">std</span>::string_view data = <span class="hljs-string">R"(ab""cd,efgh)"</span>sv;
<span class="hljs-keyword">constexpr</span> <span class="hljs-built_in">std</span>::<span class="hljs-keyword">size_t</span> columns = <span class="hljs-number">2</span>, rows = <span class="hljs-number">1</span>;

<span class="hljs-keyword">constexpr</span> Csv::Parser parser;

<span class="hljs-comment">// parse into std::array&lt;std::array&lt;CellStringReference, rows&gt;, columns&gt;</span>
<span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> matrix = parser.parseTo2DArray&lt;columns, rows&gt;(data);

<span class="hljs-comment">// Verify the data at compile-time.</span>
<span class="hljs-keyword">static_assert</span>(matrix[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>].getOriginalStringView() == <span class="hljs-string">"ab\"\"cd"</span>sv);
<span class="hljs-keyword">static_assert</span>(matrix[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>]
    .getCleanStringBuffer&lt;<span class="hljs-string">"ab\"\"cd"</span>sv.size()&gt;()
    .getStringView() == <span class="hljs-string">"with \"quote inside"</span>sv);
</code></pre>
<h3 id="future-improvements">Future Improvements</h3>
<p>As you can see, with some work, even string processing can be done at compile-time.</p>
<p>The above code could be improved further by detecting and parsing a string representation of a IEEE 754 <code>double</code> value at compile-time. Unfortunately, <code>std::strtod()</code> and its family of C library functions cannot be used in <code>constexpr</code> context, so a similar <code>constexpr</code> function would have to be implemented.</p>
<p>Perhaps, with time, all the common algorithms and functions we're used to in C++ and C library will have <code>constexpr</code> versions.</p>
<h1 id="useful-links">Useful Links</h1>
<ul>
<li><a target="_blank" href="https://github.com/ashaduri/csv-parser">Csv::Parser at GitHub</a> </li>
<li><a target="_blank" href="https://www.ietf.org/rfc/rfc4180.txt">RFC 4180 - CSV Format</a></li>
<li><a target="_blank" href="https://en.cppreference.com/w/cpp/language/constexpr">constexpr reference</a></li>
<li><a target="_blank" href="https://en.cppreference.com/w/cpp/compiler_support">Features supported by various C++ compilers</a></li>
<li><a target="_blank" href="https://www.youtube.com/watch?v=PJwd4JLYJJY">CppCon 2017: Ben Deane &amp; Jason Turner “constexpr ALL the Things!” - a compile-time JSON parser</a></li>
</ul>
<h6 id="copyright-c-2021-alexander-shaduri"><em>Copyright © 2021 Alexander Shaduri</em></h6>
]]></content:encoded></item><item><title><![CDATA[Writing a Compile-Time CSV Parser in C++, Part 1: Constexpr Overview]]></title><description><![CDATA[Introduction
In part 1 of the article I discuss the constexpr feature as it's specified in C++17 and C++20.
In part 2 I describe the steps I took to write a library for compile-time (and runtime) parsing of CSV files called Csv::Parser:
https://githu...]]></description><link>https://blog.shaduri.dev/writing-a-compile-time-csv-parser-in-c-part-1-constexpr-overview</link><guid isPermaLink="true">https://blog.shaduri.dev/writing-a-compile-time-csv-parser-in-c-part-1-constexpr-overview</guid><category><![CDATA[C++]]></category><category><![CDATA[compiler]]></category><dc:creator><![CDATA[Alexander Shaduri]]></dc:creator><pubDate>Mon, 15 Feb 2021 13:55:33 GMT</pubDate><content:encoded><![CDATA[<h1 id="introduction">Introduction</h1>
<p>In part 1 of the article I discuss the <code>constexpr</code> feature as it's specified in C++17 and C++20.</p>
<p>In <a target="_blank" href="https://ashaduri.hashnode.dev/writing-a-compile-time-csv-parser-in-c-part-2-the-parser">part 2</a> I describe the steps I took to write a library for compile-time (and runtime) parsing of CSV files called <a target="_blank" href="https://github.com/ashaduri/csv-parser">Csv::Parser</a>:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/ashaduri/csv-parser">https://github.com/ashaduri/csv-parser</a></div>
<h3 id="what-do-i-mean-by-compile-time">What Do I Mean by Compile-Time?</h3>
<p>One of the major strengths of C++, like other compiled-to-native-code languages, is speed. C++ achieves it by compiling the source code to fast, optimized, CPU-native machine code.</p>
<p>While the first ISO standard of C++ (C++98) is a fast language, many developers think that there is more room for efficiency. Move operations, "as if" rule for optimizing out memory allocations, and guaranteed copy elision introduced in later C++ standards are some of the new features which improve the performance of existing code "for free".</p>
<p>However, the quickest calculation is the one that does not happen at all, or, more precisely, happens in advance during compilation.</p>
<p>Templates always provided the standard C++ with some rudimentary capability of compile-time calculation. However, C++11 introduced a whole new feature called <code>constexpr</code> which made compile-time calculation easier, but still tricky and difficult to reason about due to its limitations. C++17 removed most of these limitations and now a lot of ordinary source code can be easily evaluated during compilation, freeing up valuable computational resources on the user's machine.</p>
<h1 id="what-constexpr-does">What <code>constexpr</code> Does</h1>
<p>For sake of simplicity, I will mainly focus on <code>constexpr</code> as it's defined in C++17. Pre-C++17 <code>constexpr</code> is quite limited and not many of C++20 <code>constexpr</code> features have been fully implemented as of GCC 11 / Clang 11.</p>
<p>Marking a function or a variable <code>constexpr</code> declares that it is <em>possible</em> to evaluate the value of the function or variable at compile time. Such values can then be used where only compile-time constant expressions are allowed. Marking a variable declaration <code>constexpr</code> also implies <code>const</code>. <em>(Note: <code>constexpr</code> methods used to be <code>const</code> by default, but this was removed in C++14).</em></p>
<p>The syntax for making a function or a variable <code>constexpr</code> is:</p>
<pre><code class="lang-C++"><span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">int</span> <span class="hljs-title">func</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// must be initialized by a constant expression</span>
<span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">int</span> variable = <span class="hljs-number">1</span>;
</code></pre>
<h3 id="compile-time-or-runtime">Compile-Time or Runtime?</h3>
<p>It is important to understand that simply marking a function <code>constexpr</code> does not mean it will be evaluated during compilation. Whether such functions are evaluated at compile-time or runtime depends on a few things:</p>
<ul>
<li><p>If the result of the function must be available at compile-time as a constant expression, the function is evaluated at compile-time. For example, assigning the function result to a <code>constexpr</code> variable or using it as a non-type template argument forces compile-time evaluation. Of course, for this to work, all the arguments of the <code>constexpr</code> function must be constant expressions as well. Note that if such evaluation is impossible (because, for example, a function argument value is unavailable at compile-time), you will get a compilation error.</p>
</li>
<li><p>If the function depends on runtime values, it will be evaluated at runtime.</p>
</li>
<li><p>If the function depends on compile-time values, but its result is not required to be a constant expression, it is up to the compiler to decide whether it will run the evaluation at compile-time or not. Current compilers fall back to runtime evaluation in this case.</p>
</li>
</ul>
<p>C++20 Standard Library provides a function <a target="_blank" href="https://en.cppreference.com/w/cpp/types/is_constant_evaluated">std::is_constant_evaluated</a> to help you find out whether the evaluation is happening at compile-time or runtime.</p>
<h3 id="a-note-on-constexpr-lambdas">A Note on <code>constexpr</code> Lambdas</h3>
<p>Unlike regular functions, lambda functions are automatically <code>constexpr</code> if the compiler determines that they can be. It may still be useful to mark them explicitly with <code>constexpr</code> keyword to force them to adhere to <code>constexpr</code> rules:</p>
<pre><code class="lang-C++"><span class="hljs-keyword">auto</span> lambda = [](<span class="hljs-keyword">int</span> argument) <span class="hljs-keyword">constexpr</span>
{
    <span class="hljs-comment">// only the operations valid in constexpr context are allowed here</span>
};
</code></pre>
<h3 id="forcing-compile-time-evaluation">Forcing Compile-Time Evaluation</h3>
<p>The only way to guarantee that a <code>constexpr</code> function will be evaluated at compile-time is to use its result as a constant expression. There are a few ways to do this in C++:</p>
<ul>
<li><p>Assign it to a <code>constexpr</code> variable:</p>
<pre><code class="lang-C++"><span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> result = func();
</code></pre>
</li>
<li><p>Provided its result can be used as a non-type template argument, you can do the following:
```C++
template 
constexpr void forceCompileTime() { }</p>
</li>
</ul>
<p>forceCompileTime();</p>
<pre><code>Or something like:
```C++
<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">int</span> N&gt;
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ForceCompileTime</span> {</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> value = N;
};

<span class="hljs-keyword">auto</span> value = ForceCompileTime&lt;func()&gt;::value;
</code></pre><ul>
<li>If you just want to call a few functions at compile-time, you could use <code>constexpr</code> lambda:<pre><code class="lang-C++">[[maybe_unused]] <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> result = [&amp;]() <span class="hljs-keyword">constexpr</span> {
  func1();
  func2();
  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;  <span class="hljs-comment">// or perhaps return func3();</span>
}();
</code></pre>
</li>
</ul>
<h1 id="constexpr-functions-what-can-i-write-inside-them">Constexpr Functions - What Can I Write Inside Them?</h1>
<p><code>constexpr</code> functions are limited in what they can do. Here are a few of these limitations (as of C++17 and C++20):</p>
<ul>
<li><p>A <code>constexpr</code> function cannot call a non-<code>constexpr</code> function. This means that the C standard library is off limits.</p>
</li>
<li><p>The definition of a <code>constexpr</code> function must be available when calling it. This is similar to function templates and <code>inline</code> functions (in fact, <code>constexpr</code> functions are implicitly <code>inline</code>). In practice, this means that the definitions should be placed in header files.</p>
</li>
<li><p>All local variables, parameters, and the return value must be of a <a target="_blank" href="https://en.cppreference.com/w/cpp/named_req/LiteralType">Literal Type</a></p>
</li>
<li><p>A <code>constexpr</code> function cannot be a coroutine.</p>
</li>
<li><p>A <code>constexpr</code> function can be virtual, but only since C++20.</p>
</li>
<li><p><code>goto</code> is not allowed.</p>
</li>
<li><p>Inside a <code>constexpr</code> function, declaration of a variable without initialization is allowed, but only since C++20. The same rule applies to <code>constexpr</code> variables.</p>
</li>
<li><p>Since C++20, <code>try</code> blocks are allowed, but actually throwing an exception at compile-time is also prohibited, causing a compilation error.</p>
</li>
<li><p>C++20 allows some use of <code>std::vector</code> and <code>std::string</code> in <code>constexpr</code> contexts, but neither GCC 11 nor Clang 11 implement it (yet).</p>
</li>
</ul>
<h3 id="testing-and-error-reporting">Testing and Error Reporting</h3>
<p>One useful side effect of evaluating <code>constexpr</code> functions at compile-time is that we can also do some testing during compilation without actually running any unit tests. By catching the errors early on at compile-time, this approach increases the reliability and correctness of the code without being dependent on runtime tests.</p>
<p>For example, we can use <code>static_assert</code> in a test to verify correctness of a function at compile-time:</p>
<pre><code class="lang-C++"><span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">int</span> <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span>
</span>{
    <span class="hljs-keyword">return</span> a + b;
}

<span class="hljs-keyword">static_assert</span>(add(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>) == <span class="hljs-number">3</span>);  <span class="hljs-comment">// OK</span>

<span class="hljs-comment">// The following line fails to compile: </span>
<span class="hljs-keyword">static_assert</span>(add(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>) == <span class="hljs-number">4</span>);
</code></pre>
<p>We can also conditionally throw an exception to trigger a compilation error. For example, to verify that a precondition is met we can write:</p>
<pre><code class="lang-C++"><span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compute</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span>
</span>{
    <span class="hljs-keyword">if</span> (a &gt; b) {
        <span class="hljs-keyword">throw</span> <span class="hljs-built_in">std</span>::logic_error(<span class="hljs-string">"Precondition failed"</span>);
    }
    <span class="hljs-keyword">return</span> a + b;
}

[[maybe_unused]] <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">int</span> result1 = compute(<span class="hljs-number">1</span>, <span class="hljs-number">5</span>);  <span class="hljs-comment">// OK</span>
[[maybe_unused]] <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">int</span> result2 = compute(<span class="hljs-number">50</span>, <span class="hljs-number">2</span>);  <span class="hljs-comment">// Fails to compile</span>
</code></pre>
<p>Note that we cannot use <code>static_assert</code> in <code>compute()</code> because the parameters may or may not be constant expressions - <code>compute()</code> can be used in runtime context as well.</p>
<p>It is important to note that a compiler is not required to issue an error if a <code>constexpr</code> function contains a code path that can only be evaluated at runtime, but is not triggered with particular set of compile-time arguments. The conditional throwing of exception in the example above relies on this behavior.</p>
<h1 id="whats-next">What's Next</h1>
<p>In <a target="_blank" href="https://ashaduri.hashnode.dev/writing-a-compile-time-csv-parser-in-c-part-2-the-parser">part 2</a> of the article I will talk about a way to approach writing a compile-time CSV parser, presenting a Csv::Parser library implemented using <code>constexpr</code>.</p>
<h1 id="useful-links">Useful Links</h1>
<ul>
<li><a target="_blank" href="https://en.cppreference.com/w/cpp/language/constexpr">constexpr reference</a></li>
<li><a target="_blank" href="https://en.cppreference.com/w/cpp/compiler_support">Features supported by various C++ compilers</a></li>
<li><a target="_blank" href="https://www.youtube.com/watch?v=PJwd4JLYJJY">CppCon 2017: Ben Deane &amp; Jason Turner “constexpr ALL the Things!” - a compile-time JSON parser</a></li>
<li><a target="_blank" href="https://github.com/ashaduri/csv-parser">Csv::Parser at GitHub</a> </li>
</ul>
<h6 id="copyright-c-2021-alexander-shaduri"><em>Copyright © 2021 Alexander Shaduri</em></h6>
]]></content:encoded></item><item><title><![CDATA[Easily Create Shared Libraries with CMake (Part 1)]]></title><description><![CDATA[Introduction
When working on a cross-platform C++ or C project, it is easy to get confused by platform differences and scattered information. Writing, compiling, and using shared libraries in a cross-platform manner with a modern build system is one ...]]></description><link>https://blog.shaduri.dev/easily-create-shared-libraries-with-cmake-part-1</link><guid isPermaLink="true">https://blog.shaduri.dev/easily-create-shared-libraries-with-cmake-part-1</guid><category><![CDATA[C++]]></category><category><![CDATA[library]]></category><category><![CDATA[Libraries]]></category><dc:creator><![CDATA[Alexander Shaduri]]></dc:creator><pubDate>Wed, 27 Jan 2021 13:35:56 GMT</pubDate><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>When working on a cross-platform C++ or C project, it is easy to get confused by platform differences and scattered information. Writing, compiling, and using shared libraries in a cross-platform manner with a modern build system is one of the most common problems in development.</p>
<p>Unfortunately, practical information about the actual specifics is quite scarce - most of the information is either outdated, scattered, or incomplete.</p>
<p>In this article I describe a simple way to create a shared library in your C++ / CMake project while accounting for platform differences.</p>
<p>I'm also providing a sample project at GitHub which can be used as a starting point or a reference. The sample project has a few features that you may find useful:</p>
<ol>
<li>It uses modern CMake.</li>
<li>It supports compiling the library either as static or shared.</li>
<li>It provides cross-platform macros for exporting library symbols, supporting Windows and Linux.</li>
</ol>
<p>The project can be found at GitHub:
%[https://github.com/ashaduri/demo-library-simple]</p>
<h2 id="libraries-a-quick-refresher">Libraries - A Quick Refresher</h2>
<p>A library can be either static or shared.¹</p>
<ul>
<li><p>A static library can be thought of as an archive of object files. There isn't a big difference between directly linking all the object files together, and grouping them into several static libraries, linking them into a final executable afterwards.²<br />During the final stages of building the project, static libraries are linked into the executable files (binaries / .exe files, or even shared libraries / .dll files). Therefore, the compiled machine code is readily available as part of the executable files.<br />It is not recommended to distribute static libraries because they may be dependent on specific compiler versions.<br />Static library files usually have <code>.lib</code> (Windows) or <code>.a</code> (Linux, MinGW) extensions.³</p>
</li>
<li><p>A shared (also known as dynamic, dynamically-linked, dynamic-link) library is a file that is searched for and loaded by your operating system's dynamic linker when you run your executable. One of the advantages of shared libraries is reduced memory and disk consumption - if multiple executables use the same shared library, only one copy has to exist on the disk and be loaded into system memory.<br />Shared library files usually have <code>.dll</code> (Windows), <code>.so</code> (Linux), or <code>.dylib</code> (macOS) extensions.</p>
</li>
</ul>
<p>¹: For sake of simplicity, in this article I am not covering C++20 modules, link-time optimization, or import libraries.</p>
<p>²: In reality, unless used, global symbols in static libraries may be optimized out by the linker. This may cause problems, for example, when Windows resource files are compiled into static libraries.</p>
<p>³: <code>.lib</code> extension is also used for <code>.dll</code> companion files called "import libraries" on Windows, used during the linking stage of project build.</p>
<h2 id="default-visibility-of-symbols">Default Visibility of Symbols</h2>
<p>There are a few facilities in C++ to control the visibility of symbols (functions, global variables) between object files. For example, by placing a function into anonymous namespace, you get a guarantee that the function is only visible in the object file it's compiled to, and does not cause conflicts with other, similarly-named functions in other object files.</p>
<p>A symbol in a static library is visible just like any other symbol in your object files - there is usually no need to modify your code.</p>
<p>However, using symbols from shared libraries is a bit more complicated. As of C++20, Standard C++ does not (yet) have a facility to control visibility of shared library symbols across the library boundary. This is where the platform- and compiler-specific behavior occurs:</p>
<ul>
<li><p>Visual C++ treats all symbols inside a shared library as private by default - you cannot use or call them from outside code.</p>
</li>
<li><p>GCC, Clang, MinGW and many other GCC-like compilers treat all symbols as public by default. While this may be very convenient because libraries can be made shared with minimal changes, it also makes the libraries "leak" their private symbols to outside world, which may cause hard-to-diagnose conflicts.⁴</p>
</li>
</ul>
<p>Fortunately, it is easy to make GCC / Clang behave like Visual C++ by simply adding <code>-fvisibility=hidden</code> option during library compilation.</p>
<p>⁴ Treating all symbols as private by default can also help wrap a symbol-leaking C code in a library with safe public interface, avoiding potential symbol conflicts.</p>
<h3 id="cmake-support-for-setting-symbol-visibility">CMake Support for Setting Symbol Visibility</h3>
<p>If using CMake, you don't have to use the visibility compiler option. Instead, simply set the properties<code>C_VISIBILITY_PRESET</code> and <code>CXX_VISIBILITY_PRESET</code> (for C and C++, respectively) to <code>hidden</code> on your library target (called <code>foo_library</code> here):</p>
<pre><code>    set_target_properties(foo_library
        PROPERTIES
            C_VISIBILITY_PRESET hidden
            CXX_VISIBILITY_PRESET hidden
    )
</code></pre><h2 id="exporting-and-importing-symbols">Exporting and Importing Symbols</h2>
<p>Since all library symbols should be private by default, a symbol has to be marked as public / exported for it to be visible form outside the shared library. This is achieved by marking the symbol with a special attribute:</p>
<ul>
<li><code>__declspec(dllexport)</code> is used on Windows (including GCC / MinGW)</li>
<li><code>__attribute__((visibility("default")))</code> is used on most other operating systems with GCC-like compilers.</li>
</ul>
<p>Additionally, on Windows, the outside code that uses these exported symbols has to "import" them from the shared library. When using these symbols, they have to be marked as "imported":</p>
<ul>
<li>__declspec(dllimport)</li>
</ul>
<p>This code illustrates a function declaration which has to be exported from a shared library on Windows:</p>
<pre><code><span class="hljs-comment">// When *building* the shared library,</span>
<span class="hljs-comment">// function declarations have to be written this way:</span>
<span class="hljs-selector-tag">__declspec</span>(dllexport) <span class="hljs-selector-tag">void</span> <span class="hljs-selector-tag">myPublicFunction</span>();
</code></pre><pre><code><span class="hljs-comment">// When *using* the shared library from outside</span>
<span class="hljs-comment">// (by including its header files),</span>
<span class="hljs-comment">// function declarations have to be written this way:</span>
<span class="hljs-selector-tag">__declspec</span>(dllimport) <span class="hljs-selector-tag">void</span> <span class="hljs-selector-tag">myPublicFunction</span>();
</code></pre><p>Obviously, it is impractical to have two header files - one with symbols marked as "exported" and one with the same symbols marked as "imported". A common workaround is to create a macro that abstracts the import / export attributes. Assuming our library is named "foo_library", let's call this macro <code>FOO_LIBRARY_BUILD</code>. Depending on whether <code>FOO_LIBRARY_BUILD</code> is defined or not, the new <code>FOO_LIBRARY_EXPORT</code> macro changes its value to either export or import the symbol:</p>
<pre><code><span class="hljs-meta">#<span class="hljs-meta-keyword">ifdef</span> FOO_LIBRARY_BUILD</span>
    <span class="hljs-comment">// Building the library</span>
    <span class="hljs-meta">#<span class="hljs-meta-keyword">ifdef</span> _WIN32</span>
        <span class="hljs-comment">// Use the Windows-specific export attribute</span>
        <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> FOO_LIBRARY_EXPORT __declspec(dllexport)</span>
    <span class="hljs-meta">#<span class="hljs-meta-keyword">elif</span> __GNUC__ &gt;= 4</span>
        <span class="hljs-comment">// Use the GCC-specific export attribute</span>
        <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> FOO_LIBRARY_EXPORT __attribute__((visibility(<span class="hljs-meta-string">"default"</span>)))</span>
    <span class="hljs-meta">#<span class="hljs-meta-keyword">else</span></span>
        <span class="hljs-comment">// Assume that no export attributes are needed</span>
        <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> FOO_LIBRARY_EXPORT</span>
    <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">else</span></span>
    <span class="hljs-comment">// Using (including) the library</span>
    <span class="hljs-meta">#<span class="hljs-meta-keyword">ifdef</span> _WIN32</span>
        <span class="hljs-comment">// Use the Windows-specific import attribute</span>
        <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> FOO_LIBRARY_EXPORT __declspec(dllimport)</span>
    <span class="hljs-meta">#<span class="hljs-meta-keyword">else</span></span>
        <span class="hljs-comment">// Assume that no import attributes are needed</span>
        <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> FOO_LIBRARY_EXPORT</span>
    <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span>
</code></pre><p>Now that we have defined the helper macro, we can easily mark our public symbols with it. For example, the library header file could contain the following declarations:</p>
<pre><code><span class="hljs-comment">// Automatically export or import a symbol depending</span>
<span class="hljs-comment">// on whether FOO_LIBRARY_BUILD is defined or not.</span>

<span class="hljs-comment">// Make the function available to outside code</span>
<span class="hljs-function">FOO_LIBRARY_EXPORT <span class="hljs-keyword">void</span> <span class="hljs-title">myPublicFunction</span><span class="hljs-params">()</span></span>;

<span class="hljs-comment">// Make the members of the struct available to outside code</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">FOO_LIBRARY_EXPORT</span> <span class="hljs-title">MyStruct</span> {</span>
    <span class="hljs-comment">// member function and data member declarations...</span>
};
</code></pre><p>The code can be easily modified to define <code>FOO_LIBRARY_EXPORT</code> to nothing if static library compilation is required. Our "demo-library-simple" project contains all the necessary enhancements to support both static and shared compilation.</p>
<h3 id="using-cmake-to-handle-foolibrarybuild">Using CMake to Handle FOO_LIBRARY_BUILD</h3>
<p>In CMake, it is easy to define our <code>FOO_LIBRARY_BUILD</code> macro only when building the library:</p>
<pre><code><span class="hljs-selector-tag">target_compile_definitions</span>(foo_library
    PRIVATE
        FOO_LIBRARY_BUILD
)
</code></pre><p>The <code>PRIVATE</code> nature of this definition tells CMake to define this macro only when building the library itself, but not when other CMake targets use this library.</p>
<h2 id="cmake-support">CMake Support</h2>
<p>Now that we have the C/C++ code support, we can write the remainder of our CMake code. The GitHub sample project contains support for compiling the same library either as static or shared, but for the sake of simplicity I describe only the shared part here.</p>
<h3 id="creating-the-library-target">Creating the Library Target</h3>
<p>Assuming our library source and header files are in "foo_library" subdirectory of our project, we start by declaring the <code>foo_library</code> CMake target in <code>foo_library/CMakeLists.txt</code>:</p>
<pre><code><span class="hljs-comment"># List all library sources</span>
<span class="hljs-keyword">set</span>(FOO_LIBRARY_SOURCES
    foo_library.h
    foo_library.cpp
)

<span class="hljs-comment"># Add a shared library target</span>
add_library(foo_library <span class="hljs-keyword">SHARED</span> ${FOO_LIBRARY_SOURCES})

<span class="hljs-comment"># Make all non-exported symbols hidden by default</span>
set_target_properties(foo_library
    PROPERTIES
        CXX_VISIBILITY_PRESET hidden
)

<span class="hljs-comment"># Treat the public symbols as exported</span>
<span class="hljs-comment"># (and not imported) by defining FOO_LIBRARY_BUILD</span>
<span class="hljs-comment"># when building the library.</span>
target_compile_definitions(foo_library
    <span class="hljs-keyword">PRIVATE</span>
        FOO_LIBRARY_BUILD
)

<span class="hljs-comment"># Export the library's public header path to dependent targets</span>
target_include_directories(foo_library
    <span class="hljs-keyword">INTERFACE</span>
        ${CMAKE_CURRENT_SOURCE_DIR}
)
</code></pre><p>Now that we have the shared library compilation down, we can create an executable target (let's call it "demo") and link our library with it:</p>
<pre><code><span class="hljs-comment"># List all sources of the executable</span>
<span class="hljs-built_in">set</span>(DEMO_SOURCES
    demo.cpp
)

<span class="hljs-comment"># Create a CMake target for the executable </span>
add_executable(demo <span class="hljs-variable">${DEMO_SOURCES}</span>)

<span class="hljs-comment"># Link the library with the executable</span>
target_link_libraries(demo
    PRIVATE
        foo_library
)
</code></pre><p>It is important to keep in mind that "link" in CMake's <code>target_link_libraries()</code> is not linking in the classical sense. Apart from the actual linking via linker, it also makes the new target inherit all the <code>PUBLIC</code> and <code>INTERFACE</code> properties of the library.</p>
<h2 id="whats-next">What's Next</h2>
<p>In the next part of the article series I will talk about generalizing the export macro to handle multiple libraries, making sure the executables can find the libraries, setting <code>RPATH</code>, and much more.</p>
<h2 id="notes-on-versions">Notes on Versions</h2>
<p>This article assumes to the following versions of software:</p>
<ul>
<li>CMake 3.0 or later (tested with CMake 3.19).</li>
<li>Visual Studio 2008 or later (tested with Visual Studio 2019).</li>
<li>GCC 4.0 or later (tested with GCC 11).</li>
</ul>
<h2 id="useful-links">Useful Links</h2>
<p>Please check out the links below for more detailed information on the topics discussed above.</p>
<ul>
<li><p><a target="_blank" href="https://github.com/ashaduri/demo-library-simple">Code from the Article: demo-library-simple</a></p>
</li>
<li><p><a target="_blank" href="https://gcc.gnu.org/wiki/Visibility">GCC: Visibility</a></p>
</li>
<li><p><a target="_blank" href="https://docs.microsoft.com/en-us/cpp/build/importing-and-exporting">MSDN: Importing and Exporting</a></p>
</li>
<li><p><a target="_blank" href="https://cmake.org/cmake/help/latest/command/add_library.html">CMake: add_library</a></p>
</li>
<li><p><a target="_blank" href="https://cmake.org/cmake/help/latest/command/target_link_libraries.html">CMake: target_link_libraries</a></p>
</li>
</ul>
<h6 id="copyright-c-2021-alexander-shaduri"><em>Copyright © 2021 Alexander Shaduri</em></h6>
]]></content:encoded></item></channel></rss>