<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>nexenne — Field notes</title>
    <link>https://nexenne.com/</link>
    <description>Technical notes on automotive HMI, embedded firmware, Android and field work.</description>
    <language>en</language>
    <atom:link href="https://nexenne.com/feed.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Fri, 18 Jul 2025 12:00:00 GMT</lastBuildDate>
    <item>
      <title>C++23 Strong Type</title>
      <link>https://nexenne.com/blog/strong_type_cpp/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/strong_type_cpp/</guid>
      <pubDate>Fri, 18 Jul 2025 12:00:00 GMT</pubDate>
      <description><![CDATA[An introduction to strong types in C++, starting from an easy mistake to make.]]></description>
      <category>C++</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>When we use naked primitive types (<code>int</code>, <code>double</code>, and similar), the meaning of values is not explicit. Code becomes more fragile: it is easy to swap two parameters and, in some cases, impossible to express the intended meaning correctly.</p>
<p>To understand the problem, let's start from a very common case.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"></span><span class="ln" data-line="2"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">rectangle</span></span><span class="ln" data-line="3">{</span><span class="ln" data-line="4">    <span class="hljs-built_in">rectangle</span>(<span class="hljs-type">double</span> <span class="hljs-type">const</span> width, <span class="hljs-type">double</span> <span class="hljs-type">const</span> height);</span><span class="ln" data-line="5">    ...</span><span class="ln" data-line="6">};</span><span class="ln" data-line="7"></span></code></pre>
<p>In this example nothing prevents you from accidentally swapping width and height: <code>rectangle(800, 600)</code> and <code>rectangle(600, 800)</code> are both valid for the compiler, but one of them may be semantically wrong. The problem appears only at runtime.</p>
<p>Let's try to make the intent clearer with different parameter names.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"></span><span class="ln" data-line="2"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">circle</span></span><span class="ln" data-line="3">{</span><span class="ln" data-line="4"><span class="hljs-keyword">public</span>:</span><span class="ln" data-line="5">    <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">circle</span><span class="hljs-params">(<span class="hljs-type">double</span> <span class="hljs-type">const</span> radius)</span> : m_radius{</span>radius} {}</span><span class="ln" data-line="6">    <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">circle</span><span class="hljs-params">(<span class="hljs-type">double</span> <span class="hljs-type">const</span> diameter)</span> : m_radius{</span>diameter/<span class="hljs-number">2</span>} {} <span class="hljs-comment">// This doesn't compile! :(</span></span><span class="ln" data-line="7">    ...</span><span class="ln" data-line="8">}</span><span class="ln" data-line="9"></span></code></pre>
<p>Here the two constructors have the same signature (circle(double)): only the parameter names change, and they do not participate in overloading. As a result, the code does not compile, and in any case we don't have distinct types to express Radius and Diameter.</p>
<p>Primitive types do not capture domain semantics. We need a way to give values an identity and make the call site self-explanatory, reducing errors and unexpected behavior.</p>
<p>For this we introduce strong, self-explanatory types: <code>Width</code>, <code>Height</code>, <code>Radius</code>, <code>Diameter</code>, and so on. In the sections that follow we will look at a C++23 header-only library, <code>st::strong_type&#x3C;T, Tag, Ability...></code>, where <code>Tag</code> separates semantic axes and the <code>Ability</code> mixins enable only the desired operators in opt-in mode. This way the compiler helps us express intent and prevent entire classes of bugs.</p>
<h2 id="cos-è-uno-strong-type">What is a strong type?</h2>
<p>A <strong>strong type</strong> is a type wrapper that replaces an underlying type to make the semantics explicit <strong>at the type level</strong> (not just in the parameter name).
With st::strong_type&#x3C;T, Tag, Ability...> we "brand" a T with a Tag (phantom type) that makes it incompatible with other values that are isomorphic but semantically different, and we enable <strong>only</strong> the operations declared in <strong>opt-in</strong> mode through the Ability mixins.</p>
<p>The <strong>strong type</strong> in code:</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">namespace</span> st {</span><span class="ln" data-line="2"><span class="hljs-keyword">template</span>&#x3C;<span class="hljs-keyword">class</span> <span class="hljs-title class_">T</span>, <span class="hljs-keyword">class</span> <span class="hljs-title class_">Tag</span>, <span class="hljs-keyword">template</span>&#x3C;<span class="hljs-keyword">class</span>> <span class="hljs-keyword">class</span>... Ability></span><span class="ln" data-line="3"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">strong_type</span>;</span><span class="ln" data-line="4">} <span class="hljs-comment">// namespace st</span></span></code></pre>
<ul>
<li>T: underlying type (e.g. double, int, std::uint32_t, an enum...).</li>
<li>Tag: empty type that identifies the semantic axis (e.g. width_tag, height_tag).</li>
<li>Ability: <strong>feature mixins</strong> (opt-in operators and utilities). The ready-to-use group st::arithmetic is also available.</li>
</ul>
<h2 id="how-does-it-work">How does it work?</h2>
<ul>
<li>Tag barrier. Binary operators between strong types are allowed only if they share the same <code>Tag</code>. Adding <code>Width</code> and <code>Height</code>? Compilation error.</li>
<li>Ability gating. Each operator/utility is protected by a <code>requires</code>: both operands must declare the required ability, and the underlying type must actually support the operation (checked at the expression level).</li>
<li>Correct constructors. From <code>T</code>: the constructor is <code>explicit</code> by default; it becomes implicit only when the conversion to <code>T</code> is safe (no narrowing). From another strong type with the same <code>Tag</code>: allowed if the underlying types are convertible; the same <code>explicit</code> rules apply.</li>
<li>Disciplined result type. Binary operators return a new strong type with: underlying = <code>std::common_type_t&#x3C;UA, UB></code>; abilities = the union of the abilities of the two operands, filtered based on the new underlying (e.g. shift only on unsigned); same <code>Tag</code>.</li>
<li>Safe shifts. Counts are normalized modulo the bit-width (with <code>assert</code> on negatives in debug), avoiding UB.</li>
<li>Saturating arithmetic. <code>st::sat_add</code> and <code>st::sat_sub</code> clamp to <code>min</code> and <code>max</code> (dedicated signed/unsigned paths) and return a type consistent with the union/filtering rules above.</li>
<li>Access and utilities. <code>x.value()</code> (lvalue/rvalue overloads), <code>st::to_underlying(x)</code> (preserves the value category), <code>st::clamp</code>, <code>explicit operator bool()</code> bool-testable style, <code>st::static_strong_cast(x)</code> (intentional cast between strong types with the same <code>Tag</code>).</li>
<li>STL integration. Consistent specializations of <code>std::hash</code>, <code>std::formatter</code> and <code>std::common_type</code> aligned with the previous rules.</li>
</ul>
<h2 id="an-example">An example</h2>
<p>This intentionally complete example shows the usefulness of a header file dedicated to strong types.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;cstdint></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;expected></span></span></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;format></span></span></span><span class="ln" data-line="4"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="5"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;limits></span></span></span><span class="ln" data-line="6"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;numeric></span></span></span><span class="ln" data-line="7"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;print></span></span></span><span class="ln" data-line="8"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;ranges></span></span></span><span class="ln" data-line="9"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;sstream></span></span></span><span class="ln" data-line="10"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;string_view></span></span></span><span class="ln" data-line="11"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;unordered_map></span></span></span><span class="ln" data-line="12"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;vector></span></span></span><span class="ln" data-line="13"></span><span class="ln" data-line="14"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"strong.h"</span></span></span><span class="ln" data-line="15"></span><span class="ln" data-line="16"><span class="hljs-comment">// tags</span></span><span class="ln" data-line="17"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">distance_tag</span></span><span class="ln" data-line="18">{};</span><span class="ln" data-line="19"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">time_tag</span></span><span class="ln" data-line="20">{};</span><span class="ln" data-line="21"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">flag_bits_tag</span></span><span class="ln" data-line="22">{};</span><span class="ln" data-line="23"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">counter_tag</span></span><span class="ln" data-line="24">{};</span><span class="ln" data-line="25"></span><span class="ln" data-line="26"><span class="hljs-comment">// strong aliases</span></span><span class="ln" data-line="27"><span class="hljs-keyword">using</span> meters</span><span class="ln" data-line="28">    = st::strong_type&#x3C;<span class="hljs-type">int</span>, distance_tag, st::arithmetic, st::equality_comparable, st::three_way_comparable, st::ostream_insertable>;</span><span class="ln" data-line="29"></span><span class="ln" data-line="30"><span class="hljs-keyword">using</span> meters64 = st::</span><span class="ln" data-line="31">    strong_type&#x3C;<span class="hljs-type">long</span> <span class="hljs-type">long</span>, distance_tag, st::arithmetic, st::equality_comparable, st::three_way_comparable, st::ostream_insertable>;</span><span class="ln" data-line="32"></span><span class="ln" data-line="33"><span class="hljs-keyword">using</span> meters_d = st::</span><span class="ln" data-line="34">    strong_type&#x3C;<span class="hljs-type">double</span>, distance_tag, st::arithmetic, st::equality_comparable, st::three_way_comparable, st::ostream_insertable>;</span><span class="ln" data-line="35"></span><span class="ln" data-line="36"><span class="hljs-keyword">using</span> seconds = st::</span><span class="ln" data-line="37">    strong_type&#x3C;<span class="hljs-type">int</span>, time_tag, st::equality_comparable, st::three_way_comparable, st::ostream_insertable, st::istream_extractable>;</span><span class="ln" data-line="38"></span><span class="ln" data-line="39"><span class="hljs-keyword">using</span> flags = st::strong_type&#x3C;std::<span class="hljs-type">uint8_t</span>,</span><span class="ln" data-line="40">                              flag_bits_tag,</span><span class="ln" data-line="41">                              st::bitwise_and,</span><span class="ln" data-line="42">                              st::bitwise_and_assign,</span><span class="ln" data-line="43">                              st::bitwise_or,</span><span class="ln" data-line="44">                              st::bitwise_or_assign,</span><span class="ln" data-line="45">                              st::bitwise_xor,</span><span class="ln" data-line="46">                              st::bitwise_xor_assign,</span><span class="ln" data-line="47">                              st::bitwise_not,</span><span class="ln" data-line="48">                              st::shift_left,</span><span class="ln" data-line="49">                              st::shift_left_assign,</span><span class="ln" data-line="50">                              st::shift_right,</span><span class="ln" data-line="51">                              st::shift_right_assign,</span><span class="ln" data-line="52">                              st::ostream_insertable>;</span><span class="ln" data-line="53"></span><span class="ln" data-line="54"><span class="hljs-keyword">using</span> flag_mask = st::strong_type&#x3C;std::<span class="hljs-type">uint8_t</span>,</span><span class="ln" data-line="55">                                  flag_bits_tag,</span><span class="ln" data-line="56">                                  st::bitwise_and,</span><span class="ln" data-line="57">                                  st::bitwise_or,</span><span class="ln" data-line="58">                                  st::bitwise_xor,</span><span class="ln" data-line="59">                                  st::bitwise_not,</span><span class="ln" data-line="60">                                  st::shift_left,</span><span class="ln" data-line="61">                                  st::shift_right,</span><span class="ln" data-line="62">                                  st::ostream_insertable,</span><span class="ln" data-line="63">                                  st::bitops_extras>;</span><span class="ln" data-line="64"></span><span class="ln" data-line="65"><span class="hljs-keyword">using</span> meters_u</span><span class="ln" data-line="66">    = st::strong_type&#x3C;<span class="hljs-type">unsigned</span>, distance_tag, st::arithmetic, st::saturating, st::equality_comparable, st::ostream_insertable>;</span><span class="ln" data-line="67"></span><span class="ln" data-line="68"><span class="hljs-keyword">using</span> meters_s_sat</span><span class="ln" data-line="69">    = st::strong_type&#x3C;<span class="hljs-type">int</span>, distance_tag, st::arithmetic, st::saturating, st::equality_comparable, st::ostream_insertable>;</span><span class="ln" data-line="70"></span><span class="ln" data-line="71"><span class="hljs-keyword">using</span> counter = st::strong_type&#x3C;<span class="hljs-type">int</span>, counter_tag, st::arithmetic, st::bool_testable, st::ostream_insertable>;</span><span class="ln" data-line="72"></span><span class="ln" data-line="73"><span class="hljs-comment">// helpers</span></span><span class="ln" data-line="74">[[nodiscard]] <span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">average_distance</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector&#x3C;meters> &#x26;path)</span> -> std::expected&#x3C;meters_d, std::string_view></span></span><span class="ln" data-line="75"><span class="hljs-function"></span>{</span><span class="ln" data-line="76">    <span class="hljs-keyword">if</span> (path.<span class="hljs-built_in">empty</span>())</span><span class="ln" data-line="77">        <span class="hljs-keyword">return</span> std::unexpected{<span class="hljs-string">"empty path"</span>};</span><span class="ln" data-line="78">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> total = std::<span class="hljs-built_in">accumulate</span>(</span><span class="ln" data-line="79">        path.<span class="hljs-built_in">begin</span>(), path.<span class="hljs-built_in">end</span>(), meters{<span class="hljs-number">0</span>}, [](<span class="hljs-type">const</span> meters &#x26;a, <span class="hljs-type">const</span> meters &#x26;b) -> meters { <span class="hljs-keyword">return</span> a + b; });</span><span class="ln" data-line="80">    <span class="hljs-keyword">return</span> st::<span class="hljs-built_in">static_strong_cast</span>&#x3C;meters_d>(total) / <span class="hljs-built_in">static_cast</span>&#x3C;<span class="hljs-type">double</span>>(path.<span class="hljs-built_in">size</span>());</span><span class="ln" data-line="81">}</span><span class="ln" data-line="82"></span><span class="ln" data-line="83">[[nodiscard]] <span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">pairwise_sum</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector&#x3C;meters> &#x26;a, <span class="hljs-type">const</span> std::vector&#x3C;meters> &#x26;b)</span></span></span><span class="ln" data-line="84"><span class="hljs-function">    -> std::expected&#x3C;std::vector&#x3C;meters>, std::string_view></span></span><span class="ln" data-line="85"><span class="hljs-function"></span>{</span><span class="ln" data-line="86">    <span class="hljs-keyword">if</span> (a.<span class="hljs-built_in">size</span>() != b.<span class="hljs-built_in">size</span>())</span><span class="ln" data-line="87">        <span class="hljs-keyword">return</span> std::unexpected{<span class="hljs-string">"size mismatch"</span>};</span><span class="ln" data-line="88">    std::vector&#x3C;meters> out;</span><span class="ln" data-line="89">    out.<span class="hljs-built_in">reserve</span>(a.<span class="hljs-built_in">size</span>());</span><span class="ln" data-line="90">    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> &#x26;&#x26;[x, y] : std::views::<span class="hljs-built_in">zip</span>(a, b))</span><span class="ln" data-line="91">        out.<span class="hljs-built_in">push_back</span>(x + y);</span><span class="ln" data-line="92">    <span class="hljs-keyword">return</span> out;</span><span class="ln" data-line="93">}</span><span class="ln" data-line="94"></span><span class="ln" data-line="95"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span></span></span><span class="ln" data-line="96"><span class="hljs-function"></span>{</span><span class="ln" data-line="97">    <span class="hljs-built_in">static_assert</span>(std::same_as&#x3C;<span class="hljs-keyword">decltype</span>(meters{<span class="hljs-number">1</span>} + meters{<span class="hljs-number">2</span>}), meters>);</span><span class="ln" data-line="98">    <span class="hljs-built_in">static_assert</span>(std::same_as&#x3C;std::<span class="hljs-type">common_type_t</span>&#x3C;meters, meters64>::underlying_type, <span class="hljs-type">long</span> <span class="hljs-type">long</span>>);</span><span class="ln" data-line="99"></span><span class="ln" data-line="100">    <span class="hljs-comment">// extra compile-time sanity checks</span></span><span class="ln" data-line="101">    <span class="hljs-built_in">static_assert</span>(std::is_trivially_copyable_v&#x3C;meters>);</span><span class="ln" data-line="102">    <span class="hljs-built_in">static_assert</span>(<span class="hljs-built_in">sizeof</span>(flags) == <span class="hljs-built_in">sizeof</span>(std::<span class="hljs-type">uint8_t</span>));</span><span class="ln" data-line="103"></span><span class="ln" data-line="104">    <span class="hljs-comment">// construction &#x26; conversions</span></span><span class="ln" data-line="105">    meters a = <span class="hljs-number">12</span>;</span><span class="ln" data-line="106">    meters b{<span class="hljs-type">short</span>{<span class="hljs-number">30</span>}};</span><span class="ln" data-line="107">    meters c = meters64{<span class="hljs-number">42</span>};   <span class="hljs-comment">// strong -> strong (same tag)</span></span><span class="ln" data-line="108">    meters_d d{meters64{<span class="hljs-number">100</span>}}; <span class="hljs-comment">// strong -> strong (same tag) to double</span></span><span class="ln" data-line="109"></span><span class="ln" data-line="110">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"a={}, b={}, c={}, d={}"</span>, a, b, c, d);</span><span class="ln" data-line="111"></span><span class="ln" data-line="112">    <span class="hljs-comment">// arithmetic + comparisons</span></span><span class="ln" data-line="113">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> sum{a + b};</span><span class="ln" data-line="114">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> diff{b - a};</span><span class="ln" data-line="115">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> ord{a &#x3C;=> b};</span><span class="ln" data-line="116">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"sum={}, diff={}, cmp={}"</span>, sum, diff, (ord &#x3C; <span class="hljs-number">0</span> ? <span class="hljs-string">"a&#x3C;b"</span> : (ord > <span class="hljs-number">0</span> ? <span class="hljs-string">"a>b"</span> : <span class="hljs-string">"a==b"</span>)));</span><span class="ln" data-line="117"></span><span class="ln" data-line="118">    <span class="hljs-comment">// compound ops / modulo / ++ --</span></span><span class="ln" data-line="119">    <span class="hljs-keyword">auto</span> mwork = meters{<span class="hljs-number">5</span>};</span><span class="ln" data-line="120">    ++mwork;</span><span class="ln" data-line="121">    mwork++;</span><span class="ln" data-line="122">    mwork -= meters{<span class="hljs-number">1</span>};</span><span class="ln" data-line="123">    mwork += meters{<span class="hljs-number">3</span>};</span><span class="ln" data-line="124">    mwork %= meters{<span class="hljs-number">4</span>};</span><span class="ln" data-line="125">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"mwork after ops = {}"</span>, mwork);</span><span class="ln" data-line="126"></span><span class="ln" data-line="127">    <span class="hljs-comment">// scalar rebind (multiply/divide)</span></span><span class="ln" data-line="128">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> scaled = a * <span class="hljs-number">2.5</span>;</span><span class="ln" data-line="129">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> halved = scaled / <span class="hljs-number">2.0</span>;</span><span class="ln" data-line="130">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"scaled={}, halved={}"</span>, scaled, halved);</span><span class="ln" data-line="131"></span><span class="ln" data-line="132">    <span class="hljs-comment">// clamp, unary -, cast, to_underlying</span></span><span class="ln" data-line="133">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> lo{meters{<span class="hljs-number">5</span>}}, hi{meters{<span class="hljs-number">25</span>}};</span><span class="ln" data-line="134">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"clamp({}, {}, {}) = {}"</span>, a, lo, hi, st::<span class="hljs-built_in">clamp</span>(a, lo, hi));</span><span class="ln" data-line="135">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"unary_minus(-{}) = {}"</span>, lo, -lo);</span><span class="ln" data-line="136">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"static_strong_cast&#x3C;double> = {}"</span>, st::<span class="hljs-built_in">static_strong_cast</span>&#x3C;meters_d>(a));</span><span class="ln" data-line="137">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"sum as raw int: {}"</span>, st::<span class="hljs-built_in">to_underlying</span>(sum));</span><span class="ln" data-line="138"></span><span class="ln" data-line="139">    <span class="hljs-comment">// streams &#x26; parsing</span></span><span class="ln" data-line="140">    std::istringstream iss{<span class="hljs-string">"77"</span>};</span><span class="ln" data-line="141">    seconds t{<span class="hljs-number">0</span>};</span><span class="ln" data-line="142">    iss >> t;</span><span class="ln" data-line="143">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"parsed seconds = {}"</span>, t);</span><span class="ln" data-line="144"></span><span class="ln" data-line="145">    <span class="hljs-comment">// unordered_map / hash</span></span><span class="ln" data-line="146">    std::unordered_map&#x3C;meters, std::string_view> names;</span><span class="ln" data-line="147">    names.<span class="hljs-built_in">emplace</span>(a, <span class="hljs-string">"start"</span>);</span><span class="ln" data-line="148">    names.<span class="hljs-built_in">emplace</span>(b, <span class="hljs-string">"end"</span>);</span><span class="ln" data-line="149">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"names[a] = {}"</span>, names.<span class="hljs-built_in">at</span>(a));</span><span class="ln" data-line="150"></span><span class="ln" data-line="151">    <span class="hljs-comment">// bitwise flags</span></span><span class="ln" data-line="152">    <span class="hljs-keyword">auto</span> f = flags{std::<span class="hljs-type">uint8_t</span>{<span class="hljs-number">0b0101</span>}};</span><span class="ln" data-line="153">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> g = flags{std::<span class="hljs-type">uint8_t</span>{<span class="hljs-number">0b0011</span>}};</span><span class="ln" data-line="154">    f |= g;</span><span class="ln" data-line="155">    f &#x26;= flags{std::<span class="hljs-type">uint8_t</span>{<span class="hljs-number">0b0110</span>}};</span><span class="ln" data-line="156">    f ^= flags{std::<span class="hljs-type">uint8_t</span>{<span class="hljs-number">0b0010</span>}};</span><span class="ln" data-line="157">    f &#x3C;&#x3C;= <span class="hljs-number">42</span>; <span class="hljs-comment">// 42 % 8 = 2</span></span><span class="ln" data-line="158">    f >>= <span class="hljs-number">2</span>;</span><span class="ln" data-line="159">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"flags final = {}"</span>, f);</span><span class="ln" data-line="160"></span><span class="ln" data-line="161">    <span class="hljs-comment">// bit-ops extras</span></span><span class="ln" data-line="162">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> mk = flag_mask{std::<span class="hljs-type">uint8_t</span>{<span class="hljs-number">0b1101</span>}};</span><span class="ln" data-line="163">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"mask={}, popcount={}, bit_width={}"</span>, mk, st::<span class="hljs-built_in">popcount</span>(mk), st::<span class="hljs-built_in">bit_width</span>(mk));</span><span class="ln" data-line="164">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"rotl({}, 3)={}, rotr({}, 2)={}"</span>, mk, st::<span class="hljs-built_in">rotl</span>(mk, <span class="hljs-number">3</span>), mk, st::<span class="hljs-built_in">rotr</span>(mk, <span class="hljs-number">2</span>));</span><span class="ln" data-line="165">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"countl_zero={}, countl_one={}, countr_zero={}, countr_one={}"</span>,</span><span class="ln" data-line="166">                 st::<span class="hljs-built_in">countl_zero</span>(mk),</span><span class="ln" data-line="167">                 st::<span class="hljs-built_in">countl_one</span>(mk),</span><span class="ln" data-line="168">                 st::<span class="hljs-built_in">countr_zero</span>(mk),</span><span class="ln" data-line="169">                 st::<span class="hljs-built_in">countr_one</span>(mk));</span><span class="ln" data-line="170"></span><span class="ln" data-line="171">    <span class="hljs-comment">// saturating arithmetic (unsigned + signed)</span></span><span class="ln" data-line="172">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> umax_less = meters_u{std::numeric_limits&#x3C;<span class="hljs-type">unsigned</span>>::<span class="hljs-built_in">max</span>() - <span class="hljs-number">5u</span>};</span><span class="ln" data-line="173">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> uplus = meters_u{<span class="hljs-number">100u</span>};</span><span class="ln" data-line="174">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> usat = st::<span class="hljs-built_in">sat_add</span>(umax_less, uplus);</span><span class="ln" data-line="175">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"sat_add({}, {}) = {}"</span>, umax_less, uplus, usat);</span><span class="ln" data-line="176"></span><span class="ln" data-line="177">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> smin = meters_s_sat{std::numeric_limits&#x3C;<span class="hljs-type">int</span>>::<span class="hljs-built_in">min</span>() + <span class="hljs-number">10</span>};</span><span class="ln" data-line="178">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> sneg = meters_s_sat{<span class="hljs-number">-1000</span>};</span><span class="ln" data-line="179">    <span class="hljs-type">const</span> <span class="hljs-keyword">auto</span> ssat = st::<span class="hljs-built_in">sat_sub</span>(smin, sneg);</span><span class="ln" data-line="180">    std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"sat_sub({}, {}) = {}"</span>, smin, sneg, ssat);</span><span class="ln" data-line="181"></span><span class="ln" data-line="182">    <span class="hljs-comment">// ranges / expected</span></span><span class="ln" data-line="183">    <span class="hljs-type">const</span> std::vector&#x3C;meters> path{meters{<span class="hljs-number">3</span>}, meters{<span class="hljs-number">4</span>}, meters{<span class="hljs-number">5</span>}, meters{<span class="hljs-number">6</span>}};</span><span class="ln" data-line="184">    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">auto</span> avg = <span class="hljs-built_in">average_distance</span>(path))</span><span class="ln" data-line="185">    {</span><span class="ln" data-line="186">        std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"average(path) = {}"</span>, *avg);</span><span class="ln" data-line="187">    }</span><span class="ln" data-line="188">    <span class="hljs-keyword">else</span></span><span class="ln" data-line="189">    {</span><span class="ln" data-line="190">        std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"average(path) error: {}"</span>, avg.<span class="hljs-built_in">error</span>());</span><span class="ln" data-line="191">    }</span><span class="ln" data-line="192">    <span class="hljs-type">const</span> std::vector&#x3C;meters> p2{meters{<span class="hljs-number">7</span>}, meters{<span class="hljs-number">8</span>}, meters{<span class="hljs-number">9</span>}, meters{<span class="hljs-number">10</span>}};</span><span class="ln" data-line="193">    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">auto</span> summed = <span class="hljs-built_in">pairwise_sum</span>(path, p2))</span><span class="ln" data-line="194">    {</span><span class="ln" data-line="195">        std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"pairwise_sum size={}, last={}"</span>, summed-><span class="hljs-built_in">size</span>(), summed-><span class="hljs-built_in">back</span>());</span><span class="ln" data-line="196">    }</span><span class="ln" data-line="197">    <span class="hljs-keyword">else</span></span><span class="ln" data-line="198">    {</span><span class="ln" data-line="199">        std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"pairwise_sum error: {}"</span>, summed.<span class="hljs-built_in">error</span>());</span><span class="ln" data-line="200">    }</span><span class="ln" data-line="201"></span><span class="ln" data-line="202">    <span class="hljs-comment">// bool_testable</span></span><span class="ln" data-line="203">    counter cnt{<span class="hljs-number">2</span>};</span><span class="ln" data-line="204">    <span class="hljs-keyword">if</span> (cnt)</span><span class="ln" data-line="205">    {</span><span class="ln" data-line="206">        std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"counter {} is truthy"</span>, cnt);</span><span class="ln" data-line="207">    }</span><span class="ln" data-line="208">    --cnt;</span><span class="ln" data-line="209">    --cnt;</span><span class="ln" data-line="210">    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">static_cast</span>&#x3C;<span class="hljs-type">bool</span>>(cnt))</span><span class="ln" data-line="211">    {</span><span class="ln" data-line="212">        std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"counter {} is falsy"</span>, cnt);</span><span class="ln" data-line="213">    }</span><span class="ln" data-line="214"></span><span class="ln" data-line="215">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="216">}</span><span class="ln" data-line="217"></span><span class="ln" data-line="218"><span class="hljs-comment">// Application output</span></span><span class="ln" data-line="219"><span class="hljs-comment">/*</span></span><span class="ln" data-line="220"><span class="hljs-comment">parsed seconds = 77</span></span><span class="ln" data-line="221"><span class="hljs-comment">names[a] = start</span></span><span class="ln" data-line="222"><span class="hljs-comment">flags final = 4</span></span><span class="ln" data-line="223"><span class="hljs-comment">mask=13, popcount=3, bit_width=4</span></span><span class="ln" data-line="224"><span class="hljs-comment">rotl(13, 3)=104, rotr(13, 2)=67</span></span><span class="ln" data-line="225"><span class="hljs-comment">countl_zero=4, countl_one=0, countr_zero=0, countr_one=1</span></span><span class="ln" data-line="226"><span class="hljs-comment">sat_add(4294967290, 100) = 4294967295</span></span><span class="ln" data-line="227"><span class="hljs-comment">sat_sub(-2147483638, -1000) = -2147482638</span></span><span class="ln" data-line="228"><span class="hljs-comment">average(path) = 4.5</span></span><span class="ln" data-line="229"><span class="hljs-comment">pairwise_sum size=4, last=16</span></span><span class="ln" data-line="230"><span class="hljs-comment">counter 2 is truthy</span></span><span class="ln" data-line="231"><span class="hljs-comment">counter 0 is falsy</span></span><span class="ln" data-line="232"><span class="hljs-comment">*/</span></span><span class="ln" data-line="233"></span></code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>Strong types make APIs more explicit, catch errors earlier, and control which operations are available for each value. Concrete aliases such as <code>using Width = st::strong_type&#x3C;double, width_tag, st::arithmetic>;</code> make both call sites and tests clearer, because the compiler can distinguish values that share the same representation but have different meanings.</p>]]></content:encoded>
    </item>
    <item>
      <title>C++20 Ranges and Views</title>
      <link>https://nexenne.com/blog/cpp20_ranges_and_views/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/cpp20_ranges_and_views/</guid>
      <pubDate>Wed, 11 Sep 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[An example of how C++20 ranges and views make collection algorithms easier to read.]]></description>
      <category>C++</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>This post shows how <code>std::ranges</code> and <code>std::views</code>, introduced in C++20, make algorithm composition on collections clearer.</p>
<h2 id="computational-model">Computational model</h2>
<h3 id="problem">Problem</h3>
<p>We want to write an algorithm that takes a collection of integers as input and returns only the ones divisible by 3, in reverse order.</p>
<p>The following table shows a few examples of input and output.</p>
<table>
<thead>
<tr>
<th>Input</th>
<th>Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>3 0 10 9 12 7 30 14 6</td>
<td>6 30 12 9 0 3</td>
</tr>
<tr>
<td>12 14 303 25</td>
<td>25 303</td>
</tr>
<tr>
<td>15 17 21 0 18</td>
<td>18 0 21 15</td>
</tr>
</tbody>
</table>
<h3 id="pre-c20-solution">Pre-C++20 solution</h3>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;algorithm></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;vector></span></span></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="4"></span><span class="ln" data-line="5"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="6">    <span class="hljs-type">const</span> std::vector&#x3C;<span class="hljs-type">int</span>> numbers{<span class="hljs-number">3</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">9</span>, <span class="hljs-number">12</span>, <span class="hljs-number">7</span>, <span class="hljs-number">30</span>, <span class="hljs-number">14</span>, <span class="hljs-number">6</span>};</span><span class="ln" data-line="7"></span><span class="ln" data-line="8">    <span class="hljs-keyword">auto</span> isDivisibleByThree = [](<span class="hljs-type">int</span> <span class="hljs-type">const</span> i) { <span class="hljs-keyword">return</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>; };</span><span class="ln" data-line="9"></span><span class="ln" data-line="10">    std::vector&#x3C;<span class="hljs-type">int</span>> tmp{};</span><span class="ln" data-line="11"></span><span class="ln" data-line="12">    std::<span class="hljs-built_in">copy_if</span>(numbers.<span class="hljs-built_in">begin</span>(), numbers.<span class="hljs-built_in">end</span>(), std::<span class="hljs-built_in">back_inserter</span>(tmp), isDivisibleByThree);</span><span class="ln" data-line="13">    std::<span class="hljs-built_in">reverse</span>(tmp.<span class="hljs-built_in">begin</span>(), tmp.<span class="hljs-built_in">end</span>());</span><span class="ln" data-line="14"></span><span class="ln" data-line="15">    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> <span class="hljs-type">const</span>&#x26; i : tmp)</span><span class="ln" data-line="16">        std::cout &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" "</span>;</span><span class="ln" data-line="17"></span><span class="ln" data-line="18">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="19">}</span></code></pre>
<p>This small snippet of code performs these steps:</p>
<ul>
<li>Creates a temporary helper <code>std::vector</code>.</li>
<li>Copies into <code>tmp</code> all the elements of <code>numbers</code> that satisfy the <code>isDivisibleByThree</code> predicate.</li>
<li>Reverses the order of the elements in <code>tmp</code>.</li>
</ul>
<p>The solution works, but it requires a temporary container and several separate steps. With C++20 we can describe the same transformation more directly.</p>
<h3 id="solution-with-stdranges">Solution with std::ranges</h3>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;algorithm></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;vector></span></span></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="4"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;ranges></span></span></span><span class="ln" data-line="5"></span><span class="ln" data-line="6"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="7">    <span class="hljs-type">const</span> std::vector&#x3C;<span class="hljs-type">int</span>> numbers{<span class="hljs-number">3</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">9</span>, <span class="hljs-number">12</span>, <span class="hljs-number">7</span>, <span class="hljs-number">30</span>, <span class="hljs-number">14</span>, <span class="hljs-number">6</span>};</span><span class="ln" data-line="8"></span><span class="ln" data-line="9">    <span class="hljs-keyword">auto</span> isDivisibleByThree = [](<span class="hljs-type">int</span> <span class="hljs-type">const</span> i) { <span class="hljs-keyword">return</span> i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>; };</span><span class="ln" data-line="10"></span><span class="ln" data-line="11">    <span class="hljs-keyword">auto</span> result{std::views::<span class="hljs-built_in">reverse</span>(std::views::<span class="hljs-built_in">filter</span>(numbers, isDivisibleByThree))};</span><span class="ln" data-line="12"></span><span class="ln" data-line="13">    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> <span class="hljs-type">const</span>&#x26; i : result)</span><span class="ln" data-line="14">        std::cout &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" "</span>;</span><span class="ln" data-line="15"></span><span class="ln" data-line="16">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="17">}</span></code></pre>
<p>The difference is clear; before going into specifics, however, it's worth clarifying the concepts of range and view.</p>
<h2 id="ranges">Ranges</h2>
<p>A range represents a sequence of elements, or more generally something that can be iterated.</p>
<p>By definition, a range is a pair of iterators <code>begin</code> and <code>end</code>: the first points to the start of a collection or sequence, the second to its end.</p>
<p>Standard-library containers satisfy this definition and can therefore be used as ranges.</p>
<h3 id="classification">Classification</h3>
<p>Ranges can be classified in different ways. One of the most important distinctions is based on iterator capabilities.</p>
<p>Having covered concepts in the <a href="/blog/cpp20_concepts/" data-sveltekit-reload>previous post</a>, we can summarize ranges in the following table.</p>
<table>
<thead>
<tr>
<th>Concept</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>std::ranges::input_range</td>
<td>Can be iterated from start to end <code>at least once</code></td>
</tr>
<tr>
<td>std::ranges::forward_range</td>
<td>Can be iterated from start to end <code>multiple times</code></td>
</tr>
<tr>
<td>std::ranges::bidirectional_range</td>
<td>The iterator can perform the <code>--</code> operation (move to the previous element)</td>
</tr>
<tr>
<td>std::ranges::random_access_range</td>
<td>The <code>[]</code> operator exists, allowing access to elements in constant time</td>
</tr>
<tr>
<td>std::ranges::contiguous_range</td>
<td>The elements are required to be stored contiguously in memory</td>
</tr>
</tbody>
</table>
<p>This classification corresponds to the related iterator concepts, such as <code>std::forward_iterator</code>.</p>
<h2 id="views">Views</h2>
<p>Three ideas about views are especially important:</p>
<ul>
<li>A view is a range.</li>
<li>A view does not own the data it accesses.</li>
<li>A view applies changes only when an element is requested (lazy evaluation).</li>
</ul>
<h3 id="una-view-è-un-range">A view is a range</h3>
<p>By definition, a view \(w\) is a range defined on another range \(r\).
The view can apply transformations to the observed range through algorithms and other operations, taking advantage of lazy evaluation.</p>
<h3 id="a-view-does-not-own-the-data-it-accesses">A view does not own the data it accesses</h3>
<p>When you access an element through a view, you still work with the data managed by the underlying range.</p>
<p>This has two implications:</p>
<ul>
<li>Views are fast to create, because they don't need to copy the underlying data.</li>
<li>Structural transformations performed by the view do not modify the original container.</li>
</ul>
<!--listend-->
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;vector></span></span></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;ranges></span></span></span><span class="ln" data-line="4"></span><span class="ln" data-line="5"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="6">    std::vector numbers{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>};</span><span class="ln" data-line="7">    <span class="hljs-keyword">auto</span> v{std::views::<span class="hljs-built_in">reverse</span>(numbers)};</span><span class="ln" data-line="8"></span><span class="ln" data-line="9">    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> <span class="hljs-type">const</span>&#x26; i : numbers)</span><span class="ln" data-line="10">        std::cout &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" "</span>;</span><span class="ln" data-line="11">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="12">}</span><span class="ln" data-line="13"></span><span class="ln" data-line="14"><span class="hljs-comment">// Output: 1 2 3 4 5</span></span></code></pre>
<p>As you can see, the view did not modify the <code>numbers</code> range.
However, the opposite is true: if you modify the original container, the change is reflected on every view that uses that range.</p>
<p>So we would get</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;vector></span></span></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;ranges></span></span></span><span class="ln" data-line="4"></span><span class="ln" data-line="5"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="6">    std::vector numbers{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>};</span><span class="ln" data-line="7">    <span class="hljs-keyword">auto</span> v{std::views::<span class="hljs-built_in">reverse</span>(numbers)};</span><span class="ln" data-line="8"></span><span class="ln" data-line="9">    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> <span class="hljs-type">const</span>&#x26; i : v)</span><span class="ln" data-line="10">        std::cout &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" "</span>;</span><span class="ln" data-line="11"></span><span class="ln" data-line="12">    std::cout &#x3C;&#x3C; std::endl;</span><span class="ln" data-line="13"></span><span class="ln" data-line="14">    numbers[<span class="hljs-number">2</span>] = <span class="hljs-number">100</span>;</span><span class="ln" data-line="15">    numbers[<span class="hljs-number">4</span>] = <span class="hljs-number">77</span>;</span><span class="ln" data-line="16"></span><span class="ln" data-line="17">    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> <span class="hljs-type">const</span>&#x26; i : v)</span><span class="ln" data-line="18">        std::cout &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" "</span>;</span><span class="ln" data-line="19"></span><span class="ln" data-line="20">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="21">}</span><span class="ln" data-line="22"></span><span class="ln" data-line="23"><span class="hljs-comment">// Output: 5 4 3 2 1</span></span><span class="ln" data-line="24"><span class="hljs-comment">//         77 4 100 2 1</span></span></code></pre>
<h3 id="lazy-evaluation">Lazy evaluation</h3>
<p>A view applies transformations when elements are requested, not necessarily when the view is created. This deferred evaluation avoids unnecessary work and copies.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;vector></span></span></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;ranges></span></span></span><span class="ln" data-line="4"></span><span class="ln" data-line="5"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="6">    std::vector numbers{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>};</span><span class="ln" data-line="7">    <span class="hljs-keyword">auto</span> v{std::views::<span class="hljs-built_in">reverse</span>(numbers)};</span><span class="ln" data-line="8">    std::cout &#x3C;&#x3C; *v.<span class="hljs-built_in">begin</span>() &#x3C;&#x3C; std::endl; <span class="hljs-comment">// the view is evaluated here</span></span><span class="ln" data-line="9">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="10">}</span><span class="ln" data-line="11"></span><span class="ln" data-line="12"><span class="hljs-comment">// Output: 5</span></span></code></pre>
<h3 id="composition-and-pipelines">Composition and pipelines</h3>
<p>You may wonder why I wrote</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">auto</span> v{std::views::<span class="hljs-built_in">reverse</span>(numbers)};</span></code></pre>
<p>instead of</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1">std::views::reverse v{numbers};</span></code></pre>
<p>The reason is that <code>std::views::reverse</code> is not itself a view, but an adapter: it takes the underlying range, in this case a <code>std::vector</code>, and returns a view over it.
The exact type of the view is hidden behind the <code>auto</code> keyword; this way we don't have to worry about writing the view's template arguments.
Another advantage of this form is the ability to chain multiple adapters via pipes.</p>
<p>For example, instead of using</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">auto</span> v{std::views::<span class="hljs-built_in">reverse</span>(std::views::<span class="hljs-built_in">filter</span>(numbers, isDivisibleByThree))};</span></code></pre>
<p>We can write</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">auto</span> v{numbers | std::views::<span class="hljs-built_in">filter</span>(isDivisibleByThree) | std::views::reverse};</span></code></pre>
<h2 id="examples">Examples</h2>
<p>We want to create a view of the first 5 elements of a std::vector and print the result.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;vector></span></span></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">&#x3C;ranges></span></span></span><span class="ln" data-line="4"></span><span class="ln" data-line="5"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="6">    std::vector numbers{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">10</span>};</span><span class="ln" data-line="7">    <span class="hljs-keyword">auto</span> v{numbers | std::views::<span class="hljs-built_in">take</span>(<span class="hljs-number">5</span>)};</span><span class="ln" data-line="8"></span><span class="ln" data-line="9">    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> <span class="hljs-type">const</span>&#x26; i : v)</span><span class="ln" data-line="10">        std::cout &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" "</span>;</span><span class="ln" data-line="11">}</span><span class="ln" data-line="12"></span><span class="ln" data-line="13"><span class="hljs-comment">// Output: 1 2 3 4 5</span></span></code></pre>
<p>We want to use a range-based algorithm to print an <code>std::vector</code> in reverse order, excluding negative values.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;algorithm></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;ranges></span></span></span><span class="ln" data-line="4"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;vector></span></span></span><span class="ln" data-line="5"></span><span class="ln" data-line="6"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="7">    std::vector numbers{<span class="hljs-number">-1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">-100</span>, <span class="hljs-number">-4</span>, <span class="hljs-number">0</span>, <span class="hljs-number">3</span>, <span class="hljs-number">-7</span>, <span class="hljs-number">1</span>};</span><span class="ln" data-line="8">    <span class="hljs-keyword">auto</span> predicate = [](<span class="hljs-type">int</span> <span class="hljs-type">const</span> i) -> <span class="hljs-type">bool</span> {</span><span class="ln" data-line="9">        <span class="hljs-keyword">return</span> i >= <span class="hljs-number">0</span>;</span><span class="ln" data-line="10">    };</span><span class="ln" data-line="11">    <span class="hljs-keyword">auto</span> printer = [](<span class="hljs-type">int</span> <span class="hljs-type">const</span> i) {</span><span class="ln" data-line="12">        std::cout &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" "</span>;</span><span class="ln" data-line="13">    };</span><span class="ln" data-line="14"></span><span class="ln" data-line="15">    std::ranges::for_each(numbers | std::views::reverse | std::views::<span class="hljs-built_in">filter</span>(predicate), printer);</span><span class="ln" data-line="16">}</span><span class="ln" data-line="17"></span><span class="ln" data-line="18"><span class="hljs-comment">// Output: 1 3 0 3</span></span></code></pre>
<h2 id="advanced-concepts">Advanced concepts</h2>
<h3 id="range-factories">Range factories</h3>
<p>The standard library also provides adapters that can generate a view without starting from an existing range.</p>
<p>One example is <code>std::views::iota</code>, which creates an incremental view of integers.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;ranges></span></span></span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="5">    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-type">const</span> i : std::views::<span class="hljs-built_in">iota</span>(<span class="hljs-number">1</span>, <span class="hljs-number">7</span>)) {</span><span class="ln" data-line="6">        std::cout &#x3C;&#x3C; i &#x3C;&#x3C; <span class="hljs-string">" "</span>;</span><span class="ln" data-line="7">    }</span><span class="ln" data-line="8">}</span><span class="ln" data-line="9"></span><span class="ln" data-line="10"><span class="hljs-comment">// Output: 1 2 3 4 5 6</span></span></code></pre>
<h3 id="zip-views">Zip views</h3>
<p><code>std::views::zip</code>, introduced in C++23, lets you combine multiple ranges into a single view. Each produced element is a tuple containing the corresponding values from the source ranges.</p>
<p>Here is a small example.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;ranges></span></span></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;vector></span></span></span><span class="ln" data-line="4"></span><span class="ln" data-line="5"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="6"></span><span class="ln" data-line="7">    std::vector numbers{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>};</span><span class="ln" data-line="8">    std::vector english{<span class="hljs-string">"cat"</span>, <span class="hljs-string">"dog"</span>, <span class="hljs-string">"table"</span>, <span class="hljs-string">"sun"</span>};</span><span class="ln" data-line="9">    std::vector italian{<span class="hljs-string">"gatto"</span>, <span class="hljs-string">"cane"</span>, <span class="hljs-string">"tavolo"</span>, <span class="hljs-string">"sole"</span>};</span><span class="ln" data-line="10"></span><span class="ln" data-line="11">    <span class="hljs-keyword">for</span> (<span class="hljs-type">const</span> <span class="hljs-keyword">auto</span>&#x26; i : std::views::<span class="hljs-built_in">zip</span>(numbers, english, italian)) {</span><span class="ln" data-line="12">        std::cout &#x3C;&#x3C; std::<span class="hljs-built_in">get</span>&#x3C;<span class="hljs-number">0</span>>(i) &#x3C;&#x3C; <span class="hljs-string">". "</span></span><span class="ln" data-line="13">                  &#x3C;&#x3C; std::<span class="hljs-built_in">get</span>&#x3C;<span class="hljs-number">1</span>>(i) &#x3C;&#x3C; <span class="hljs-string">": "</span></span><span class="ln" data-line="14">                  &#x3C;&#x3C; std::<span class="hljs-built_in">get</span>&#x3C;<span class="hljs-number">2</span>>(i) &#x3C;&#x3C; <span class="hljs-string">'\n'</span>;</span><span class="ln" data-line="15">    }</span><span class="ln" data-line="16"></span><span class="ln" data-line="17">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="18">}</span></code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>Ranges and views let you describe a transformation pipeline without introducing unnecessary temporary containers. For a complete overview of the available adapters and algorithms, see the <a href="https://en.cppreference.com/w/cpp/ranges">ranges library documentation</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>C++20 Concepts</title>
      <link>https://nexenne.com/blog/cpp20_concepts/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/cpp20_concepts/</guid>
      <pubDate>Sun, 25 Aug 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[An introduction to C++20 concepts: what they are, when they help and how to use them without making the code heavier.]]></description>
      <category>C++</category>
      <category>Tutorial</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>This post introduces <code>concepts</code>, one of the most useful C++20 features for generic code. We will cover the essential terminology and see how concepts make template requirements explicit.</p>
<p>For a complete reference, cppreference has detailed <a href="https://en.cppreference.com/w/cpp/language/constraints">documentation on constraints and concepts</a>.</p>
<h2 id="motivation">Motivation</h2>
<p>The first question is simple: what are concepts useful for?</p>
<p>When we write code we almost always want the algorithms and data structures we implement to be generic, that is, usable with different data types.
We want a single generic solution, without having to reimplement it for specific data types.
This has several advantages:</p>
<ul>
<li>Better maintainability.</li>
<li>Reusing the same code with different types.</li>
<li>Allowing API users to provide custom types.</li>
</ul>
<p>Common examples are standard-library algorithms and containers such as <code>std::swap</code> and <code>std::vector</code>.</p>
<p>In C++, this abstraction is expressed with templates. A template can be instantiated with different types, but sometimes the type cannot be arbitrary: the implementation may require specific operations or properties.</p>
<p>Suppose we have an algorithm that, given two values of the same type, wants to perform a binary addition and return the result.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;format></span></span></span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> T></span><span class="ln" data-line="5"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">add</span><span class="hljs-params">(T <span class="hljs-type">const</span>&#x26; a, T <span class="hljs-type">const</span>&#x26; b)</span> -> T </span>{</span><span class="ln" data-line="6">    <span class="hljs-keyword">return</span> a + b;</span><span class="ln" data-line="7">}</span><span class="ln" data-line="8"></span><span class="ln" data-line="9"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="10">    std::cout &#x3C;&#x3C; std::format(<span class="hljs-string">"{}\n"</span>, <span class="hljs-built_in">add</span>(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>));</span><span class="ln" data-line="11">    <span class="hljs-comment">// std::cout &#x3C;&#x3C; std::format("{}\n", add("foo", "bar")); -> compilation error</span></span><span class="ln" data-line="12">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="13">}</span></code></pre>
<p>The generic template parameter <code>T</code> is unconstrained: from the compiler's point of view it can be instantiated with any type.
However, if you ask for this function to be instantiated with certain data types, the compiler will produce an error.</p>
<p>This happens because the function template implicitly requires the types passed as parameters to provide the binary addition operator.
If a type does not provide that operator, the compiler cannot generate the function.</p>
<p>But where does the compiler fail?</p>
<p>Not at the template declaration, but during instantiation, when the implementation tries to use an operation that is not available.</p>
<p>This produces error messages that are hard to decipher and causes plenty of frustration.
In medium-to-large codebases, with nested data structures, the situation worsens exponentially.</p>
<p>To work around this issue we need to introduce <code>constraints</code>, so that we can explicitly define the requirements of the template parameters.</p>
<h2 id="terminology">Terminology</h2>
<h3 id="template">Template</h3>
<p>A template is a construct that generates an ordinary type or function at compile time based on the arguments the user supplies for the template parameters.
A template's arguments can be constrained.</p>
<h3 id="requirements">Requirements</h3>
<p>Requirements are expressed with the <code>requires</code> keyword, which describes the conditions that a type or expression must satisfy.
For details, see the <a href="https://en.cppreference.com/w/cpp/language/requires">cppreference documentation on requires</a>.</p>
<h3 id="constraint">Constraint</h3>
<p>A <code>constraint</code> is a set of <code>requirements</code> on a template's arguments.</p>
<p>These are used to:</p>
<ul>
<li>Correctly select function overloads.</li>
<li>Decide the most appropriate specialization for a template.</li>
</ul>
<h3 id="concepts">Concepts</h3>
<p>A concept is a predicate that wraps a set of constraints.
Each concept is evaluated at compile time and becomes part of the interface of a template when it is used as a constraint.</p>
<p>In addition:</p>
<ul>
<li>A data type that satisfies all the requirements (and therefore the constraints) of a concept is said to model that concept.</li>
<li>A concept made up of another concept plus additional constraints is said to refine that concept (or those concepts).</li>
</ul>
<h2 id="syntax">Syntax</h2>
<p>Depending on the complexity of a constraint declaration, you can use three different syntaxes to enforce constraints.
All the definitions below are equivalent, and you can combine them. Note that std::integral is a predefined concept.</p>
<h3 id="full-explicit-declaration">Full, explicit declaration</h3>
<p>Very useful when you have multiple constraints to enforce.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> T, <span class="hljs-keyword">typename</span> Q></span><span class="ln" data-line="2">    <span class="hljs-function"><span class="hljs-keyword">requires</span> std::integral&#x3C;T> <span class="hljs-keyword">and</span> std::integral&#x3C;Q></span></span><span class="ln" data-line="3"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">add</span><span class="hljs-params">(T <span class="hljs-type">const</span> t, Q <span class="hljs-type">const</span> q)</span> </span>{</span><span class="ln" data-line="4">    <span class="hljs-keyword">return</span> t + q;</span><span class="ln" data-line="5">}</span></code></pre>
<h3 id="intermediate-declaration">Intermediate declaration</h3>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">template</span> &#x3C;std::integral T, std::integral Q></span><span class="ln" data-line="2"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">add</span><span class="hljs-params">(T <span class="hljs-type">const</span> t, Q <span class="hljs-type">const</span> q)</span> </span>{</span><span class="ln" data-line="3">    <span class="hljs-keyword">return</span> t + q;</span><span class="ln" data-line="4">}</span></code></pre>
<h3 id="compact-declaration">Compact declaration</h3>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">add</span><span class="hljs-params">(std::integral <span class="hljs-keyword">auto</span> <span class="hljs-type">const</span> t, std::integral <span class="hljs-keyword">auto</span> <span class="hljs-type">const</span> q)</span> </span>{</span><span class="ln" data-line="2">    <span class="hljs-keyword">return</span> t + q;</span><span class="ln" data-line="3">}</span></code></pre>
<h2 id="solution">Solution</h2>
<p>To solve the problem, we declare the <code>Addable</code> concept and apply it to the parameters of <code>add</code>. In this case we use the compact form.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;iostream></span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;concepts></span></span></span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> T> <span class="hljs-keyword">concept</span> Addable = <span class="hljs-built_in">requires</span>(T a, T b) {</span><span class="ln" data-line="5"> a + b; <span class="hljs-comment">// requirement 1</span></span><span class="ln" data-line="6">};</span><span class="ln" data-line="7"></span><span class="ln" data-line="8"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">add</span><span class="hljs-params">(Addable <span class="hljs-keyword">auto</span> <span class="hljs-type">const</span> t, Addable <span class="hljs-keyword">auto</span> <span class="hljs-type">const</span> q)</span> </span>{</span><span class="ln" data-line="9">    <span class="hljs-keyword">return</span> t + q;</span><span class="ln" data-line="10">}</span><span class="ln" data-line="11"></span><span class="ln" data-line="12"><span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> -> <span class="hljs-type">int</span> </span>{</span><span class="ln" data-line="13">    std::cout &#x3C;&#x3C; <span class="hljs-built_in">add</span>(<span class="hljs-number">5</span>, <span class="hljs-number">6</span>) &#x3C;&#x3C; std::endl;</span><span class="ln" data-line="14">    <span class="hljs-comment">//std::cout &#x3C;&#x3C; add("foo", "bar") &#x3C;&#x3C; std::endl;  -> compilation error</span></span><span class="ln" data-line="15">    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="16">}</span></code></pre>
<p>The template rejects any type that does not satisfy the requirement at compile time. Compared with the unconstrained version, the compiler can produce an error closer to the function interface and therefore easier to interpret.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Concepts and constraints make template requirements explicit and help produce more understandable compilation errors. For more detail, cppreference offers a complete guide to <a href="https://en.cppreference.com/w/cpp/language/constraints">constraints and concepts</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>openSUSE Tumbleweed review</title>
      <link>https://nexenne.com/blog/opensuse_tumbleweed_review/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/opensuse_tumbleweed_review/</guid>
      <pubDate>Thu, 01 Aug 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[My experience with openSUSE Tumbleweed: rolling releases, KDE, stability and a few everyday trade-offs.]]></description>
      <category>OS</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>After leaving Arch Linux behind, I moved to openSUSE Tumbleweed, which eventually became my main operating system. I used it for software development, gaming, office work, and everyday use.</p>
<p>What I appreciate most is the balance between frequent updates and reliability. Tumbleweed is a rolling-release distribution, but its testing process removes many of the problems usually associated with that model.</p>
<p>After trying several distributions over the years, Tumbleweed stands out for its KDE Plasma integration, administration tools, and update quality. This article collects what worked well for me and the trade-offs I ran into.</p>
<h2 id="what-works-well">What works well</h2>
<h3 id="stable-and-bleeding-edge">Stable and bleeding-edge</h3>
<p>openSUSE Tumbleweed is a rolling-release distribution that gives users the latest features and developments from the Linux world: kernel, drivers, desktop environments and up-to-date applications.</p>
<p>That is useful for advanced users, software developers, and gamers who need recent packages.</p>
<p>Because Tumbleweed is updated continuously, you do not need large version upgrades every six months as with some other GNU/Linux distributions.</p>
<p>This distribution does not rely on rigid periodic release cycles; it is based on Factory, openSUSE's development base, which is updated frequently.</p>
<p>Each update, which follows a strict industrial standard and a rigorous quality-assurance process, is published only after passing thorough tests and quality checks through the openQA platform.</p>
<p>In practice, every new package version is tested individually and together with other groups of versions, ensuring overall system consistency.</p>
<p>In addition, openSUSE uses a modern filesystem like Btrfs, which lets users take snapshots of the system state and roll back to a previous state when something goes wrong.</p>
<p>All of this makes Tumbleweed suitable for everyday use despite being a rolling release. Once installed and configured, it can be used continuously as long as it is kept updated.</p>
<h3 id="yast-and-snapper">YaST and Snapper</h3>
<p>Tumbleweed includes YaST, a very complete control panel.
YaST, short for Yet another Setup Tool, can manage users, printers, software sources, the boot loader, partitions, services, networking, virtualization, AppArmor, filesystem snapshots, and more.</p>
<p>Tumbleweed also ships Snapper, created by Arvin Schnell, to manage snapshots of Btrfs filesystem subvolumes and LVM thin-provisioned volumes.
Beyond creating and deleting snapshots, including scheduled and automatic ones, Snapper can compare snapshots and restore the differences between them.
In practice, it lets users view earlier versions of files and recover changes in a controlled way.</p>
<p>Together, YaST and Snapper make system administration easier and simplify recovery after a problematic update.</p>
<h3 id="intuitive-setup">Intuitive setup</h3>
<p>Let's start with the installation. Tumbleweed's installer is, in my opinion, the most complete and straightforward one I have ever encountered.
It gives you full control over what will be installed and provides a default configuration that is more than sufficient for most users.
I personally prefer and always recommend enabling full-disk encryption and Logical Volume Management to manage physical resources more effectively.</p>
<p>Once the installation is finished, if you need third-party codecs, you just install opi, a front-end for openSUSE's Open Build Service.
To pull in all codecs at once, just use <code>opi codecs</code>, which enables new repositories and installs them.</p>
<p>If you need drivers for your NVIDIA graphics card, you can install them directly from YaST or via the simple <code>zypper install-new-recommends --repo NVIDIA</code> command.</p>
<h2 id="the-downsides">The downsides</h2>
<p>One of Tumbleweed's main drawbacks is that it may not be compatible with some third-party kernel modules or proprietary drivers, especially for graphics cards. That's because the Linux kernel is updated very frequently in Tumbleweed and some external modules or drivers may not keep up with the changes, or may not be available at all.
The workaround is to compile such modules or drivers from source, or to avoid using them entirely.</p>
<p>Also, installing third-party software not in the repositories, even through YaST Software, triggers an integrity-check error. The error can be safely ignored and the software will still install correctly.</p>
<p>A sore point: the ongoing conflict between PackageKit and YaST Software. Just remove the PackageKit package and the problem disappears.</p>
<p>openSUSE is still a niche distribution, so third-party documentation is less abundant. The official documentation is excellent, although it can feel more concise than the documentation available for distributions like Fedora or Ubuntu.</p>
<p>I'll end with an annoyance rather than a serious problem. If you enable third-party repositories, especially Packman, you may run into package-version conflicts that block updates. Usually, waiting a few days before updating or using Flatpak versions of the affected applications is enough.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In my experience, openSUSE Tumbleweed offers recent packages without sacrificing system reliability. openQA, Btrfs, Snapper, and YaST form a solid base for a rolling-release distribution.</p>
<p>The main compromises are external kernel modules and possible conflicts between repositories. To reduce those issues, I prefer limiting third-party repositories and using Flatpak for applications that need proprietary codecs. With those precautions, Tumbleweed has been a strong distribution for both work and everyday use.</p>]]></content:encoded>
    </item>
    <item>
      <title>Auto-updates straight from the source</title>
      <link>https://nexenne.com/blog/autoupdate_your_android_foss_apps_directly_from_source/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/autoupdate_your_android_foss_apps_directly_from_source/</guid>
      <pubDate>Sat, 13 Jul 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[How I use Obtainium to update open-source Android apps directly from their official sources.]]></description>
      <category>OSS</category>
      <category>Android</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>F-Droid is often recommended by security and privacy enthusiasts, and I imagine that many people use it to download, install and update their favorite apps on their phones.
I used F-Droid for a long time too, but after reading <a href="https://privsec.dev/posts/android/f-droid-security-issues/">PrivSec's article</a> on its security issues, I decided to look for an alternative.</p>
<p>At first I used RSS feeds to follow the updates of the applications I was interested in.
I would download the app directly from the developer's GitHub or GitLab page, subscribe to the RSS feed and wait for the update notifications.
When a new release came out I would go back to the dedicated page, download the APK and manually install the update.
Soon, however, I realized it was a clunky, slow and tiresome process.</p>
<p>I needed something that could do the same job automatically after the initial setup. The solution I chose was <code>Obtainium</code>.</p>
<h2 id="cos-è-obtainium">What is Obtainium?</h2>
<p>Obtainium automates the download and installation of Android app updates directly from source websites, meaning sites where the APK files are available for direct download.</p>
<h2 id="how-to-get-obtainium">How to get Obtainium</h2>
<p>To install Obtainium, start from its <a href="https://github.com/ImranR98/Obtainium">official GitHub page</a>.</p>
<p>Under "Installation", select "Get it on GitHub", expand the assets, and pick the APK variant for your device. For a Google Pixel, for example, the variant is usually <code>app-arm64-v8a</code>.
The download will start, then proceed with the install as usual.
Once the install is finished, you can open the app. I recommend allowing notifications: they are handled locally and do not require Google Play services.</p>
<h2 id="the-interface">The interface</h2>
<p>When I first opened Obtainium I was pleasantly surprised by the design.</p>
<p>The project changes often, so names and screen layout may differ over time, but the overall flow should remain similar.</p>
<p>The app has a bottom navigation with these entries:</p>
<ul>
<li>Apps, the full list of apps added to Obtainium.</li>
<li>Add App, to register a new app to track.</li>
<li>Import/Export, to import or export the app list and Obtainium configuration.</li>
<li>Settings, to configure the application.</li>
</ul>
<h2 id="adding-an-app">Adding an app</h2>
<p>To add a new application, tap "Add App".
At the bottom there is a button to see the supported sources. Some, like GitHub and Codeberg, are flagged as searchable: in those cases you can search directly from Obtainium.
There are two text fields: in the first you can paste the source URL of the app you want to add; the second lets you search for it by a text string.</p>
<p>In this case, let's search for NewPipe since the project is on GitHub. The search may return many results, especially for a popular project; in my case, the first one was the official NewPipe repository.
For safety, I always suggest verifying that the project is the right one; even better, look up the page in your browser and paste the URL manually.</p>
<p>Once the source is validated, you can select it by tapping "Select".</p>
<p>A separate section appears with additional options. The most important ones are:</p>
<ul>
<li>Include prereleases: enable this only if you want preview versions that may be unstable.</li>
<li>Fallback to older releases: useful when GitHub releases are not organized cleanly. For example, if iOS and Android builds are published as separate releases and the iOS one is newer, Obtainium may otherwise find no Android APK to install.</li>
<li>Filter Release Titles by Regular Expression: to filter release titles by a regular expression. It's an edge case, but it can happen that the URL points to multiple downloadable applications.</li>
<li>Track Only: track only, with no automatic updates. I leave this option off, since I want Obtainium to download the APKs for me so they can be installed automatically.</li>
</ul>
<p>Now you can tap "Add". The APK is downloaded to your device; once the download is complete, Obtainium shows the details of the newly added app.
Tapping "Install" asks Android to allow app installation from Obtainium as an unknown source. Enable it and install the app.</p>
<p>After the installation is finished, going back to the app list you can see that NewPipe is now monitored, with the latest version correctly installed.</p>
<p>It sounds like a long process, but once you know the basic steps it takes only a few seconds.</p>
<h2 id="what-if-obtainium-were-compromised-by-malware">What if Obtainium were compromised by malware?</h2>
<p>As always, you should be skeptical about everything you download from the web, open source or not.
An extra precaution is to install the first version manually from the official source and add it to Obtainium only afterwards. Android accepts updates only when the signature matches the app already installed, so an APK signed with a different key would be rejected.</p>
<h2 id="limitations">Limitations</h2>
<p>There are some limitations to keep in mind.
The first is that app installs happen asynchronously and you cannot directly determine the success or failure of an installation.
This means that install state and versions are not synced with the operating system until the next launch or until the issue is fixed manually.
In short, you have to restart the application.</p>
<p>The second is that for some sources, data is gathered through web scraping, which can easily break due to changes in the website design.
Anyone who has used NewPipe will have noticed that, on every change in YouTube's interface, NewPipe's extractor is at risk of breaking and may no longer be able to extract fundamental data.
Something similar can happen with Obtainium: it's a consequence of the fragile nature of web scraping.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Obtainium lets you follow updates directly from official sources and reduces the manual work needed to download new releases. It still requires care when choosing repositories and does not remove the risks of APK distribution, but it is a good compromise for managing open-source apps outside traditional stores.</p>]]></content:encoded>
    </item>
    <item>
      <title>Migrating to open-source apps on your phone</title>
      <link>https://nexenne.com/blog/migrate_to_open_source_applications_on_mobile/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/migrate_to_open_source_applications_on_mobile/</guid>
      <pubDate>Sat, 29 Jun 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[After moving to GrapheneOS, I started replacing my main apps with more privacy-respecting alternatives.]]></description>
      <category>OSS</category>
      <category>Android</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>After moving to GrapheneOS, I decided to replace many of my apps with more privacy-respecting alternatives.
I also realized how dependent I was on applications from the major platforms.</p>
<p>These were the apps I used most on my old Samsung S10:</p>
<ul>
<li>WhatsApp.</li>
<li>Google suite: Mail, Calendar and Drive.</li>
<li>Google Workspace suite.</li>
<li>YouTube.</li>
<li>Google Maps.</li>
<li>Google Play Store.</li>
</ul>
<p>These were only some of the main applications I used.
In practice, a large part of my digital life went through Google, Microsoft, and Meta.</p>
<p>I started looking for open-source and privacy-oriented alternatives, accepting a few compromises compared with the services I used before.</p>
<h2 id="replacing-whatsapp">Replacing WhatsApp</h2>
<p>This was the hardest step, not because of the app choice but because of the migration.
Today almost everyone uses WhatsApp, and for many people it has become the default channel.
I had to warn my contacts that I was moving away from it and would soon no longer be reachable there.
There were plenty of questions.
In the end I moved to Signal Messenger, and very few people followed me. That was not a big problem: anyone who needed to reach me could still call instead of sending text messages or endless voice notes.</p>
<p>Why Signal and not alternatives like Session, Briar, Element, or SimpleX Chat?</p>
<p>In short:</p>
<p>Contact lists on Signal are encrypted with the Signal PIN and the server has no access to them.
Personal profiles are also encrypted and only shared with the contacts you chat with.
Signal supports private groups, where the server has no record of group membership, group titles, group avatars or group attributes.
Signal has minimal metadata when Sealed Sender is enabled.
The sender address is encrypted alongside the message body and only the recipient address is visible to the server.
Sealed Sender is enabled only for people in your contact list, but it can be enabled for all recipients, at the cost of a higher risk of receiving spam.</p>
<p>In addition, the protocol has been independently reviewed and its specifications are public.</p>
<p>As a bonus, Signal's interface is very close to WhatsApp's.</p>
<h2 id="replacing-the-google-suite-mail-calendar-and-drive">Replacing the Google suite: Mail, Calendar and Drive</h2>
<p>These are some of the providers I evaluated, grouped by category.</p>
<p>Email services:</p>
<ul>
<li>Proton Mail</li>
<li>Tutanota</li>
<li>Mailbox.org</li>
</ul>
<p>Cloud storage services:</p>
<ul>
<li>Proton Drive</li>
<li>Tresorit</li>
<li>Peergos</li>
</ul>
<p>Calendar services:</p>
<ul>
<li>Proton Calendar</li>
<li>Tuta (Tutanota's calendar)</li>
</ul>
<p>The options were two: split the services across different providers or use a single company.</p>
<p>I chose Proton because it brings mail, calendar, and cloud storage into one service, with polished applications and a privacy-focused model.</p>
<p>To recap, I replaced Gmail, Google Calendar, and Google Drive with Proton Mail, Proton Calendar, and Proton Drive.</p>
<h2 id="replacing-the-google-workspace-suite">Replacing the Google Workspace suite</h2>
<p>Unfortunately, for this one I did not find a solid alternative.
The choice I made was to pair an excellent app, Collabora Office, for editing documents, with a good sync service, Syncthing.</p>
<p>That's because Collabora Online (Office) is not a standalone piece of software.
On the contrary, the online office suite plugs into an existing infrastructure and requires a cloud solution as its base (NextCloud, Dropbox, etc.).</p>
<h2 id="replacing-youtube">Replacing YouTube</h2>
<p>I mostly use YouTube for learning, but also for entertainment.
In this case I still use Google's platform, but through a third-party client instead of the official app.</p>
<p>Fortunately, I did not need to do much research: I had already been using NewPipe for several years.</p>
<p>NewPipe is an intuitive, feature-rich and privacy-respecting app that lets you watch videos on YouTube.
The app offers many parts of the YouTube experience without invasive ads and without the permissions requested by the official app.
It is also open source and its code is available on GitHub.</p>
<h2 id="replacing-google-maps">Replacing Google Maps</h2>
<p>Here the only solution is to switch to a client based on OpenStreetMap.</p>
<p>The alternatives are:</p>
<ul>
<li>OsmAnd</li>
<li>Organic Maps</li>
<li>Magic Earth</li>
</ul>
<p>The first two are excellent both for browsing maps and for the features they offer; however, they fall short in real-time navigation. During navigation, they often suggest very inconvenient secondary roads or pick routes that needlessly lengthen the trip.
The third one excels where the others struggle, but its source code is not available.</p>
<p>I mostly use Organic Maps for its clean interface and for hiking. When I need to drive somewhere unfamiliar, I prefer Magic Earth.</p>
<h2 id="replacing-the-google-play-store">Replacing the Google Play Store</h2>
<p>As with YouTube, one solution is to use an alternative client for the Play Store catalog.
The app I use for this is Aurora Store.
Aurora Store is a free alternative to the Google Play Store.
With this app you can download applications, update the existing ones, get details on in-app trackers, hide your location, search for applications and much more.
Aurora's developers have also done good work on the design and interface.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Replacing mainstream smartphone apps requires compromises: sometimes you lose convenience, sometimes you gain control. For me the switch made sense because it reduced my dependency on major platforms without making the phone uncomfortable to use.</p>]]></content:encoded>
    </item>
    <item>
      <title>GrapheneOS: my favorite smartphone operating system</title>
      <link>https://nexenne.com/blog/graphene_os_as_my_daily_driver/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/graphene_os_as_my_daily_driver/</guid>
      <pubDate>Fri, 07 Jun 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[After months of daily use, GrapheneOS became my favorite smartphone operating system.]]></description>
      <category>OS</category>
      <category>Android</category>
      <category>Smartphone</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>In June 2023, my old Samsung Galaxy S10 died after a bad fall down the stairs.</p>
<p>I had already been thinking about replacing it, and I wanted a device that gave me more control, with a system designed around security and privacy.</p>
<p>I compared several operating systems based on the Android Open Source Project, each with different privacy and security goals.</p>
<p>I eventually narrowed the choice down to three alternatives: CalyxOS, LineageOS, and GrapheneOS.</p>
<p>After evaluating them, I chose GrapheneOS and bought an officially supported device.</p>
<p>Let's look at what GrapheneOS is, how it is installed, and which features convinced me.</p>
<h2 id="cos-è-grapheneos">What is GrapheneOS?</h2>
<p>GrapheneOS is an operating system based on the Android Open Source Project, with many additional security and privacy features.
It is open source, officially supports all the most recent Google Pixel devices, and can be installed with great ease to replace the operating system on a Google Pixel.</p>
<p>By default it includes neither Google applications nor Google services: it essentially breaks the control Google has over the mobile device.</p>
<p>The result is a spartan device, in the positive sense: only stock applications from the AOSP project, everything to be configured by you, with no constraints imposed by third parties.</p>
<h2 id="how-do-you-install-grapheneos">How do you install GrapheneOS?</h2>
<p>The project provides a minimal web interface that lets you install the operating system by following clear instructions.</p>
<p>To get started, open the <a href="https://grapheneos.org/install/">official installation guide</a> and choose between the WebUSB-based installation and the classic terminal-based one.</p>
<p>After that, just follow the step-by-step guide.</p>
<h2 id="alcune-funzionalità-di-grapheneos">Some GrapheneOS features</h2>
<p>What follows is a non-exhaustive overview of GrapheneOS features. For details, see the <a href="https://grapheneos.org/features">official features page</a>.</p>
<h3 id="protezione-contro-le-vulnerabilità-zero-day-oltre-a-funzionalità-aggiuntive-per-utenti-e-rete-dot">Protection against zero-day vulnerabilities, plus extra user and network features.</h3>
<p>GrapheneOS protects its users from zero-day vulnerabilities.
To achieve this, GrapheneOS reduces the attack surface by removing unnecessary code from the operating system.</p>
<p>For app management, GrapheneOS includes dedicated toggles for network and sensor permissions, which are rare on AOSP-based custom ROMs.</p>
<p>At the network level, the operating system supports MAC randomization for every connection and an LTE-only mode to reduce the network attack surface by disabling legacy code (2G, 3G) and bleeding-edge code (5G).
Wi-Fi and Bluetooth (as well as the mobile hotspot) support automatic shutdown if they are not connected to a device, saving battery life and preventing potential wireless attacks from outside.</p>
<p>A notable feature is the private screenshot function that disables the inclusion of sensitive metadata.</p>
<h3 id="sandboxing-and-memory-corruption-protection">Sandboxing and memory-corruption protection.</h3>
<p>To significantly reduce operating system vulnerabilities, the team behind GrapheneOS dedicates significant resources to the development of memory-safe languages and libraries, static and dynamic analysis tools and much more.</p>
<p>GrapheneOS applies sandboxing at multiple levels, hardening both the kernel and the operating system components.
In short, every element of the operating system is isolated in its own compartment, allowing app permissions and processes to remain separate and protecting them from malware and other potential security threats.</p>
<h3 id="the-applications">The applications</h3>
<p>GrapheneOS provides a set of hardened applications designed to reduce permissions and attack surface.</p>
<p>First, there is the WebViewer and the Vanadium browser.
Vanadium is a hardened Chromium-based browser with additional security and privacy measures.</p>
<p>Secure Camera, built by the GrapheneOS team, is the system's default camera. It includes the usual camera features and adds privacy and security options, such as QR scanning without network or media-access permissions and optional EXIF metadata removal from photos and videos.</p>
<p>Auditor provides hardware-based attestation for the device's software and firmware. It is a specialized feature, especially useful for people exposed to targeted risks.</p>
<p>Last but not least is Secure PDF Viewer, another application built by the GrapheneOS team. It is a PDF reader that requires no permissions and runs in a sandbox.</p>
<h3 id="improved-profile-management">Improved profile management</h3>
<p>GrapheneOS has improved the user-profile functionality and is improving monitoring across profiles.</p>
<p>In detail, it gives you the ability to:</p>
<ul>
<li>Add multiple profiles.</li>
<li>End the session.</li>
<li>Disable app installs in specific profiles.</li>
<li>Install available apps from one profile into another.</li>
<li>Forward notifications from inactive profiles to the current session.</li>
</ul>
<h3 id="google-in-a-sandbox">Google in a sandbox</h3>
<p>Google apps can be installed on GrapheneOS through a dedicated compatibility layer.
The apps are stripped of the special access or privileges they would normally have.
You can use Google's applications and services, but they will be reshaped to meet high privacy and security standards.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I have been using GrapheneOS for about a year, and the experience has been positive. Privacy-focused products often require significant usability compromises; in this case, the impact on everyday use stayed limited.</p>
<p>I did not have to radically change how I use my phone. The most annoying part was replacing many applications tied to the Google ecosystem, which deserves its own post.</p>
<p>The main limitation concerns apps that depend on Google Play services. Without installing the dedicated compatibility layer, some features, such as Firebase-based push notifications, may not be available.</p>
<p>Overall, I ended up with a phone that is more controllable, more privacy-oriented, and still comfortable for daily use.</p>]]></content:encoded>
    </item>
    <item>
      <title>Kill switch for wg-quick and nftables</title>
      <link>https://nexenne.com/blog/nftables_wg_quick_killswitch/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/nftables_wg_quick_killswitch/</guid>
      <pubDate>Mon, 20 May 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[How I moved kill-switch rules from iptables to nftables inside a wg-quick configuration.]]></description>
      <category>GNU/Linux</category>
      <category>HowTo</category>
      <category>Administration</category>
      <category>VPN</category>
      <category>Command Line</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>A kill switch prevents traffic from leaving through the regular connection when the WireGuard tunnel is not active, avoiding accidental exposure of the real IP address.</p>
<p>After moving from iptables to nftables, I had to translate the rules in my wg-quick peer configuration. Since I could not find useful information at the time, I decided to share the solution I used.</p>
<h2 id="configuration">Configuration</h2>
<p>This configuration assumes that nftables rules are reloaded through the init system.</p>
<p>In the wg-quick configuration file, add these two lines:</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1">PostUp = nft insert rule &#x3C;family> &#x3C;table> &#x3C;chain> ip oifname != <span class="hljs-string">"%i"</span> mark != $(wg show %i fwmark) fib daddr <span class="hljs-built_in">type</span> != <span class="hljs-built_in">local</span> counter reject</span><span class="ln" data-line="2">PostDown = systemctl restart &#x3C;service></span></code></pre>
<p>These lines need to be adapted to your system and firewall.</p>
<h3 id="postup">PostUp</h3>
<p>In a terminal, run <code>nft list ruleset</code> and look for the <code>type filter hook output</code> directive.</p>
<p>Now adjust the <code>PostUp</code> line with the information you found. In my case this directive lives in the <code>firewalld</code> table, family <code>inet</code>, chain <code>filter_OUTPUT</code>.</p>
<h3 id="postdown">PostDown</h3>
<p>In the <code>PostDown</code> line, put the command that reloads the firewall. In this example I use systemd.</p>
<h2 id="example">Example</h2>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1">PostUp = nft insert rule ip inet firewalld filter_OUTPUT oifname != <span class="hljs-string">"%i"</span> mark != $(wg show %i fwmark) fib daddr <span class="hljs-built_in">type</span> != <span class="hljs-built_in">local</span> counter reject</span><span class="ln" data-line="2">PostDown = systemctl restart firewalld.service</span></code></pre>]]></content:encoded>
    </item>
    <item>
      <title>iptables vs nftables</title>
      <link>https://nexenne.com/blog/iptables_vs_nftables/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/iptables_vs_nftables/</guid>
      <pubDate>Fri, 10 May 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Why nftables makes firewall rules easier to organize than separate iptables and ip6tables configurations.]]></description>
      <category>GNU/Linux</category>
      <category>HowTo</category>
      <category>Administration</category>
      <category>Command Line</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>nftables is the Netfilter framework introduced in the Linux kernel as the successor to iptables. It provides a more consistent and flexible interface, replacing separate tools such as <code>iptables</code>, <code>ip6tables</code>, <code>arptables</code>, and <code>ebtables</code>.</p>
<p>With iptables, IPv4 and IPv6 rules often had to be maintained separately through <code>iptables</code> and <code>ip6tables</code>.</p>
<p>Aside from a new syntax and a few updates, nftables works similarly to its predecessor.</p>
<h2 id="chains-and-rules">Chains and rules</h2>
<p>The most common iptables tables use predefined chains such as <code>INPUT</code>, <code>OUTPUT</code>, and <code>FORWARD</code>. Each chain contains rules evaluated in order; if none matches, the default policy, such as <code>ACCEPT</code> or <code>DROP</code>, is applied.</p>
<p>iptables can become inefficient because packets traverse the expected chains even when some of them are not actually needed, adding unnecessary checks.</p>
<p>nftables keeps the same conceptual model of tables, chains, and rules, but it does not force predefined chains. You create only the chains you need and attach them to the appropriate kernel hooks.</p>
<h2 id="syntax-difference">Syntax difference</h2>
<p>iptables syntax can become hard to read as rules grow. nftables uses a more uniform grammar and can express many cases with less duplication.</p>
<p>To create a new rule you use a form similar to this:</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1">nft add rule family_type table_name chain_name handle handle_value statement</span></code></pre>
<p>Let's compare a few equivalent examples.</p>
<h3 id="blocking-a-connection">Blocking a connection</h3>
<p>This command blocks an inbound connection from IP 192.168.7.5.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-comment"># iptables</span></span><span class="ln" data-line="2">iptables -A INPUT -s 192.168.7.5 -j DROP</span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-comment"># nftables</span></span><span class="ln" data-line="5">nft add rule ip filter INPUT ip saddr 192.168.7.5 counter drop</span></code></pre>
<h3 id="allowing-incoming-ssh-connections">Allowing incoming SSH connections</h3>
<p>This command allows incoming SSH connections.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-comment"># iptables</span></span><span class="ln" data-line="2">iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT</span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-comment"># nftables</span></span><span class="ln" data-line="5">nft add rule ip filter INPUT tcp dport 22 ct state new,established counter accept</span></code></pre>
<p>This command allows incoming SSH connections from the whole 192.168.155.0/24 network.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-comment"># iptables</span></span><span class="ln" data-line="2">iptables -A INPUT -p tcp -s 192.168.155.0/24 --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT</span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-comment"># nftables</span></span><span class="ln" data-line="5">nft add rule ip filter INPUT ip saddr 192.168.155.0/24 tcp dport 22 ct state new,established counter accept</span></code></pre>
<h3 id="allowing-mysql-traffic-on-the-eth0-network-interface">Allowing MySQL traffic on the eth0 network interface</h3>
<p>This command allows connections on the eth0 network interface toward the MySQL server.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-comment"># iptables</span></span><span class="ln" data-line="2">iptables -A INPUT -i eth0 -p tcp --dport 3306 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT</span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-comment"># nftables</span></span><span class="ln" data-line="5">nft add rule ip filter INPUT iifname eth0 tcp dport 3306 ct state new,established counter accept</span></code></pre>
<h3 id="allowing-http-and-https-traffic">Allowing HTTP and HTTPS traffic</h3>
<p>This command allows traffic on ports 80 and 443.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-comment"># iptables</span></span><span class="ln" data-line="2">iptables -A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT</span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-comment"># nftables</span></span><span class="ln" data-line="5">nft add rule ip filter INPUT ip protocol tcp tcp dport { 80, 443 } ct state new,established counter accept</span></code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>nftables offers a more uniform model for managing IPv4, IPv6, and other protocols, with clearer syntax and more flexible configuration. For more details on creating tables and chains, the <a href="https://wiki.archlinux.org/title/Nftables">Arch Linux nftables documentation</a> is a good starting point.</p>]]></content:encoded>
    </item>
    <item>
      <title>Header guards vs pragma once</title>
      <link>https://nexenne.com/blog/header_guards_vs_pragma_once/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/header_guards_vs_pragma_once/</guid>
      <pubDate>Wed, 24 Apr 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[A comparison between header guards and pragma once, with benefits and limits of both options.]]></description>
      <category>C++</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>While developing the "Enne 2D Engine" project, I asked myself more than once: why do I keep writing header guards when I could just use the <code>#pragma once</code> directive?</p>
<p>So I decided to dig into the question and share my analysis.</p>
<h2 id="perché-serve-proteggere-i-file-di-dichiarazione">Why do declaration files need to be protected?</h2>
<h3 id="the-odr-principle">The ODR principle</h3>
<p>The One Definition Rule is a fundamental rule in C++.</p>
<p>In simplified form, it states that a function, variable, or type must respect precise constraints on how many definitions may appear in a program.</p>
<p>Violating the ODR can cause "multiple definitions" errors during compilation or linking problems when the linker cannot decide which definition to use.</p>
<p>In the example below, with three files, compilation fails because <code>struct foo</code> is defined more than once.</p>
<p>File alpha.hpp</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">foo</span> {};</span></code></pre>
<p>File bravo.hpp</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"alpha.hpp"</span></span></span></code></pre>
<p>File charlie.hpp</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"alpha.hpp"</span></span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"bravo.hpp"</span></span></span></code></pre>
<p>The preprocessor, once the relevant substitutions are done, produces the following result.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">foo</span> {};</span><span class="ln" data-line="2"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">foo</span> {};</span></code></pre>
<p>So how do you make sure the ODR is respected?</p>
<p>The first solution that comes to mind, although very spartan, is to manage the <code>#include</code> hierarchy manually.
In the example above, you would have to avoid including <code>alpha.hpp</code> in <code>charlie.hpp</code>.
This is fragile and does not scale to medium or large projects.</p>
<p>There are two common solutions:</p>
<ul>
<li>Header guards.</li>
<li>The <code>#pragma once</code> directive.</li>
</ul>
<h2 id="header-guards">Header guards</h2>
<p>The solution that is part of the C++ standard is the use of header guards.
These guards prevent a header file from being included more than once in a single translation unit.
To achieve this, they use preprocessor macros to check whether the header has already been included before.
If it has already been included, those clauses prevent it from being included again.</p>
<p>The <code>#define</code> directive creates a macro, that is, the association of an identifier (or an identifier with parameters) with a string of tokens.
Once the macro is defined, the compiler can substitute the token string for every occurrence of the identifier in the source file.</p>
<p>Going back to the previous example, with a few small changes we get:</p>
<p>File alpha.hpp</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> ALPHA_HPP</span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">define</span> ALPHA_HPP</span></span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">foo</span> {};</span><span class="ln" data-line="5"></span><span class="ln" data-line="6"><span class="hljs-meta">#<span class="hljs-keyword">endif</span> <span class="hljs-comment">// ALPHA_HPP</span></span></span></code></pre>
<p>File bravo.hpp</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> BRAVO_HPP</span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">define</span> BRAVO_HPP</span></span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"alpha.hpp"</span></span></span><span class="ln" data-line="5"></span><span class="ln" data-line="6"><span class="hljs-meta">#<span class="hljs-keyword">endif</span> <span class="hljs-comment">// BRAVO_HPP</span></span></span></code></pre>
<p>File charlie.hpp</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> CHARLIE_HPP</span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">define</span> CHARLIE_HPP</span></span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"alpha.hpp"</span></span></span><span class="ln" data-line="5"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"bravo.hpp"</span></span></span><span class="ln" data-line="6"></span><span class="ln" data-line="7"><span class="hljs-meta">#<span class="hljs-keyword">endif</span> <span class="hljs-comment">// CHARLIE_HPP</span></span></span></code></pre>
<p>After the relevant substitutions, the preprocessor produces the following result.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">foo</span> {};</span></code></pre>
<p>When working on large projects, it is important to define clear guidelines for macro names: using only the file name can easily lead to clashes. Other problems appear when a header is copied and the macro is not updated, or when the final <code>#endif</code> is missing.
Tools such as clang-tidy can help catch some of these mistakes.</p>
<p>I, for example, follow this scheme to define a macro name: <code>&#x3C;PROJECT_ROOT>_&#x3C;RELATIVE_PATH_TO_HPP_FILE>_&#x3C;FILE_NAME>_HPP_</code>.</p>
<p>This avoids generating the same identifier for two files with the same name.</p>
<p>Assume the following structure, with <code>CPP_PROJECT</code> as the root directory, and two files with the same name in different directories.</p>
<pre class="has-lang" data-lang="txt"><code class="hljs language-txt"><span class="ln" data-line="1">.</span><span class="ln" data-line="2">├── libfoo</span><span class="ln" data-line="3">│   ├── CMakeLists.txt</span><span class="ln" data-line="4">│   ├── docs</span><span class="ln" data-line="5">│   │   └── CMakeLists.txt</span><span class="ln" data-line="6">│   ├── include</span><span class="ln" data-line="7">│   │   └── libfoo</span><span class="ln" data-line="8">│   │       ├── detail</span><span class="ln" data-line="9">│   │       │   └── alpha.hpp</span><span class="ln" data-line="10">│   │       └── common</span><span class="ln" data-line="11">│   │           └── alpha.hpp</span></code></pre>
<p>The macro name on the <code>alpha.hpp</code> file inside the <code>detail</code> directory will be:</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">define</span> CPP_PROJECT_LIBFOO_INCLUDE_LIBFOO_DETAIL_ALPHA_HPP_</span></span></code></pre>
<p>The macro name on the <code>alpha.hpp</code> file inside the <code>common</code> directory will be:</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">define</span> CPP_PROJECT_LIBFOO_INCLUDE_LIBFOO_COMMON_ALPHA_HPP_</span></span></code></pre>
<h2 id="pragma-once">Pragma once</h2>
<p>The alternative to header guards, widely used but not part of the C++ standard, is the <code>#pragma once</code> directive.</p>
<p>File alpha.hpp</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">pragma</span> once</span></span><span class="ln" data-line="2"></span><span class="ln" data-line="3"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">foo</span> {};</span></code></pre>
<p>File bravo.hpp</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">pragma</span> once</span></span><span class="ln" data-line="2"></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"alpha.hpp"</span></span></span></code></pre>
<p>File charlie.hpp</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">pragma</span> once</span></span><span class="ln" data-line="2"></span><span class="ln" data-line="3"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"alpha.hpp"</span></span></span><span class="ln" data-line="4"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"bravo.hpp"</span></span></span></code></pre>
<p>After the relevant substitutions, the preprocessor produces the following result.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">foo</span> {};</span></code></pre>
<p>You write less code and avoid macro-name clashes, but this solution has trade-offs.</p>
<p><code>#pragma once</code> is not part of the C++ standard, so compilers are not required by ISO C++ to support it.</p>
<p>But why isn't it part of the standard?</p>
<p>The answer lies in the complexity of detecting file equality consistently.
One known issue involves reaching the same file through different paths or symbolic links. In some cases, a compiler may fail to recognize that two paths refer to the same header and include it more than once. See <a href="https://en.m.wikipedia.org/wiki/Pragma_once#Caveats">https://en.m.wikipedia.org/wiki/Pragma_once#Caveats</a>.</p>
<p>There is also no guarantee that support for <code>#pragma once</code> is the same across different compilers, which can be a problem for some developers.</p>
<h2 id="header-guards-or-pragma-once">Header guards or pragma once?</h2>
<p>It depends on the case.</p>
<p>The choice depends on portability requirements and project conventions.</p>
<p>Header guards are standard and work everywhere, at the cost of a few extra lines and a reliable macro convention. <code>#pragma once</code> is shorter and widely supported by common compilers, but it is not part of the standard.</p>
<h2 id="conclusion">Conclusion</h2>
<p>For projects where portability is a priority, I prefer header guards. In codebases with a well-defined toolchain, <code>#pragma once</code> remains a pragmatic and widely supported choice.</p>]]></content:encoded>
    </item>
    <item>
      <title>Undoing the last Git commit</title>
      <link>https://nexenne.com/blog/git_revert_commit/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/git_revert_commit/</guid>
      <pubDate>Mon, 15 Apr 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[The difference between git revert and git reset when you need to undo the last commit.]]></description>
      <category>Git</category>
      <category>Control</category>
      <category>Command Line</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Suppose you are working on a project with <code>Git</code>.
By mistake or distraction, you may create a commit that should not exist.
In that situation you need to undo it in the right way, choosing between two very different approaches.</p>
<p>The main commands are:</p>
<ul>
<li>The <code>revert</code> command</li>
<li>The <code>reset</code> command</li>
</ul>
<h2 id="the-revert-command">The revert command</h2>
<p>The <code>revert</code> command creates a new commit that reverses the changes from the commit we want to undo.</p>
<p>The basic form is:</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1">git revert &#x3C;commit to revert></span></code></pre>
<p>The usual steps are:</p>
<ol>
<li>Run the <code>git log</code> command.</li>
<li>Find the commit you want to revert.</li>
<li>Copy the alphanumeric hash of the commit to revert.</li>
<li>Run <code>git revert &#x3C;commit to revert></code>.</li>
</ol>
<p>Example.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-built_in">cd</span> project_with_a_dot_git_folder</span><span class="ln" data-line="2"></span><span class="ln" data-line="3">git <span class="hljs-built_in">log</span></span><span class="ln" data-line="4"><span class="hljs-comment">### Example of the git log output</span></span><span class="ln" data-line="5"><span class="hljs-comment">#</span></span><span class="ln" data-line="6"><span class="hljs-comment"># commit 2596f783998c8ec230b38a044b49e39d07770901 (HEAD -> main, origin/main)</span></span><span class="ln" data-line="7"><span class="hljs-comment"># Author: Foo &#x3C;foo@bar.com></span></span><span class="ln" data-line="8"><span class="hljs-comment"># Date:   Tue Apr 9 14:47:17 2024 +0200</span></span><span class="ln" data-line="9"><span class="hljs-comment">#</span></span><span class="ln" data-line="10"><span class="hljs-comment">#     Add something bad</span></span><span class="ln" data-line="11"><span class="hljs-comment">#</span></span><span class="ln" data-line="12"><span class="hljs-comment"># commit 2ce5e2e7e36f23673b32ccef7f908f161527142b</span></span><span class="ln" data-line="13"><span class="hljs-comment"># Author: Foo &#x3C;foo@bar.com></span></span><span class="ln" data-line="14"><span class="hljs-comment"># Date:   Tue Apr 2 23:33:27 2024 +0200</span></span><span class="ln" data-line="15"><span class="hljs-comment">#</span></span><span class="ln" data-line="16"><span class="hljs-comment">#     Update something</span></span><span class="ln" data-line="17"></span><span class="ln" data-line="18">git revert 2ce5e2e7e36f23673b32ccef7f908f161527142b</span></code></pre>
<p>After running the command, a new commit will be generated that reverses the earlier changes, without rewriting commit history.</p>
<p><img src="/blog/images/git_revert_commit_before_revert.png" alt=""></p>
<p><img src="/blog/images/git_revert_commit_after_revert.png" alt=""></p>
<h2 id="the-reset-command">The reset command</h2>
<p>The alternative is the <code>reset</code> command.
<code>git reset</code> rewrites local history, so it should be used carefully, especially if the commits have already been shared. It moves <code>HEAD</code> to the selected commit; what happens to files and the index depends on the option used.</p>
<h3 id="soft-reset">Soft reset</h3>
<p>The <code>--soft</code> option moves <code>HEAD</code> while keeping the removed commit's changes staged in the index.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1">git reset --soft HEAD~1</span></code></pre>
<p><img src="/blog/images/fit_revert_commit_before_soft_reset.png" alt=""></p>
<p><img src="/blog/images/fit_revert_commit_after_soft_reset.png" alt=""></p>
<h3 id="hard-reset">Hard reset</h3>
<p>The <code>--hard</code> option aligns both the index and working tree with the selected commit, discarding local uncommitted changes too.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1">git reset --hard HEAD~1</span></code></pre>
<p><img src="/blog/images/git_revert_commit_before_hard_reset.png" alt=""></p>
<p><img src="/blog/images/git_revert_commit_after_hard_reset.png" alt=""></p>
<h2 id="conclusion">Conclusion</h2>
<p><code>git revert</code> is the safer choice for history that has already been shared, because it adds a new commit instead of rewriting existing ones. <code>git reset</code> is mainly useful for cleaning up local history before it is published.</p>]]></content:encoded>
    </item>
    <item>
      <title>How to check a package version on GNU/Linux</title>
      <link>https://nexenne.com/blog/check_package_version_on_linux/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/check_package_version_on_linux/</guid>
      <pubDate>Tue, 09 Apr 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[A quick way to check an installed package version without running the binary directly.]]></description>
      <category>GNU/Linux</category>
      <category>HowTo</category>
      <category>Administration</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>After the backdoor in the XZ Utils package was disclosed (<a href="https://nvd.nist.gov/vuln/detail/CVE-2024-3094">CVE-2024-3094</a>),
I noticed many people suggesting <code>xz --version</code> to check the installed package version instead of asking the distribution's package manager.</p>
<p>If an executable may be compromised, it is better not to run it just to learn its version. The package manager can provide the same information without executing the binary.</p>
<p>So let's look at how to check the version of an installed package on GNU/Linux systems.
For convenience I split the distributions by package manager.</p>
<p>Note that the commands below explicitly filter for packages with <code>xz</code> in the name.</p>
<h2 id="debian">Debian</h2>
<p>This works on every Debian-based distribution.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-comment"># apt</span></span><span class="ln" data-line="2">apt list xz</span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-comment"># or with more details</span></span><span class="ln" data-line="5">apt show xz</span><span class="ln" data-line="6"></span><span class="ln" data-line="7"><span class="hljs-comment"># or with dpkg</span></span><span class="ln" data-line="8">dpkg-query -l <span class="hljs-string">'*xz*'</span></span><span class="ln" data-line="9"></span><span class="ln" data-line="10"><span class="hljs-comment"># or</span></span><span class="ln" data-line="11">dpkg-query -l | grep xz</span></code></pre>
<h2 id="fedora">Fedora</h2>
<p>This applies to Fedora and to distributions that use RPM and DNF.
You can also use RPM Package Manager (RPM).</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-comment"># dnf</span></span><span class="ln" data-line="2">dnf list installed xz*</span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-comment"># or</span></span><span class="ln" data-line="5">dnf list installed | grep xz</span><span class="ln" data-line="6"></span><span class="ln" data-line="7"><span class="hljs-comment"># or with yum</span></span><span class="ln" data-line="8">yum list installed | grep xz</span><span class="ln" data-line="9"></span><span class="ln" data-line="10"><span class="hljs-comment"># or via RPM</span></span><span class="ln" data-line="11">rpm -qa | grep xz</span></code></pre>
<h2 id="arch-linux">Arch Linux</h2>
<p>This also works for Arch-based distributions, including Manjaro, EndeavourOS and SteamOS.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-comment"># pacman</span></span><span class="ln" data-line="2">pacman -Qs xz</span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-comment"># or</span></span><span class="ln" data-line="5">pacman -Q | grep xz</span></code></pre>
<h2 id="opensuse-tumbleweed">openSUSE Tumbleweed</h2>
<p>These commands are also valid for SUSE- and openSUSE-based distributions like GeckoLinux and Linux Kamarada.
You can also use RPM.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1"><span class="hljs-comment"># zypper</span></span><span class="ln" data-line="2">zypper info xz</span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-comment"># or</span></span><span class="ln" data-line="5">rpm -qa | grep xz</span></code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Signals and Slots</title>
      <link>https://nexenne.com/blog/signals_and_slots/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/signals_and_slots/</guid>
      <pubDate>Mon, 01 Apr 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Starting from Qt's signal/slot model, I build a small modern C++ implementation.]]></description>
      <category>C++</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>After working with Qt, I started wondering how a signal/slot mechanism could be implemented in modern C++.</p>
<p>Wikipedia defines it this way.</p>
<blockquote>
<p>[...] a language construct [...] which makes it easy to implement the Observer pattern while avoiding boilerplate code.
The concept is that GUI widgets can send signals containing event information which can be received by other controls using special functions known as slots.</p>
<p>-- Wikipedia</p>
</blockquote>
<p>In simpler words, the signal/slot mechanism enables event-based communication between objects.
Several libraries implement this pattern very well; here I present a small personal implementation built mainly to understand the mechanism better.</p>
<h2 id="uml-diagram">UML diagram</h2>
<p>The following UML diagram summarizes the project structure.</p>
<p><img src="/blog/images/signals_and_slots_uml.png" alt=""></p>
<h2 id="the-code">The code</h2>
<p>Because the implementation uses templates, the code lives entirely in a header file.
The comments describe the responsibilities and behavior of the main components.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> LIBSIGSLOTPP_INCLUDE_LIBSIGSLOTPP_SIGNAL_HPP_</span></span><span class="ln" data-line="2"><span class="hljs-meta">#<span class="hljs-keyword">define</span> LIBSIGSLOTPP_INCLUDE_LIBSIGSLOTPP_SIGNAL_HPP_</span></span><span class="ln" data-line="3"></span><span class="ln" data-line="4"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;functional></span></span></span><span class="ln" data-line="5"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&#x3C;memory></span></span></span><span class="ln" data-line="6"></span><span class="ln" data-line="7"><span class="hljs-keyword">namespace</span> sigslotpp {</span><span class="ln" data-line="8"></span><span class="ln" data-line="9"><span class="hljs-keyword">typedef</span> std::<span class="hljs-type">size_t</span> IDType;</span><span class="ln" data-line="10"></span><span class="ln" data-line="11"><span class="hljs-comment">/**</span></span><span class="ln" data-line="12"><span class="hljs-comment"> * @brief Interface ISlotConnection</span></span><span class="ln" data-line="13"><span class="hljs-comment"> * It provides a simple interface that identifies the Slot and its main</span></span><span class="ln" data-line="14"><span class="hljs-comment"> * connectivity operations.</span></span><span class="ln" data-line="15"><span class="hljs-comment"> * ISignal can access private and protected regions for design purposes.</span></span><span class="ln" data-line="16"><span class="hljs-comment"> */</span></span><span class="ln" data-line="17"><span class="hljs-keyword">class</span> <span class="hljs-title class_">ISlotConnection</span> {</span><span class="ln" data-line="18"> <span class="hljs-keyword">private</span>:</span><span class="ln" data-line="19">  <span class="hljs-keyword">friend</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ISignal</span>;</span><span class="ln" data-line="20"></span><span class="ln" data-line="21">  IDType id_;         <span class="hljs-comment">///&#x3C; identifier and index into the signal slots vector.</span></span><span class="ln" data-line="22">  <span class="hljs-type">bool</span> isConnected_;  <span class="hljs-comment">///&#x3C; slot connection status.</span></span><span class="ln" data-line="23">  <span class="hljs-type">bool</span> isBlocked_;    <span class="hljs-comment">///&#x3C; slot connection block status.</span></span><span class="ln" data-line="24"></span><span class="ln" data-line="25"> <span class="hljs-keyword">protected</span>:</span><span class="ln" data-line="26">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="27"><span class="hljs-comment">   * @brief Getter for the identifier</span></span><span class="ln" data-line="28"><span class="hljs-comment">   * @return id_</span></span><span class="ln" data-line="29"><span class="hljs-comment">   */</span></span><span class="ln" data-line="30">  <span class="hljs-function">IDType <span class="hljs-title">id</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span> </span>{ <span class="hljs-keyword">return</span> id_; }</span><span class="ln" data-line="31"></span><span class="ln" data-line="32">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="33"><span class="hljs-comment">   * @brief Getter/Setter for the identifier</span></span><span class="ln" data-line="34"><span class="hljs-comment">   * @return a reference to id_</span></span><span class="ln" data-line="35"><span class="hljs-comment">   */</span></span><span class="ln" data-line="36">  <span class="hljs-function">IDType &#x26;<span class="hljs-title">id</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span> </span>{ <span class="hljs-keyword">return</span> id_; }</span><span class="ln" data-line="37"></span><span class="ln" data-line="38">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="39"><span class="hljs-comment">   * @brief Clears the slot connection</span></span><span class="ln" data-line="40"><span class="hljs-comment">   * Where the real disconnection happens.</span></span><span class="ln" data-line="41"><span class="hljs-comment">   * This gets called by the ISlotConnection::disconnect() function, the</span></span><span class="ln" data-line="42"><span class="hljs-comment">   * implementation depends on the child subclasses.</span></span><span class="ln" data-line="43"><span class="hljs-comment">   */</span></span><span class="ln" data-line="44">  <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">void</span> <span class="hljs-title">clear</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span> </span>= <span class="hljs-number">0</span>;</span><span class="ln" data-line="45"></span><span class="ln" data-line="46"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="47">  <span class="hljs-built_in">ISlotConnection</span>() = <span class="hljs-keyword">delete</span>;  <span class="hljs-comment">// not needed but better for compilation error</span></span><span class="ln" data-line="48"></span><span class="ln" data-line="49">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="50"><span class="hljs-comment">   * @brief Primary constructor</span></span><span class="ln" data-line="51"><span class="hljs-comment">   * @post id_ == id</span></span><span class="ln" data-line="52"><span class="hljs-comment">   * @post isConnected__ == isConnected</span></span><span class="ln" data-line="53"><span class="hljs-comment">   * @post isBlocked_ == isBlocked</span></span><span class="ln" data-line="54"><span class="hljs-comment">   */</span></span><span class="ln" data-line="55">  <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">ISlotConnection</span><span class="hljs-params">(<span class="hljs-type">const</span> IDType id, <span class="hljs-type">const</span> <span class="hljs-type">bool</span> isConnected = <span class="hljs-literal">true</span>,</span></span></span><span class="ln" data-line="56"><span class="hljs-function"><span class="hljs-params">                           <span class="hljs-type">const</span> <span class="hljs-type">bool</span> isBlocked = <span class="hljs-literal">false</span>)</span> <span class="hljs-keyword">noexcept</span></span></span><span class="ln" data-line="57"><span class="hljs-function">      : id_(id), isConnected_(isConnected), isBlocked_(isBlocked) {</span>}</span><span class="ln" data-line="58"></span><span class="ln" data-line="59">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="60"><span class="hljs-comment">   * @brief Destructor</span></span><span class="ln" data-line="61"><span class="hljs-comment">   */</span></span><span class="ln" data-line="62">  <span class="hljs-keyword">virtual</span> ~<span class="hljs-built_in">ISlotConnection</span>() {}</span><span class="ln" data-line="63"></span><span class="ln" data-line="64"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="65">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="66"><span class="hljs-comment">   * @brief Checks the connection status</span></span><span class="ln" data-line="67"><span class="hljs-comment">   * Used to see if a slot is still connected to a Signal</span></span><span class="ln" data-line="68"><span class="hljs-comment">   */</span></span><span class="ln" data-line="69">  <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">bool</span> <span class="hljs-title">isConnected</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span> </span>{ <span class="hljs-keyword">return</span> isConnected_; }</span><span class="ln" data-line="70"></span><span class="ln" data-line="71">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="72"><span class="hljs-comment">   * @brief Checks if the connection is blocked</span></span><span class="ln" data-line="73"><span class="hljs-comment">   * Used to temporarily disable slot invocation.</span></span><span class="ln" data-line="74"><span class="hljs-comment">   * @return true if the slot invocation is blocked else false.</span></span><span class="ln" data-line="75"><span class="hljs-comment">   */</span></span><span class="ln" data-line="76">  <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isBlocked</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span> </span>{ <span class="hljs-keyword">return</span> isBlocked_; }</span><span class="ln" data-line="77"></span><span class="ln" data-line="78"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="79">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="80"><span class="hljs-comment">   * @brief Blocks the slot invocation</span></span><span class="ln" data-line="81"><span class="hljs-comment">   * @post isBlocked_ == true</span></span><span class="ln" data-line="82"><span class="hljs-comment">   */</span></span><span class="ln" data-line="83">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">block</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span> </span>{ isBlocked_ = <span class="hljs-literal">true</span>; }</span><span class="ln" data-line="84"></span><span class="ln" data-line="85">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="86"><span class="hljs-comment">   * @brief Unblocks the slot invocation</span></span><span class="ln" data-line="87"><span class="hljs-comment">   * @post isBlocked_ == false</span></span><span class="ln" data-line="88"><span class="hljs-comment">   */</span></span><span class="ln" data-line="89">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">unblock</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span> </span>{ isBlocked_ = <span class="hljs-literal">false</span>; }</span><span class="ln" data-line="90"></span><span class="ln" data-line="91">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="92"><span class="hljs-comment">   * @brief Disconnects the slot from the signal</span></span><span class="ln" data-line="93"><span class="hljs-comment">   * @post isConnected_ == false</span></span><span class="ln" data-line="94"><span class="hljs-comment">   */</span></span><span class="ln" data-line="95">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">disconnect</span><span class="hljs-params">()</span> </span>{</span><span class="ln" data-line="96">    <span class="hljs-keyword">if</span> (isConnected_) {</span><span class="ln" data-line="97">      isConnected_ = <span class="hljs-literal">false</span>;</span><span class="ln" data-line="98">      <span class="hljs-built_in">clear</span>();</span><span class="ln" data-line="99">    }</span><span class="ln" data-line="100">  }</span><span class="ln" data-line="101">};</span><span class="ln" data-line="102"></span><span class="ln" data-line="103"><span class="hljs-comment">/**</span></span><span class="ln" data-line="104"><span class="hljs-comment"> * @brief Class Connection</span></span><span class="ln" data-line="105"><span class="hljs-comment"> * This class allows interaction with an ongoing signal-slot connection and</span></span><span class="ln" data-line="106"><span class="hljs-comment"> * exposes an interface to manipulate and query the status of this connection.</span></span><span class="ln" data-line="107"><span class="hljs-comment"> * Note that Connection is not a RAII object, one does not need to hold one</span></span><span class="ln" data-line="108"><span class="hljs-comment"> * such object to keep the signal-slot connection alive.</span></span><span class="ln" data-line="109"><span class="hljs-comment"> */</span></span><span class="ln" data-line="110"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Connection</span> {</span><span class="ln" data-line="111"> <span class="hljs-keyword">private</span>:</span><span class="ln" data-line="112">  std::weak_ptr&#x3C;ISlotConnection> slot_;  <span class="hljs-comment">///&#x3C; the slot to manipulate and query</span></span><span class="ln" data-line="113"></span><span class="ln" data-line="114"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="115">  <span class="hljs-built_in">Connection</span>() = <span class="hljs-keyword">delete</span>;  <span class="hljs-comment">// not needed but better for compilation error</span></span><span class="ln" data-line="116"></span><span class="ln" data-line="117">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="118"><span class="hljs-comment">   * @brief Primary constructor</span></span><span class="ln" data-line="119"><span class="hljs-comment">   * @param slot that will be handled by this connection</span></span><span class="ln" data-line="120"><span class="hljs-comment">   */</span></span><span class="ln" data-line="121">  <span class="hljs-function"><span class="hljs-keyword">explicit</span> <span class="hljs-title">Connection</span><span class="hljs-params">(std::weak_ptr&#x3C;ISlotConnection> &#x26;&#x26;slot)</span> : slot_(slot) {</span>}</span><span class="ln" data-line="122"></span><span class="ln" data-line="123">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="124"><span class="hljs-comment">   * @brief Destructor</span></span><span class="ln" data-line="125"><span class="hljs-comment">   */</span></span><span class="ln" data-line="126">  <span class="hljs-keyword">virtual</span> ~<span class="hljs-built_in">Connection</span>() {}</span><span class="ln" data-line="127"></span><span class="ln" data-line="128"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="129">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="130"><span class="hljs-comment">   * @brief Checks if this connection is still valid</span></span><span class="ln" data-line="131"><span class="hljs-comment">   * To have this information just see if the std::weak_ptr is expired</span></span><span class="ln" data-line="132"><span class="hljs-comment">   * @return true if the connection is valid else false</span></span><span class="ln" data-line="133"><span class="hljs-comment">   */</span></span><span class="ln" data-line="134">  <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isValid</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span> </span>{ <span class="hljs-keyword">return</span> !slot_.<span class="hljs-built_in">expired</span>(); }</span><span class="ln" data-line="135"></span><span class="ln" data-line="136">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="137"><span class="hljs-comment">   * @brief Checks if the slot is still connected to its signal</span></span><span class="ln" data-line="138"><span class="hljs-comment">   * @return true if the slot is still connected else false</span></span><span class="ln" data-line="139"><span class="hljs-comment">   * @see ISlotConnection::isConnected()</span></span><span class="ln" data-line="140"><span class="hljs-comment">   */</span></span><span class="ln" data-line="141">  <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isConnected</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="142">    std::shared_ptr&#x3C;ISlotConnection> d = slot_.<span class="hljs-built_in">lock</span>();</span><span class="ln" data-line="143">    <span class="hljs-keyword">return</span> d &#x26;&#x26; d-><span class="hljs-built_in">isConnected</span>();</span><span class="ln" data-line="144">  }</span><span class="ln" data-line="145"></span><span class="ln" data-line="146">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="147"><span class="hljs-comment">   * @brief Checks if the slot connection is blocked</span></span><span class="ln" data-line="148"><span class="hljs-comment">   * @return true if the slot connection is blocked else false</span></span><span class="ln" data-line="149"><span class="hljs-comment">   * @see ISlotConnection::isBLocked()</span></span><span class="ln" data-line="150"><span class="hljs-comment">   */</span></span><span class="ln" data-line="151">  <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isBlocked</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="152">    std::shared_ptr&#x3C;ISlotConnection> d = slot_.<span class="hljs-built_in">lock</span>();</span><span class="ln" data-line="153">    <span class="hljs-keyword">return</span> d &#x26;&#x26; d-><span class="hljs-built_in">isBlocked</span>();</span><span class="ln" data-line="154">  }</span><span class="ln" data-line="155"></span><span class="ln" data-line="156">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="157"><span class="hljs-comment">   * @brief Blocks the slot invocation</span></span><span class="ln" data-line="158"><span class="hljs-comment">   * @see ISlotConnection::block()</span></span><span class="ln" data-line="159"><span class="hljs-comment">   */</span></span><span class="ln" data-line="160">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">block</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="161">    std::shared_ptr&#x3C;ISlotConnection> d = slot_.<span class="hljs-built_in">lock</span>();</span><span class="ln" data-line="162">    <span class="hljs-keyword">if</span> (d) d-><span class="hljs-built_in">block</span>();</span><span class="ln" data-line="163">  }</span><span class="ln" data-line="164"></span><span class="ln" data-line="165">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="166"><span class="hljs-comment">   * @brief Unblocks the slot invocation</span></span><span class="ln" data-line="167"><span class="hljs-comment">   * @see ISlotConnection::unblock()</span></span><span class="ln" data-line="168"><span class="hljs-comment">   */</span></span><span class="ln" data-line="169">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">unblock</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="170">    std::shared_ptr&#x3C;ISlotConnection> d = slot_.<span class="hljs-built_in">lock</span>();</span><span class="ln" data-line="171">    <span class="hljs-keyword">if</span> (d) d-><span class="hljs-built_in">unblock</span>();</span><span class="ln" data-line="172">  }</span><span class="ln" data-line="173"></span><span class="ln" data-line="174">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="175"><span class="hljs-comment">   * @brief Disconnects the slot from its signal</span></span><span class="ln" data-line="176"><span class="hljs-comment">   * @see ISlotConnection::disconnect()</span></span><span class="ln" data-line="177"><span class="hljs-comment">   */</span></span><span class="ln" data-line="178">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">disconnect</span><span class="hljs-params">()</span> </span>{</span><span class="ln" data-line="179">    std::shared_ptr&#x3C;ISlotConnection> d = slot_.<span class="hljs-built_in">lock</span>();</span><span class="ln" data-line="180">    <span class="hljs-keyword">if</span> (d) d-><span class="hljs-built_in">disconnect</span>();</span><span class="ln" data-line="181">  }</span><span class="ln" data-line="182">};</span><span class="ln" data-line="183"></span><span class="ln" data-line="184"><span class="hljs-comment">///////////////////////////////</span></span><span class="ln" data-line="185"></span><span class="ln" data-line="186"><span class="hljs-comment">/**</span></span><span class="ln" data-line="187"><span class="hljs-comment"> * @brief Interface ISignal</span></span><span class="ln" data-line="188"><span class="hljs-comment"> * It provides a simple interface that identifies the Signal and its essential</span></span><span class="ln" data-line="189"><span class="hljs-comment"> * functions.</span></span><span class="ln" data-line="190"><span class="hljs-comment"> */</span></span><span class="ln" data-line="191"><span class="hljs-keyword">class</span> <span class="hljs-title class_">ISignal</span> {</span><span class="ln" data-line="192"> <span class="hljs-keyword">protected</span>:</span><span class="ln" data-line="193">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="194"><span class="hljs-comment">   * @brief Getter for specified ISlotConnection identifier</span></span><span class="ln" data-line="195"><span class="hljs-comment">   * @return id of the specified slot</span></span><span class="ln" data-line="196"><span class="hljs-comment">   */</span></span><span class="ln" data-line="197">  <span class="hljs-function">IDType <span class="hljs-title">idOf</span><span class="hljs-params">(ISlotConnection *<span class="hljs-type">const</span> slotPtr)</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="198">    <span class="hljs-keyword">return</span> slotPtr-><span class="hljs-built_in">id</span>();</span><span class="ln" data-line="199">  }</span><span class="ln" data-line="200"></span><span class="ln" data-line="201">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="202"><span class="hljs-comment">   * @brief Getter/Setter for specified ISlotConnection identifier</span></span><span class="ln" data-line="203"><span class="hljs-comment">   * @return a reference to id of the specified slot</span></span><span class="ln" data-line="204"><span class="hljs-comment">   */</span></span><span class="ln" data-line="205">  <span class="hljs-function">IDType &#x26;<span class="hljs-title">idOf</span><span class="hljs-params">(ISlotConnection *<span class="hljs-type">const</span> slotPtr)</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="206">    <span class="hljs-keyword">return</span> slotPtr-><span class="hljs-built_in">id</span>();</span><span class="ln" data-line="207">  }</span><span class="ln" data-line="208"></span><span class="ln" data-line="209"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="210">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="211"><span class="hljs-comment">   * @brief Destructor</span></span><span class="ln" data-line="212"><span class="hljs-comment">   */</span></span><span class="ln" data-line="213">  <span class="hljs-keyword">virtual</span> ~<span class="hljs-built_in">ISignal</span>() {}</span><span class="ln" data-line="214"></span><span class="ln" data-line="215">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="216"><span class="hljs-comment">   * @brief Disconnect the slot from this signal</span></span><span class="ln" data-line="217"><span class="hljs-comment">   * @param slot a pointer to the slot to disconnect from</span></span><span class="ln" data-line="218"><span class="hljs-comment">   */</span></span><span class="ln" data-line="219">  <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">void</span> <span class="hljs-title">disconnect</span><span class="hljs-params">(ISlotConnection *<span class="hljs-type">const</span> slot)</span> </span>= <span class="hljs-number">0</span>;</span><span class="ln" data-line="220">};</span><span class="ln" data-line="221"></span><span class="ln" data-line="222"><span class="hljs-comment">/**</span></span><span class="ln" data-line="223"><span class="hljs-comment"> * @brief Interface ISlot</span></span><span class="ln" data-line="224"><span class="hljs-comment"> * It provides a simple interface that identifies the Slot and its core</span></span><span class="ln" data-line="225"><span class="hljs-comment"> * invocation functionality.</span></span><span class="ln" data-line="226"><span class="hljs-comment"> * @tparam Args... the argument types of the function to call</span></span><span class="ln" data-line="227"><span class="hljs-comment"> */</span></span><span class="ln" data-line="228"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span>... Args></span><span class="ln" data-line="229"><span class="hljs-keyword">class</span> <span class="hljs-title class_">ISlot</span> : <span class="hljs-keyword">public</span> ISlotConnection {</span><span class="ln" data-line="230"> <span class="hljs-keyword">private</span>:</span><span class="ln" data-line="231">  ISignal &#x26;signal_;  <span class="hljs-comment">///&#x3C; reference to the signal</span></span><span class="ln" data-line="232"></span><span class="ln" data-line="233"> <span class="hljs-keyword">protected</span>:</span><span class="ln" data-line="234">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="235"><span class="hljs-comment">   * @brief Clears the slot connection</span></span><span class="ln" data-line="236"><span class="hljs-comment">   */</span></span><span class="ln" data-line="237">  <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">void</span> <span class="hljs-title">clear</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span> <span class="hljs-keyword">override</span> </span>{ signal_.<span class="hljs-built_in">disconnect</span>(<span class="hljs-keyword">this</span>); }</span><span class="ln" data-line="238"></span><span class="ln" data-line="239">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="240"><span class="hljs-comment">   * @brief Invokes the function</span></span><span class="ln" data-line="241"><span class="hljs-comment">   * Where the function is really invoked.</span></span><span class="ln" data-line="242"><span class="hljs-comment">   * This gets called by the ISlot::operator()() function, the</span></span><span class="ln" data-line="243"><span class="hljs-comment">   * implementation depends on the child subclasses.</span></span><span class="ln" data-line="244"><span class="hljs-comment">   * @param args the arguments of the function to call</span></span><span class="ln" data-line="245"><span class="hljs-comment">   */</span></span><span class="ln" data-line="246">  <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">void</span> <span class="hljs-title">invoke</span><span class="hljs-params">(Args... args)</span> </span>= <span class="hljs-number">0</span>;</span><span class="ln" data-line="247"></span><span class="ln" data-line="248"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="249">  <span class="hljs-built_in">ISlot</span>() = <span class="hljs-keyword">delete</span>;  <span class="hljs-comment">// not needed but better for compilation error</span></span><span class="ln" data-line="250"></span><span class="ln" data-line="251">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="252"><span class="hljs-comment">   * @brief Primary constructor</span></span><span class="ln" data-line="253"><span class="hljs-comment">   * We have a reference that must be initialized, so no default constructor</span></span><span class="ln" data-line="254"><span class="hljs-comment">   * allowed here.</span></span><span class="ln" data-line="255"><span class="hljs-comment">   * @param id the slot identifier</span></span><span class="ln" data-line="256"><span class="hljs-comment">   * @param signal the reference of the signal connected to this slot</span></span><span class="ln" data-line="257"><span class="hljs-comment">   * @post signal_ points to the signal connected to this slot</span></span><span class="ln" data-line="258"><span class="hljs-comment">   * @see ISlotConnection</span></span><span class="ln" data-line="259"><span class="hljs-comment">   */</span></span><span class="ln" data-line="260">  <span class="hljs-built_in">ISlot</span>(<span class="hljs-type">const</span> IDType id, ISignal &#x26;signal) <span class="hljs-keyword">noexcept</span></span><span class="ln" data-line="261">      : <span class="hljs-built_in">ISlotConnection</span>(id), <span class="hljs-built_in">signal_</span>(signal) {}</span><span class="ln" data-line="262"></span><span class="ln" data-line="263">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="264"><span class="hljs-comment">   * @brief Invokes the function</span></span><span class="ln" data-line="265"><span class="hljs-comment">   * Invokes or calls the function stored in the slot.</span></span><span class="ln" data-line="266"><span class="hljs-comment">   * Take note that i add an extra template here for flexibility and design</span></span><span class="ln" data-line="267"><span class="hljs-comment">   * purposes. As mentioned in Signal::emit() we use the signature here.</span></span><span class="ln" data-line="268"><span class="hljs-comment">   * At the end it is used std::forward to forward lvalues as either lvalues</span></span><span class="ln" data-line="269"><span class="hljs-comment">   * or as rvalues, depending on Params.</span></span><span class="ln" data-line="270"><span class="hljs-comment">   * @see Signal::emit()</span></span><span class="ln" data-line="271"><span class="hljs-comment">   * @param params the parameters of the function to call</span></span><span class="ln" data-line="272"><span class="hljs-comment">   * @tparam ...Params types of the parameters of the function to call</span></span><span class="ln" data-line="273"><span class="hljs-comment">   */</span></span><span class="ln" data-line="274">  <span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span>... Params></span><span class="ln" data-line="275">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">operator</span><span class="hljs-params">()</span><span class="hljs-params">(Params &#x26;&#x26;...params)</span> </span>{</span><span class="ln" data-line="276">    <span class="hljs-keyword">if</span> (ISlotConnection::<span class="hljs-built_in">isConnected</span>() &#x26;&#x26; !ISlotConnection::<span class="hljs-built_in">isBlocked</span>())</span><span class="ln" data-line="277">      <span class="hljs-built_in">invoke</span>(std::forward&#x3C;Params>(params)...);</span><span class="ln" data-line="278">  }</span><span class="ln" data-line="279">};</span><span class="ln" data-line="280"></span><span class="ln" data-line="281"><span class="hljs-comment">/**</span></span><span class="ln" data-line="282"><span class="hljs-comment"> * @brief Class SimpleSlot</span></span><span class="ln" data-line="283"><span class="hljs-comment"> * Basic slot that does not track anything and thus it is imperative that what</span></span><span class="ln" data-line="284"><span class="hljs-comment"> * is called by the std::function must exceeds the lifetime of signal this</span></span><span class="ln" data-line="285"><span class="hljs-comment"> * slot is connected to.</span></span><span class="ln" data-line="286"><span class="hljs-comment"> * @tparam Args... the argument types of the function to call</span></span><span class="ln" data-line="287"><span class="hljs-comment"> */</span></span><span class="ln" data-line="288"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span>... Args></span><span class="ln" data-line="289"><span class="hljs-keyword">class</span> <span class="hljs-title class_">SimpleSlot</span> <span class="hljs-keyword">final</span> : <span class="hljs-keyword">public</span> ISlot&#x3C;Args...> {</span><span class="ln" data-line="290"> <span class="hljs-keyword">private</span>:</span><span class="ln" data-line="291">  std::function&#x3C;<span class="hljs-type">void</span>(Args...)></span><span class="ln" data-line="292">      function_;  <span class="hljs-comment">///&#x3C; function to be invoked by this slot</span></span><span class="ln" data-line="293"></span><span class="ln" data-line="294"> <span class="hljs-keyword">protected</span>:</span><span class="ln" data-line="295">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="296"><span class="hljs-comment">   * @copydoc ISlot::invoke()</span></span><span class="ln" data-line="297"><span class="hljs-comment">   */</span></span><span class="ln" data-line="298">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">invoke</span><span class="hljs-params">(Args... args)</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">final</span> </span>{</span><span class="ln" data-line="299">    <span class="hljs-built_in">function_</span>(std::forward&#x3C;Args>(args)...);</span><span class="ln" data-line="300">  }</span><span class="ln" data-line="301"></span><span class="ln" data-line="302"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="303">  <span class="hljs-built_in">SimpleSlot</span>() = <span class="hljs-keyword">delete</span>;  <span class="hljs-comment">// not needed but better for compilation error</span></span><span class="ln" data-line="304"></span><span class="ln" data-line="305">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="306"><span class="hljs-comment">   * @brief Primary constructor</span></span><span class="ln" data-line="307"><span class="hljs-comment">   * @param id the slot identifier</span></span><span class="ln" data-line="308"><span class="hljs-comment">   * @param function to be invoked by this slot</span></span><span class="ln" data-line="309"><span class="hljs-comment">   * @param signal the reference to the signal this slot is connected to</span></span><span class="ln" data-line="310"><span class="hljs-comment">   * @see ISlot</span></span><span class="ln" data-line="311"><span class="hljs-comment">   */</span></span><span class="ln" data-line="312">  <span class="hljs-built_in">SimpleSlot</span>(<span class="hljs-type">const</span> IDType id, std::function&#x3C;<span class="hljs-built_in">void</span>(Args...)> &#x26;&#x26;function,</span><span class="ln" data-line="313">             ISignal &#x26;signal) <span class="hljs-keyword">noexcept</span></span><span class="ln" data-line="314">      : <span class="hljs-built_in">ISlot</span>&#x3C;Args...>(id, signal),</span><span class="ln" data-line="315">        <span class="hljs-built_in">function_</span>(std::forward&#x3C;std::function&#x3C;<span class="hljs-built_in">void</span>(Args...)>>(function)) {}</span><span class="ln" data-line="316">};</span><span class="ln" data-line="317"></span><span class="ln" data-line="318"><span class="hljs-comment">/**</span></span><span class="ln" data-line="319"><span class="hljs-comment"> * @brief Class TrackingSlot</span></span><span class="ln" data-line="320"><span class="hljs-comment"> * Tracking slot that tracks an object with a std::weak_ptr, it is auto</span></span><span class="ln" data-line="321"><span class="hljs-comment"> * disconnected upon expiration of the std::weak_ptr thus no need to take care</span></span><span class="ln" data-line="322"><span class="hljs-comment"> * of complex object lifetime managment.</span></span><span class="ln" data-line="323"><span class="hljs-comment"> * @tparam T the type of the object to track</span></span><span class="ln" data-line="324"><span class="hljs-comment"> * @tparam Args... the argument types of the function to call</span></span><span class="ln" data-line="325"><span class="hljs-comment"> */</span></span><span class="ln" data-line="326"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> T, <span class="hljs-keyword">typename</span>... Args></span><span class="ln" data-line="327"><span class="hljs-keyword">class</span> <span class="hljs-title class_">TrackingSlot</span> <span class="hljs-keyword">final</span> : <span class="hljs-keyword">public</span> ISlot&#x3C;Args...> {</span><span class="ln" data-line="328"> <span class="hljs-keyword">private</span>:</span><span class="ln" data-line="329">  std::function&#x3C;<span class="hljs-type">void</span>(Args...)></span><span class="ln" data-line="330">      function_;  <span class="hljs-comment">///&#x3C; function to be invoked by this slot</span></span><span class="ln" data-line="331">  std::weak_ptr&#x3C;T></span><span class="ln" data-line="332">      objectPtr_;  <span class="hljs-comment">///&#x3C; the pointer to the object this slot is tracking</span></span><span class="ln" data-line="333"></span><span class="ln" data-line="334"> <span class="hljs-keyword">protected</span>:</span><span class="ln" data-line="335">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="336"><span class="hljs-comment">   * @copydoc ISlot::invoke()</span></span><span class="ln" data-line="337"><span class="hljs-comment">   */</span></span><span class="ln" data-line="338">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">invoke</span><span class="hljs-params">(Args... args)</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">final</span> </span>{</span><span class="ln" data-line="339">    <span class="hljs-comment">// this is probably useless in signle thread</span></span><span class="ln" data-line="340">    <span class="hljs-keyword">auto</span> sp = objectPtr_.<span class="hljs-built_in">lock</span>();</span><span class="ln" data-line="341">    <span class="hljs-keyword">if</span> (!sp) {</span><span class="ln" data-line="342">      ISlotConnection::<span class="hljs-built_in">disconnect</span>();</span><span class="ln" data-line="343">      <span class="hljs-keyword">return</span>;</span><span class="ln" data-line="344">    }</span><span class="ln" data-line="345">    <span class="hljs-keyword">if</span> (ISlotConnection::<span class="hljs-built_in">isConnected</span>()) {</span><span class="ln" data-line="346">      <span class="hljs-built_in">function_</span>(args...);</span><span class="ln" data-line="347">    }</span><span class="ln" data-line="348">  }</span><span class="ln" data-line="349"></span><span class="ln" data-line="350"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="351">  <span class="hljs-built_in">TrackingSlot</span>() = <span class="hljs-keyword">delete</span>;  <span class="hljs-comment">// not needed but better for compilation error</span></span><span class="ln" data-line="352"></span><span class="ln" data-line="353">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="354"><span class="hljs-comment">   * @brief Primary constructor</span></span><span class="ln" data-line="355"><span class="hljs-comment">   * @param id the slot identifier</span></span><span class="ln" data-line="356"><span class="hljs-comment">   * @param objectPtr the pointer to the object to track</span></span><span class="ln" data-line="357"><span class="hljs-comment">   * @param function to be invoked by this slot</span></span><span class="ln" data-line="358"><span class="hljs-comment">   * @param signal the reference to the signal this slot is connected to</span></span><span class="ln" data-line="359"><span class="hljs-comment">   * @see ISlot</span></span><span class="ln" data-line="360"><span class="hljs-comment">   */</span></span><span class="ln" data-line="361">  <span class="hljs-built_in">TrackingSlot</span>(<span class="hljs-type">const</span> IDType id, std::weak_ptr&#x3C;T> &#x26;&#x26;objectPtr,</span><span class="ln" data-line="362">               std::function&#x3C;<span class="hljs-built_in">void</span>(Args...)> &#x26;&#x26;function,</span><span class="ln" data-line="363">               ISignal &#x26;signal) <span class="hljs-keyword">noexcept</span></span><span class="ln" data-line="364">      : <span class="hljs-built_in">ISlot</span>&#x3C;Args...>(id, signal),</span><span class="ln" data-line="365">        <span class="hljs-built_in">function_</span>(std::forward&#x3C;std::function&#x3C;<span class="hljs-built_in">void</span>(Args...)>>(function)),</span><span class="ln" data-line="366">        <span class="hljs-built_in">objectPtr_</span>(std::forward&#x3C;std::weak_ptr&#x3C;T>>(objectPtr)) {}</span><span class="ln" data-line="367"></span><span class="ln" data-line="368"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="369">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="370"><span class="hljs-comment">   * @brief Checks if the connection still there</span></span><span class="ln" data-line="371"><span class="hljs-comment">   */</span></span><span class="ln" data-line="372">  <span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isConnected</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">final</span> </span>{</span><span class="ln" data-line="373">    <span class="hljs-keyword">return</span> !objectPtr_.<span class="hljs-built_in">expired</span>() &#x26;&#x26; ISlotConnection::<span class="hljs-built_in">isConnected</span>();</span><span class="ln" data-line="374">  }</span><span class="ln" data-line="375">};</span><span class="ln" data-line="376"></span><span class="ln" data-line="377"><span class="hljs-comment">/**</span></span><span class="ln" data-line="378"><span class="hljs-comment"> * @brief Class Signal</span></span><span class="ln" data-line="379"><span class="hljs-comment"> * This class manages a collection of ISlots</span></span><span class="ln" data-line="380"><span class="hljs-comment"> * @tparam R the return type of the function to call</span></span><span class="ln" data-line="381"><span class="hljs-comment"> * @tparam Args... the argument types of the function to call</span></span><span class="ln" data-line="382"><span class="hljs-comment"> */</span></span><span class="ln" data-line="383"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> R, <span class="hljs-keyword">typename</span>... Args></span><span class="ln" data-line="384"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Signal</span> <span class="hljs-keyword">final</span> : <span class="hljs-keyword">public</span> ISignal {</span><span class="ln" data-line="385"> <span class="hljs-keyword">private</span>:</span><span class="ln" data-line="386">  std::vector&#x3C;std::shared_ptr&#x3C;ISlot&#x3C;Args...>>></span><span class="ln" data-line="387">      slots_;  <span class="hljs-comment">///&#x3C; slots connected to this Signal</span></span><span class="ln" data-line="388"></span><span class="ln" data-line="389">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="390"><span class="hljs-comment">   * @brief Disconnects the specified slot</span></span><span class="ln" data-line="391"><span class="hljs-comment">   * @param slot the pointer to the slot to disconnect from</span></span><span class="ln" data-line="392"><span class="hljs-comment">   * @post slots_.size() decremented by one</span></span><span class="ln" data-line="393"><span class="hljs-comment">   */</span></span><span class="ln" data-line="394">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">disconnect</span><span class="hljs-params">(ISlotConnection *<span class="hljs-type">const</span> slot)</span> <span class="hljs-keyword">noexcept</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">final</span> </span>{</span><span class="ln" data-line="395">    IDType index = <span class="hljs-built_in">idOf</span>(slot);</span><span class="ln" data-line="396">    <span class="hljs-keyword">if</span> (!slots_.<span class="hljs-built_in">empty</span>()) {</span><span class="ln" data-line="397">      std::<span class="hljs-built_in">swap</span>(slots_[index], slots_.<span class="hljs-built_in">back</span>());</span><span class="ln" data-line="398">      <span class="hljs-built_in">idOf</span>(slots_[index].<span class="hljs-built_in">get</span>()) = index;</span><span class="ln" data-line="399">      slots_.<span class="hljs-built_in">pop_back</span>();</span><span class="ln" data-line="400">    }</span><span class="ln" data-line="401">  }</span><span class="ln" data-line="402"></span><span class="ln" data-line="403"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="404">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="405"><span class="hljs-comment">   * @brief Default constructor</span></span><span class="ln" data-line="406"><span class="hljs-comment">   * @post slots_.size() == 0</span></span><span class="ln" data-line="407"><span class="hljs-comment">   */</span></span><span class="ln" data-line="408">  <span class="hljs-built_in">Signal</span>() <span class="hljs-keyword">noexcept</span> : <span class="hljs-built_in">slots_</span>() {}</span><span class="ln" data-line="409"></span><span class="ln" data-line="410">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="411"><span class="hljs-comment">   * @brief Destructor</span></span><span class="ln" data-line="412"><span class="hljs-comment">   */</span></span><span class="ln" data-line="413">  <span class="hljs-keyword">virtual</span> ~<span class="hljs-built_in">Signal</span>() <span class="hljs-keyword">noexcept</span> { <span class="hljs-built_in">disconnectAll</span>(); }</span><span class="ln" data-line="414"></span><span class="ln" data-line="415">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="416"><span class="hljs-comment">   * @brief Move constructor</span></span><span class="ln" data-line="417"><span class="hljs-comment">   * @see Signal::operator=()</span></span><span class="ln" data-line="418"><span class="hljs-comment">   */</span></span><span class="ln" data-line="419">  <span class="hljs-built_in">Signal</span>(Signal &#x26;&#x26;other) <span class="hljs-keyword">noexcept</span> : <span class="hljs-built_in">slots_</span>() { *<span class="hljs-keyword">this</span> = std::<span class="hljs-built_in">move</span>(other); }</span><span class="ln" data-line="420"></span><span class="ln" data-line="421">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="422"><span class="hljs-comment">   * @brief Move assignment operator</span></span><span class="ln" data-line="423"><span class="hljs-comment">   * @return a reference to this</span></span><span class="ln" data-line="424"><span class="hljs-comment">   * @post slots_ == other.slots_</span></span><span class="ln" data-line="425"><span class="hljs-comment">   */</span></span><span class="ln" data-line="426">  Signal &#x26;<span class="hljs-keyword">operator</span>=(Signal &#x26;&#x26;other) <span class="hljs-keyword">noexcept</span> {</span><span class="ln" data-line="427">    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span> != &#x26;other) {</span><span class="ln" data-line="428">      slots_ = std::<span class="hljs-built_in">move</span>(other.slots_);</span><span class="ln" data-line="429">    }</span><span class="ln" data-line="430">    <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;</span><span class="ln" data-line="431">  }</span><span class="ln" data-line="432"></span><span class="ln" data-line="433">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="434"><span class="hljs-comment">   * @brief Connects a slot to the signal</span></span><span class="ln" data-line="435"><span class="hljs-comment">   * Connects a std::function to the signal.</span></span><span class="ln" data-line="436"><span class="hljs-comment">   * @param function the signal connects to</span></span><span class="ln" data-line="437"><span class="hljs-comment">   * @return a Connection instance for managment purposes</span></span><span class="ln" data-line="438"><span class="hljs-comment">   * @post slots_.size() incremented by one</span></span><span class="ln" data-line="439"><span class="hljs-comment">   */</span></span><span class="ln" data-line="440">  <span class="hljs-function">Connection <span class="hljs-title">connect</span><span class="hljs-params">(std::function&#x3C;R(Args...)> &#x26;&#x26;function)</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="441">    IDType id = slots_.<span class="hljs-built_in">size</span>();</span><span class="ln" data-line="442">    std::shared_ptr&#x3C;ISlot&#x3C;Args...>> slot =</span><span class="ln" data-line="443">        std::make_shared&#x3C;SimpleSlot&#x3C;Args...>>(</span><span class="ln" data-line="444">            id, std::forward&#x3C;std::function&#x3C;<span class="hljs-built_in">R</span>(Args...)>>(function), *<span class="hljs-keyword">this</span>);</span><span class="ln" data-line="445">    slots_.<span class="hljs-built_in">push_back</span>(slot);</span><span class="ln" data-line="446">    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Connection</span>(std::<span class="hljs-built_in">weak_ptr</span>(slots_[id]));</span><span class="ln" data-line="447">  }</span><span class="ln" data-line="448"></span><span class="ln" data-line="449">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="450"><span class="hljs-comment">   * @brief Connects and tracks a slot to the signal</span></span><span class="ln" data-line="451"><span class="hljs-comment">   * Connects a std::function to the signal but this time the slot is tracked</span></span><span class="ln" data-line="452"><span class="hljs-comment">   * by a std::weak_ptr pointing to the object of type T. The purpose is to</span></span><span class="ln" data-line="453"><span class="hljs-comment">   * disconnect this slot automatically upon said object destruction.</span></span><span class="ln" data-line="454"><span class="hljs-comment">   * @param function the signal connects to</span></span><span class="ln" data-line="455"><span class="hljs-comment">   * @param objectPtr pointer to the object to be tracked</span></span><span class="ln" data-line="456"><span class="hljs-comment">   * @tparam T the type of the object to track</span></span><span class="ln" data-line="457"><span class="hljs-comment">   * @return a Connection instance for managment purposes</span></span><span class="ln" data-line="458"><span class="hljs-comment">   * @post slots_.size() incremented by one</span></span><span class="ln" data-line="459"><span class="hljs-comment">   */</span></span><span class="ln" data-line="460">  <span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> T></span><span class="ln" data-line="461">  <span class="hljs-function">Connection <span class="hljs-title">connect</span><span class="hljs-params">(std::weak_ptr&#x3C;T> &#x26;&#x26;objectPtr,</span></span></span><span class="ln" data-line="462"><span class="hljs-function"><span class="hljs-params">                     std::function&#x3C;R(Args...)> &#x26;&#x26;function)</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="463">    IDType id = slots_.<span class="hljs-built_in">size</span>();</span><span class="ln" data-line="464">    std::shared_ptr&#x3C;ISlot&#x3C;Args...>> slot =</span><span class="ln" data-line="465">        std::make_shared&#x3C;TrackingSlot&#x3C;T, Args...>>(</span><span class="ln" data-line="466">            id, std::forward&#x3C;std::weak_ptr&#x3C;T>>(objectPtr),</span><span class="ln" data-line="467">            std::forward&#x3C;std::function&#x3C;<span class="hljs-built_in">R</span>(Args...)>>(function), *<span class="hljs-keyword">this</span>);</span><span class="ln" data-line="468">    slots_.<span class="hljs-built_in">push_back</span>(slot);</span><span class="ln" data-line="469">    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Connection</span>(std::<span class="hljs-built_in">weak_ptr</span>(slots_[id]));</span><span class="ln" data-line="470">  }</span><span class="ln" data-line="471"></span><span class="ln" data-line="472">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="473"><span class="hljs-comment">   * @brief Connects and tracks a slot to the signal</span></span><span class="ln" data-line="474"><span class="hljs-comment">   * Convenience method to connect a member function.</span></span><span class="ln" data-line="475"><span class="hljs-comment">   * Connects a function pointer to the signal.</span></span><span class="ln" data-line="476"><span class="hljs-comment">   * The slot is tracked by a std::weak_ptr pointing to the object of type T.</span></span><span class="ln" data-line="477"><span class="hljs-comment">   * The purpose is to disconnect this slot automatically upon said object</span></span><span class="ln" data-line="478"><span class="hljs-comment">   * destruction.</span></span><span class="ln" data-line="479"><span class="hljs-comment">   * @param function pointer the signal connects to</span></span><span class="ln" data-line="480"><span class="hljs-comment">   * @param objectPtr pointer to the object to be tracked</span></span><span class="ln" data-line="481"><span class="hljs-comment">   * @tparam T the type of the object to track</span></span><span class="ln" data-line="482"><span class="hljs-comment">   * @return a Connection instance for managment purposes</span></span><span class="ln" data-line="483"><span class="hljs-comment">   * @post slots_.size() incremented by one</span></span><span class="ln" data-line="484"><span class="hljs-comment">   */</span></span><span class="ln" data-line="485">  <span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> T></span><span class="ln" data-line="486">  <span class="hljs-function">Connection <span class="hljs-title">connect</span><span class="hljs-params">(std::shared_ptr&#x3C;T> &#x26;objectPtr,</span></span></span><span class="ln" data-line="487"><span class="hljs-function"><span class="hljs-params">                     R (T::*function)(Args...))</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="488">    T *<span class="hljs-type">const</span> ptr = objectPtr.<span class="hljs-built_in">get</span>();</span><span class="ln" data-line="489">    <span class="hljs-keyword">return</span> <span class="hljs-built_in">connect</span>(std::<span class="hljs-built_in">weak_ptr</span>&#x3C;T>(objectPtr),</span><span class="ln" data-line="490">                   [ptr, function](Args... args) -> R {</span><span class="ln" data-line="491">                     <span class="hljs-built_in">return</span> (ptr->*function)(args...);</span><span class="ln" data-line="492">                   });</span><span class="ln" data-line="493">  }</span><span class="ln" data-line="494"></span><span class="ln" data-line="495">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="496"><span class="hljs-comment">   * @brief Connects and tracks a slot to the signal</span></span><span class="ln" data-line="497"><span class="hljs-comment">   * Convenience method to connect a const member function.</span></span><span class="ln" data-line="498"><span class="hljs-comment">   * Connects a function pointer to the signal.</span></span><span class="ln" data-line="499"><span class="hljs-comment">   * The slot is tracked by a std::weak_ptr pointing to the object of type T.</span></span><span class="ln" data-line="500"><span class="hljs-comment">   * The purpose is to disconnect this slot automatically upon said object</span></span><span class="ln" data-line="501"><span class="hljs-comment">   * destruction.</span></span><span class="ln" data-line="502"><span class="hljs-comment">   * @param function pointer the signal connects to</span></span><span class="ln" data-line="503"><span class="hljs-comment">   * @param objectPtr pointer to the object to be tracked</span></span><span class="ln" data-line="504"><span class="hljs-comment">   * @tparam T the type of the object to track</span></span><span class="ln" data-line="505"><span class="hljs-comment">   * @return a Connection instance for managment purposes</span></span><span class="ln" data-line="506"><span class="hljs-comment">   * @post slots_.size() incremented by one</span></span><span class="ln" data-line="507"><span class="hljs-comment">   */</span></span><span class="ln" data-line="508">  <span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> T></span><span class="ln" data-line="509">  <span class="hljs-function">Connection <span class="hljs-title">connect</span><span class="hljs-params">(std::shared_ptr&#x3C;T> &#x26;objectPtr,</span></span></span><span class="ln" data-line="510"><span class="hljs-function"><span class="hljs-params">                     R (T::*function)(Args...) <span class="hljs-type">const</span>)</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="511">    T *<span class="hljs-type">const</span> ptr = objectPtr.<span class="hljs-built_in">get</span>();</span><span class="ln" data-line="512">    <span class="hljs-keyword">return</span> <span class="hljs-built_in">connect</span>(std::<span class="hljs-built_in">weak_ptr</span>&#x3C;T>(objectPtr),</span><span class="ln" data-line="513">                   [ptr, function](Args... args) -> R {</span><span class="ln" data-line="514">                     <span class="hljs-built_in">return</span> (ptr->*function)(args...);</span><span class="ln" data-line="515">                   });</span><span class="ln" data-line="516">  }</span><span class="ln" data-line="517"></span><span class="ln" data-line="518">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="519"><span class="hljs-comment">   * @brief Disconnects all the slots</span></span><span class="ln" data-line="520"><span class="hljs-comment">   * Disconnects all the slots this signal is connected to.</span></span><span class="ln" data-line="521"><span class="hljs-comment">   * @post slots_.empty() == true</span></span><span class="ln" data-line="522"><span class="hljs-comment">   */</span></span><span class="ln" data-line="523">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">disconnectAll</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span> </span>{ slots_.<span class="hljs-built_in">clear</span>(); }</span><span class="ln" data-line="524"></span><span class="ln" data-line="525">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="526"><span class="hljs-comment">   * @brief Emits the signal</span></span><span class="ln" data-line="527"><span class="hljs-comment">   * All non blocked and connected slot functions will be called</span></span><span class="ln" data-line="528"><span class="hljs-comment">   * with supplied arguments.</span></span><span class="ln" data-line="529"><span class="hljs-comment">   * Important explanation!</span></span><span class="ln" data-line="530"><span class="hljs-comment">   * A template is used here, this is for flexibility and design purposes.</span></span><span class="ln" data-line="531"><span class="hljs-comment">   * To this function can be passed rvalues or lvalues but if we use the Args</span></span><span class="ln" data-line="532"><span class="hljs-comment">   * type it is basically precluding us to emit the signal when it is a</span></span><span class="ln" data-line="533"><span class="hljs-comment">   * different types from Args. Suppose Signal&#x3C;bool, int, double&#x26;> the matched</span></span><span class="ln" data-line="534"><span class="hljs-comment">   * function is std::function&#x3C;bool(int, double&#x26;)>. Now i can't emit something</span></span><span class="ln" data-line="535"><span class="hljs-comment">   * like emit(5, 5.0) because 5.0 is a non const lvalue.</span></span><span class="ln" data-line="536"><span class="hljs-comment">   * @see ISlot::operator()()</span></span><span class="ln" data-line="537"><span class="hljs-comment">   * @param params arguments to emit</span></span><span class="ln" data-line="538"><span class="hljs-comment">   * @tparam ...Params type of the parameter to emit</span></span><span class="ln" data-line="539"><span class="hljs-comment">   */</span></span><span class="ln" data-line="540">  <span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span>... Params></span><span class="ln" data-line="541">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">emit</span><span class="hljs-params">(Params... params)</span> </span>{</span><span class="ln" data-line="542">    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> <span class="hljs-type">const</span> &#x26;it : slots_) {</span><span class="ln" data-line="543">      it-><span class="hljs-built_in">operator</span>()(params...);</span><span class="ln" data-line="544">    }</span><span class="ln" data-line="545">  }</span><span class="ln" data-line="546"></span><span class="ln" data-line="547">  <span class="hljs-comment">/**</span></span><span class="ln" data-line="548"><span class="hljs-comment">   * @brief Gets the number of connected slots</span></span><span class="ln" data-line="549"><span class="hljs-comment">   * @return slots.size()</span></span><span class="ln" data-line="550"><span class="hljs-comment">   */</span></span><span class="ln" data-line="551">  <span class="hljs-function">std::<span class="hljs-type">size_t</span> <span class="hljs-title">connectedSlots</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span> </span>{ <span class="hljs-keyword">return</span> slots_.<span class="hljs-built_in">size</span>(); }</span><span class="ln" data-line="552">};</span><span class="ln" data-line="553"></span><span class="ln" data-line="554">}  <span class="hljs-comment">// namespace sigslotpp</span></span><span class="ln" data-line="555"></span><span class="ln" data-line="556"><span class="hljs-meta">#<span class="hljs-keyword">endif</span>  <span class="hljs-comment">// LIBSIGSLOTPP_INCLUDE_LIBSIGSLOTPP_SIGNAL_HPP_</span></span></span></code></pre>
<h2 id="usage">Usage</h2>
<p>To understand how to use the implemented solution, I'll show you the unit tests I wrote.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-type">const</span> std::string ff = <span class="hljs-string">"free function"</span>;</span><span class="ln" data-line="2"><span class="hljs-type">const</span> std::string mf = <span class="hljs-string">"member function"</span>;</span><span class="ln" data-line="3"><span class="hljs-type">const</span> std::string smf = <span class="hljs-string">"static member function"</span>;</span><span class="ln" data-line="4"><span class="hljs-type">const</span> std::string mo = <span class="hljs-string">"member operator"</span>;</span><span class="ln" data-line="5"><span class="hljs-type">const</span> std::string l = <span class="hljs-string">"lambda"</span>;</span><span class="ln" data-line="6"><span class="hljs-type">const</span> std::string gl = <span class="hljs-string">"generic lambda"</span>;</span><span class="ln" data-line="7"></span><span class="ln" data-line="8"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f</span><span class="hljs-params">()</span> </span>{ fmt::<span class="hljs-built_in">print</span>(<span class="hljs-string">"{}\n"</span>, ff); }</span><span class="ln" data-line="9"></span><span class="ln" data-line="10"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">s</span> {</span><span class="ln" data-line="11">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">m</span><span class="hljs-params">()</span> </span>{ fmt::<span class="hljs-built_in">print</span>(<span class="hljs-string">"{}\n"</span>, mf); }</span><span class="ln" data-line="12">  <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">sm</span><span class="hljs-params">()</span> </span>{ fmt::<span class="hljs-built_in">print</span>(<span class="hljs-string">"{}\n"</span>, smf); }</span><span class="ln" data-line="13">};</span><span class="ln" data-line="14"></span><span class="ln" data-line="15"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">o</span> {</span><span class="ln" data-line="16">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">operator</span><span class="hljs-params">()</span><span class="hljs-params">()</span> </span>{ fmt::<span class="hljs-built_in">print</span>(<span class="hljs-string">"{}\n, mo"</span>); }</span><span class="ln" data-line="17">};</span><span class="ln" data-line="18"></span><span class="ln" data-line="19"><span class="hljs-built_in">TEST_CASE</span>(<span class="hljs-string">"slots can be added and removed from the signal"</span>) {</span><span class="ln" data-line="20">  std::shared_ptr&#x3C;s> d;</span><span class="ln" data-line="21">  <span class="hljs-keyword">auto</span> lambda = []() { fmt::<span class="hljs-built_in">print</span>(<span class="hljs-string">"{}\n"</span>, l); };</span><span class="ln" data-line="22">  <span class="hljs-keyword">auto</span> gen_lambda = [](<span class="hljs-keyword">auto</span> &#x26;&#x26;...a) { fmt::<span class="hljs-built_in">print</span>(<span class="hljs-string">"{}\n"</span>, gl); };</span><span class="ln" data-line="23"></span><span class="ln" data-line="24">  sigslotpp::Signal&#x3C;<span class="hljs-type">void</span>> sig;</span><span class="ln" data-line="25"></span><span class="ln" data-line="26">  <span class="hljs-built_in">SUBCASE</span>(<span class="hljs-string">"connecting slots to signal increases connected slots"</span>) {</span><span class="ln" data-line="27">    <span class="hljs-keyword">auto</span> c1 = sig.<span class="hljs-built_in">connect</span>(f);</span><span class="ln" data-line="28">    sig.<span class="hljs-built_in">connect</span>(d, &#x26;s::m);</span><span class="ln" data-line="29">    sig.<span class="hljs-built_in">connect</span>(&#x26;s::sm);</span><span class="ln" data-line="30">    <span class="hljs-keyword">auto</span> c2 = sig.<span class="hljs-built_in">connect</span>(<span class="hljs-built_in">o</span>());</span><span class="ln" data-line="31">    sig.<span class="hljs-built_in">connect</span>(lambda);</span><span class="ln" data-line="32">    sig.<span class="hljs-built_in">connect</span>(gen_lambda);</span><span class="ln" data-line="33">    <span class="hljs-built_in">CHECK</span>(sig.<span class="hljs-built_in">connectedSlots</span>() == <span class="hljs-number">6</span>);</span><span class="ln" data-line="34">  }</span><span class="ln" data-line="35"></span><span class="ln" data-line="36">  <span class="hljs-built_in">SUBCASE</span>(<span class="hljs-string">"discconnecting all slots from the signal put size to zero"</span>) {</span><span class="ln" data-line="37">    <span class="hljs-keyword">auto</span> c1 = sig.<span class="hljs-built_in">connect</span>(f);</span><span class="ln" data-line="38">    <span class="hljs-keyword">auto</span> c2 = sig.<span class="hljs-built_in">connect</span>(<span class="hljs-built_in">o</span>());</span><span class="ln" data-line="39">    sig.<span class="hljs-built_in">connect</span>(lambda);</span><span class="ln" data-line="40">    sig.<span class="hljs-built_in">connect</span>(gen_lambda);</span><span class="ln" data-line="41">    sig.<span class="hljs-built_in">disconnectAll</span>();</span><span class="ln" data-line="42">    <span class="hljs-built_in">CHECK</span>(sig.<span class="hljs-built_in">connectedSlots</span>() == <span class="hljs-number">0</span>);</span><span class="ln" data-line="43">  }</span><span class="ln" data-line="44"></span><span class="ln" data-line="45">  <span class="hljs-built_in">SUBCASE</span>(<span class="hljs-string">"disconnecting slots from signal decreases connected slots"</span>) {</span><span class="ln" data-line="46">    <span class="hljs-keyword">auto</span> c1 = sig.<span class="hljs-built_in">connect</span>(f);</span><span class="ln" data-line="47">    <span class="hljs-keyword">auto</span> c2 = sig.<span class="hljs-built_in">connect</span>(<span class="hljs-built_in">o</span>());</span><span class="ln" data-line="48">    c<span class="hljs-number">1.</span><span class="hljs-built_in">disconnect</span>();</span><span class="ln" data-line="49">    <span class="hljs-built_in">CHECK</span>(sig.<span class="hljs-built_in">connectedSlots</span>() == <span class="hljs-number">1</span>);</span><span class="ln" data-line="50">  }</span><span class="ln" data-line="51">}</span><span class="ln" data-line="52"></span><span class="ln" data-line="53"><span class="hljs-built_in">TEST_CASE</span>(<span class="hljs-string">"signal can be emitted"</span>) {</span><span class="ln" data-line="54">  <span class="hljs-type">int</span> x{<span class="hljs-number">0</span>};</span><span class="ln" data-line="55">  <span class="hljs-keyword">auto</span> lambda = [&#x26;x](<span class="hljs-type">int</span> value) { x = x + value; };</span><span class="ln" data-line="56"></span><span class="ln" data-line="57">  sigslotpp::Signal&#x3C;<span class="hljs-type">void</span>, <span class="hljs-type">int</span>> sig;</span><span class="ln" data-line="58"></span><span class="ln" data-line="59">  sig.<span class="hljs-built_in">connect</span>(lambda);</span><span class="ln" data-line="60"></span><span class="ln" data-line="61">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">5</span>);</span><span class="ln" data-line="62">  <span class="hljs-built_in">CHECK</span>(x == <span class="hljs-number">5</span>);</span><span class="ln" data-line="63">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">1</span>);</span><span class="ln" data-line="64">  <span class="hljs-built_in">CHECK</span>(x == <span class="hljs-number">6</span>);</span><span class="ln" data-line="65">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">-6</span>);</span><span class="ln" data-line="66">  <span class="hljs-built_in">CHECK</span>(x == <span class="hljs-number">0</span>);</span><span class="ln" data-line="67">}</span><span class="ln" data-line="68"></span><span class="ln" data-line="69"><span class="hljs-type">int</span> sum = <span class="hljs-number">0</span>;</span><span class="ln" data-line="70"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">x</span> {</span><span class="ln" data-line="71">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-type">int</span> i)</span> </span>{ sum += i; }</span><span class="ln" data-line="72">};</span><span class="ln" data-line="73"></span><span class="ln" data-line="74"><span class="hljs-built_in">TEST_CASE</span>(<span class="hljs-string">"signal can be automatically disconnected"</span>) {</span><span class="ln" data-line="75">  <span class="hljs-keyword">auto</span> a = std::<span class="hljs-built_in">make_shared</span>&#x3C;x>();</span><span class="ln" data-line="76">  sigslotpp::Signal&#x3C;<span class="hljs-type">void</span>, <span class="hljs-type">int</span>> sig;</span><span class="ln" data-line="77">  sig.<span class="hljs-built_in">connect</span>(a, &#x26;x::f);</span><span class="ln" data-line="78">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">4</span>);</span><span class="ln" data-line="79">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">3</span>);</span><span class="ln" data-line="80">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">-2</span>);</span><span class="ln" data-line="81">  <span class="hljs-built_in">CHECK</span>(sum == <span class="hljs-number">5</span>);</span><span class="ln" data-line="82">  a.<span class="hljs-built_in">reset</span>();</span><span class="ln" data-line="83">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">9</span>);</span><span class="ln" data-line="84">  <span class="hljs-built_in">CHECK</span>(sum == <span class="hljs-number">5</span>);</span><span class="ln" data-line="85">  <span class="hljs-built_in">CHECK</span>(sig.<span class="hljs-built_in">connectedSlots</span>() == <span class="hljs-number">0</span>);</span><span class="ln" data-line="86">}</span><span class="ln" data-line="87"></span><span class="ln" data-line="88"><span class="hljs-built_in">TEST_CASE</span>(<span class="hljs-string">"signal connection to slot can be blocked"</span>) {</span><span class="ln" data-line="89">  <span class="hljs-type">int</span> x{<span class="hljs-number">0</span>};</span><span class="ln" data-line="90">  <span class="hljs-keyword">auto</span> lambda = [&#x26;x](<span class="hljs-type">int</span> value) { x = x + value; };</span><span class="ln" data-line="91"></span><span class="ln" data-line="92">  sigslotpp::Signal&#x3C;<span class="hljs-type">void</span>, <span class="hljs-type">int</span>> sig;</span><span class="ln" data-line="93"></span><span class="ln" data-line="94">  <span class="hljs-keyword">auto</span> c1 = sig.<span class="hljs-built_in">connect</span>(lambda);</span><span class="ln" data-line="95"></span><span class="ln" data-line="96">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">5</span>);</span><span class="ln" data-line="97">  <span class="hljs-built_in">CHECK</span>(x == <span class="hljs-number">5</span>);</span><span class="ln" data-line="98">  c<span class="hljs-number">1.</span><span class="hljs-built_in">block</span>();</span><span class="ln" data-line="99">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">1</span>);</span><span class="ln" data-line="100">  <span class="hljs-built_in">CHECK</span>(x == <span class="hljs-number">5</span>);</span><span class="ln" data-line="101">  c<span class="hljs-number">1.</span><span class="hljs-built_in">unblock</span>();</span><span class="ln" data-line="102">  sig.<span class="hljs-built_in">emit</span>(<span class="hljs-number">-6</span>);</span><span class="ln" data-line="103">  <span class="hljs-built_in">CHECK</span>(x == <span class="hljs-number">-1</span>);</span><span class="ln" data-line="104">}</span></code></pre>
<h2 id="analysis">Analysis</h2>
<p>The classes involved are:</p>
<ul>
<li>ISlotConnection</li>
<li>ISlot</li>
<li>SimpleSlot</li>
<li>TrackingSlot</li>
<li>ISignal</li>
<li>Signal</li>
<li>Connection</li>
</ul>
<p>There is no need to walk through every class line by line, but two elements deserve a closer explanation: TrackingSlot and Connection.</p>
<h3 id="the-trackingslot-class">The TrackingSlot class</h3>
<p>This class makes it possible, via a <code>std::weak_ptr</code>, to track the in-memory existence of a specific object.
To do that, from a design point of view, it requires the use of smart pointers, and in particular a <code>std::shared_ptr</code> to instantiate the object to be tracked.</p>
<p>When the <code>weak_ptr</code> is no longer able to <code>lock</code>, it means the tracked object has reached the end of its lifetime.</p>
<p>This slot is not removed immediately at the end of the tracked object's lifetime, but on the next <code>emit</code> of the signal it is connected to.</p>
<p>It is important to point out that this class does not interfere with the lifecycle of the tracked object.</p>
<h3 id="the-connection-class">The Connection class</h3>
<p>This class does not follow the RAII pattern.
It serves as an explicit access point to the connection between signal and slot.
Connection holds a reference, a <code>weak_ptr</code>, to the slot created as the result of a <code>signal.connect(...)</code> call.</p>
<p>The class can operate on the following slot features:</p>
<ul>
<li>Disconnection</li>
<li>Blocking/unblocking the reception of a signal emission</li>
<li>Checking the state of the slot</li>
</ul>
<p>It is important to point out that the lifecycle of this class does not affect the lifecycle of the slot.</p>
<h2 id="open-issues">Open issues</h2>
<p>There are some immediate functional gaps, namely:</p>
<ol>
<li>It is not thread-safe.</li>
<li>I can't disconnect directly from the slot (imagine a signal that should only be emitted once).</li>
<li>Method overloading.</li>
<li>Methods with default values.</li>
<li>There is no way to retrieve the slots' return value.</li>
</ol>
<p>I do not address point 1 here, given the complexity of thread safety.</p>
<p>For point 4, I have not found a satisfying solution yet.</p>
<p>Issue 5 only exists for design reasons; I preferred not to implement it because it was unnecessary for the purpose of the Signal class.
In my implementation it is possible to connect to slots with return values, which are then transformed into slots with a void return.
To implement the return value you would need a helper vector in which to store the return of each slot, and to expose methods that let you read from it. Obviously each emit would overwrite that vector.</p>
<h3 id="issue-disconnecting-directly-from-the-slot">Issue: disconnecting directly from the slot</h3>
<p>The <code>Connection</code> class solves this problem directly.
The idea is to add an ExtendedSlot class with a Connection member variable, so the connection can be injected inside the <code>invoke</code> function. There is a trade-off, however: the connected function now needs to accept a Connection parameter.</p>
<p>Here is one possible solution.</p>
<p>We add an ExtendedSlot class.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span>... Args></span><span class="ln" data-line="2"><span class="hljs-keyword">class</span> <span class="hljs-title class_">ExtendedSlot</span> <span class="hljs-keyword">final</span> : <span class="hljs-keyword">public</span> ISlot&#x3C;Args...> {</span><span class="ln" data-line="3"> <span class="hljs-keyword">private</span>:</span><span class="ln" data-line="4">  std::function&#x3C;<span class="hljs-type">void</span>(Args...)></span><span class="ln" data-line="5">      function_;  <span class="hljs-comment">///&#x3C; function to be invoked by this slot</span></span><span class="ln" data-line="6">Connection connection_ <span class="hljs-comment">///&#x3C; connection handle injected into the slot</span></span><span class="ln" data-line="7"></span><span class="ln" data-line="8"> <span class="hljs-keyword">protected</span>:</span><span class="ln" data-line="9"></span><span class="ln" data-line="10">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">invoke</span><span class="hljs-params">(Args... args)</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">final</span> </span>{</span><span class="ln" data-line="11">    <span class="hljs-built_in">function_</span>(connection_, std::forward&#x3C;Args>(args)...);</span><span class="ln" data-line="12">  }</span><span class="ln" data-line="13"></span><span class="ln" data-line="14"> <span class="hljs-keyword">public</span>:</span><span class="ln" data-line="15"></span><span class="ln" data-line="16">  <span class="hljs-built_in">ExtendedSlot</span>(<span class="hljs-type">const</span> IDType id, std::function&#x3C;<span class="hljs-built_in">void</span>(Args...)> &#x26;&#x26;function,</span><span class="ln" data-line="17">             ISignal &#x26;signal) <span class="hljs-keyword">noexcept</span></span><span class="ln" data-line="18">      : <span class="hljs-built_in">ISlot</span>&#x3C;Args...>(id, signal),</span><span class="ln" data-line="19">        <span class="hljs-built_in">function_</span>(std::forward&#x3C;std::function&#x3C;<span class="hljs-built_in">void</span>(Args...)>>(function)) {}</span><span class="ln" data-line="20"></span><span class="ln" data-line="21">  <span class="hljs-comment">// Not ideal, but enough for this implementation.</span></span><span class="ln" data-line="22">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">setConnection</span><span class="hljs-params">(Connection connection)</span> </span>{ connection_ = connection }</span><span class="ln" data-line="23"></span><span class="ln" data-line="24">};</span></code></pre>
<p>We modify Signal and add a <code>connectExtended</code> method.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"> <span class="hljs-function">Connection <span class="hljs-title">connectExtended</span><span class="hljs-params">(std::function&#x3C;R(Args...)> &#x26;&#x26;function)</span> <span class="hljs-keyword">noexcept</span> </span>{</span><span class="ln" data-line="2">  IDType id = slots_.<span class="hljs-built_in">size</span>();</span><span class="ln" data-line="3">  std::shared_ptr&#x3C;ISlot&#x3C;Args...>> slot =</span><span class="ln" data-line="4">      std::make_shared&#x3C;ExtendedSlot&#x3C;Args...>>(</span><span class="ln" data-line="5">          id, std::forward&#x3C;std::function&#x3C;<span class="hljs-built_in">R</span>(Args...)>>(function), *<span class="hljs-keyword">this</span>);</span><span class="ln" data-line="6">  slots_.<span class="hljs-built_in">push_back</span>(slot);</span><span class="ln" data-line="7">  <span class="hljs-keyword">auto</span> c = <span class="hljs-built_in">Connection</span>(std::<span class="hljs-built_in">weak_ptr</span>(slots_[id]));</span><span class="ln" data-line="8">  slots_[id]-><span class="hljs-built_in">setConnection</span>(c);</span><span class="ln" data-line="9">  <span class="hljs-keyword">return</span> c;</span><span class="ln" data-line="10">}</span></code></pre>
<p>Usage would look like this.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{</span><span class="ln" data-line="2">  <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;</span><span class="ln" data-line="3">  sigslot::signal&#x3C;<span class="hljs-type">void</span>> sig;</span><span class="ln" data-line="4"></span><span class="ln" data-line="5">  <span class="hljs-keyword">auto</span> f = [](<span class="hljs-keyword">auto</span> &#x26;con) {</span><span class="ln" data-line="6">    i += <span class="hljs-number">1</span>;</span><span class="ln" data-line="7">    con.<span class="hljs-built_in">disconnect</span>();</span><span class="ln" data-line="8">  };</span><span class="ln" data-line="9"></span><span class="ln" data-line="10">  sig.<span class="hljs-built_in">connectExtended</span>(f);</span><span class="ln" data-line="11">}</span></code></pre>
<p>The limitation is that each slot now needs that Connection parameter, and it has to be the first argument.</p>
<h3 id="issue-overloading">Issue: overloading</h3>
<p>If we add the following support functions, we can solve this one too.</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> T, <span class="hljs-keyword">typename</span> R, <span class="hljs-keyword">typename</span>... Args></span><span class="ln" data-line="2"><span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> <span class="hljs-title">overload</span><span class="hljs-params">(R(I::*ptr)(Args...))</span> </span>{</span><span class="ln" data-line="3">    <span class="hljs-keyword">return</span> ptr;</span><span class="ln" data-line="4">}</span><span class="ln" data-line="5"></span><span class="ln" data-line="6"><span class="hljs-keyword">template</span> &#x3C;<span class="hljs-keyword">typename</span> R, <span class="hljs-keyword">typename</span>... Args></span><span class="ln" data-line="7"><span class="hljs-function"><span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> <span class="hljs-title">overload</span><span class="hljs-params">(R(*ptr)(Args...))</span> </span>{</span><span class="ln" data-line="8">    <span class="hljs-keyword">return</span> ptr;</span><span class="ln" data-line="9">}</span></code></pre>
<p>Once expanded, the parameter pack makes the overload unambiguous:</p>
<pre class="has-lang" data-lang="cpp"><code class="hljs language-cpp"><span class="ln" data-line="1"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">obj</span> {</span><span class="ln" data-line="2">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">operator</span><span class="hljs-params">()</span><span class="hljs-params">(<span class="hljs-type">int</span>)</span> <span class="hljs-type">const</span> </span>{}</span><span class="ln" data-line="3">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">operator</span><span class="hljs-params">()</span><span class="hljs-params">()</span> </span>{}</span><span class="ln" data-line="4">};</span><span class="ln" data-line="5"></span><span class="ln" data-line="6"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">foo</span> {</span><span class="ln" data-line="7">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">bar</span><span class="hljs-params">(<span class="hljs-type">int</span>)</span> </span>{}</span><span class="ln" data-line="8">  <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">bar</span><span class="hljs-params">()</span> </span>{}</span><span class="ln" data-line="9"></span><span class="ln" data-line="10">  <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">baz</span><span class="hljs-params">(<span class="hljs-type">int</span>)</span> </span>{}</span><span class="ln" data-line="11">  <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">baz</span><span class="hljs-params">()</span> </span>{}</span><span class="ln" data-line="12">};</span><span class="ln" data-line="13"></span><span class="ln" data-line="14"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">moo</span><span class="hljs-params">(<span class="hljs-type">int</span>)</span> </span>{}</span><span class="ln" data-line="15"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">moo</span><span class="hljs-params">()</span> </span>{}</span><span class="ln" data-line="16"></span><span class="ln" data-line="17"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{</span><span class="ln" data-line="18">  sigslot::signal&#x3C;<span class="hljs-type">void</span>, <span class="hljs-type">int</span>> sig;</span><span class="ln" data-line="19"></span><span class="ln" data-line="20">  foo ff;</span><span class="ln" data-line="21">  sig.<span class="hljs-built_in">connect</span>(<span class="hljs-built_in">overload</span>&#x3C;<span class="hljs-type">int</span>>(&#x26;foo::bar), &#x26;ff);</span><span class="ln" data-line="22">  sig.<span class="hljs-built_in">connect</span>(<span class="hljs-built_in">overload</span>&#x3C;<span class="hljs-type">int</span>>(&#x26;foo::baz));</span><span class="ln" data-line="23">  sig.<span class="hljs-built_in">connect</span>(<span class="hljs-built_in">overload</span>&#x3C;<span class="hljs-type">int</span>>(&#x26;moo));</span><span class="ln" data-line="24">  sig.<span class="hljs-built_in">connect</span>(<span class="hljs-built_in">obj</span>());</span><span class="ln" data-line="25"></span><span class="ln" data-line="26">  <span class="hljs-built_in">sig</span>(<span class="hljs-number">0</span>);</span><span class="ln" data-line="27"></span><span class="ln" data-line="28">  <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span><span class="ln" data-line="29">}</span></code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>We've seen how to implement a signal/slot mechanism.
The solution has clear limitations, but it provides a simple interface without hidden mechanisms.</p>
<p>With this foundation we can implement the observer pattern in a clear and controlled way.</p>
<p>At this <a href="https://github.com/signorenne/cpp_playground/tree/main/signals_and_slots">link</a> you can find the project folder, which you can compile with CMake.</p>]]></content:encoded>
    </item>
    <item>
      <title>How to structure a C++ project</title>
      <link>https://nexenne.com/blog/cpp_project_structure/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/cpp_project_structure/</guid>
      <pubDate>Thu, 28 Mar 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[A tidy C++ project structure with separate libraries and applications, and a build that is easier to follow.]]></description>
      <category>C++</category>
      <category>CMake</category>
      <category>Conan</category>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Sooner or later, every C++ project has to deal with CMake, dependencies, internal libraries, and applications built on top of those libraries.
In my first projects I often spent too much time organizing directories and keeping every <code>CMakeLists.txt</code> file consistent.
When the structure is unclear, adding a module or dependency can make the build more fragile than it needs to be.</p>
<p>After studying how other developers organize their projects, I found a structure I like for its simplicity and clarity.</p>
<p>What follows is opinionated. The structure is designed to:</p>
<ul>
<li>Avoid patterns that cause conflicts.</li>
<li>Avoid making the build more complicated than it needs to be.</li>
<li>Make the project easier to read.</li>
</ul>
<h2 id="tools-used">Tools used</h2>
<p>For this example I will use three tools.</p>
<h3 id="cmake">CMake</h3>
<blockquote>
<p>CMake is a free, cross-platform software for build automation whose name is short for cross platform make. This software was born to replace Automake in the generation of Makefiles, trying to be simpler to use. In fact, on most projects there is no Makefile included in the sources, since that would not be portable.</p>
<p>-- Wikipedia</p>
</blockquote>
<p>We will use it to generate the build system and compile the project.</p>
<p>This is not a complete CMake guide; the focus here is project layout. For deeper documentation, start from the <a href="https://cmake.org/">official CMake website</a>.</p>
<h3 id="conan">Conan</h3>
<blockquote>
<p>Conan is a dependency and package manager for C and C++ languages. It is free and open-source, works in all platforms ( Windows, Linux, OSX, FreeBSD, Solaris, etc.), and can be used to develop for all targets including embedded, mobile (iOS, Android), and bare metal. It also integrates with all build systems like CMake, Visual Studio (MSBuild), Makefiles, SCons, etc., including proprietary ones.</p>
<p>-- Conan</p>
</blockquote>
<p>We will use it for dependency and package management.</p>
<p>For more details, see the <a href="https://conan.io/">official Conan website</a>.</p>
<p>Before moving on, here are the minimum Conan steps used in this project.</p>
<p>Start by generating a Conan profile, which describes the compiler, build configuration, architecture, and other environment settings.</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1">conan profile detect --force</span></code></pre>
<p>When the command finishes, if you are on Unix, you'll find a <code>.conan2</code> folder in your home directory containing the files mentioned above.</p>
<p>To install dependencies, run:</p>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1">conan install . --output-folder=build --build=missing</span></code></pre>
<p>Conan then performs two main operations:</p>
<ul>
<li>It installs the libraries specified in the <code>conanfile.txt</code> from a remote server, usually Conan Center, if they are available.
This server stores both Conan recipes, which define how the libraries should be built, and binaries that can be reused so you don't have to recompile them every time.</li>
<li>It generates several files in the build directory.
<ul>
<li>CMakeDeps generates the files needed to let CMake find the libraries we downloaded.</li>
<li>CMakeToolchain generates a CMake toolchain file so we can build our project with CMake.</li>
</ul>
</li>
</ul>
<h3 id="doxygen">Doxygen</h3>
<blockquote>
<p>Doxygen is an application for the automatic generation of documentation starting from the source code of a generic software. It is an open-source project available under the GPL license, written mostly by Dimitri van Heesch starting in 1997.</p>
<p>-- Wikipedia</p>
</blockquote>
<p>Doxygen generates documentation from comments in the source code.
As with the previous tools, this article will not cover it in depth. For details, see the <a href="https://www.doxygen.nl/">official Doxygen website</a>.</p>
<h2 id="structure">Structure</h2>
<p>The project structure looks like this.</p>
<pre class="has-lang" data-lang="txt"><code class="hljs language-txt"><span class="ln" data-line="1">.</span><span class="ln" data-line="2">├── CMakeLists.txt</span><span class="ln" data-line="3">├── conanfile.txt</span><span class="ln" data-line="4">├── conan_provider.cmake</span><span class="ln" data-line="5">├── libfoo</span><span class="ln" data-line="6">│   ├── CMakeLists.txt</span><span class="ln" data-line="7">│   ├── docs</span><span class="ln" data-line="8">│   │   └── CMakeLists.txt</span><span class="ln" data-line="9">│   ├── include</span><span class="ln" data-line="10">│   │   └── libfoo</span><span class="ln" data-line="11">│   │       └── foo.hpp</span><span class="ln" data-line="12">│   ├── src</span><span class="ln" data-line="13">│   │   └── foo.cpp</span><span class="ln" data-line="14">│   └── tests</span><span class="ln" data-line="15">│       ├── foo.test.cpp</span><span class="ln" data-line="16">│       └── main.cpp</span><span class="ln" data-line="17">└── standalone</span><span class="ln" data-line="18">    ├── CMakeLists.txt</span><span class="ln" data-line="19">    └── main.cpp</span></code></pre>
<p>Let's go through it more carefully.</p>
<p>The underlying idea is to separate project components by directory. Each directory contains either an executable or a library and defines its own target.</p>
<p><code>standalone</code> is the executable target. In this example it uses the <code>libfoo</code> library and represents an application built on top of one or more core libraries.</p>
<p><code>libfoo</code> is a static library used by <code>standalone</code>, but it is organized as a self-contained component with a public interface, implementation, tests, and documentation.
Each library has to:</p>
<ul>
<li>Follow a predictable structure.
<ul>
<li>An <code>include</code> directory for public declarations, which form the library interface.</li>
<li>A <code>src</code> directory for definitions and private headers.</li>
</ul>
</li>
<li>Provide documentation generation through Doxygen.</li>
<li>Provide a testing environment with doctest (or similar).</li>
</ul>
<p>The image below shows the idea: <code>app</code> executables use <code>core</code> libraries, while each component stays separate.</p>
<p><img src="/blog/images/cpp_project_structure_example_1.png" alt=""></p>
<h3 id="top-level-cmakeliststxt">Top-level CMakeLists.txt</h3>
<p>The CMakeLists.txt at the root contains the top-level configuration of our project. Its contents are below.</p>
<pre class="has-lang" data-lang="txt"><code class="hljs language-txt"><span class="ln" data-line="1">cmake_minimum_required(VERSION 3.27)</span><span class="ln" data-line="2"></span><span class="ln" data-line="3">### Project</span><span class="ln" data-line="4">project(cpp_project_structure VERSION 1.0 LANGUAGES CXX)</span><span class="ln" data-line="5">set(CMAKE_CXX_STANDARD 17)</span><span class="ln" data-line="6">set(CMAKE_EXPORT_COMPILE_COMMANDS ON)</span><span class="ln" data-line="7"></span><span class="ln" data-line="8">### Packages</span><span class="ln" data-line="9">find_package(fmt REQUIRED)</span><span class="ln" data-line="10">find_package(doctest REQUIRED)</span><span class="ln" data-line="11">find_package(Doxygen REQUIRED)</span><span class="ln" data-line="12"></span><span class="ln" data-line="13">### Subdirectories (the order is important)</span><span class="ln" data-line="14">add_subdirectory(libfoo)</span><span class="ln" data-line="15">add_subdirectory(standalone)</span><span class="ln" data-line="16"></span></code></pre>
<p>We have a single project and we tell CMake to look for other configuration files in the libfoo and standalone subfolders, respectively "core" and "app".
We then specify a few simple variables and issue <code>find_package</code> commands to look for the dependencies needed by the build process.</p>
<h3 id="dependency-management">Dependency management</h3>
<p>The <code>conanfile.txt</code> file specifies the packages our project's targets need. In our case <a href="https://github.com/fmtlib/fmt">fmt</a> is used by both libfoo and standalone, and <a href="https://github.com/doctest/doctest">doctest</a> is required by libfoo for unit tests.
Its contents are below.</p>
<pre class="has-lang" data-lang="txt"><code class="hljs language-txt"><span class="ln" data-line="1">[requires]</span><span class="ln" data-line="2">fmt/10.2.1</span><span class="ln" data-line="3">doctest/2.4.11</span><span class="ln" data-line="4"></span><span class="ln" data-line="5">[layout]</span><span class="ln" data-line="6">cmake_layout</span></code></pre>
<p>To use Conan more comfortably, we leverage the <a href="https://github.com/conan-io/cmake-conan">cmake-conan</a> wrapper. We're specifically interested in the <code>conan_provider.cmake</code> file, which we save at the root of the project.
This file will come in handy later.</p>
<h3 id="the-libfoo-target">The libfoo target</h3>
<p>The libfoo target, which in our case is a static library, follows the canonical shape of a standard library.
Going back to the idea of one target per subdirectory, our CMakeLists.txt is the one below.</p>
<pre class="has-lang" data-lang="txt"><code class="hljs language-txt"><span class="ln" data-line="1">### Library libfoo</span><span class="ln" data-line="2">add_library(libfoo STATIC src/foo.cpp</span><span class="ln" data-line="3">        include/libfoo/foo.hpp)</span><span class="ln" data-line="4">add_library(libfoo::libfoo ALIAS libfoo)</span><span class="ln" data-line="5">set_target_properties(libfoo PROPERTIES VERSION 0.0)</span><span class="ln" data-line="6">target_include_directories(libfoo PUBLIC include PRIVATE src)</span><span class="ln" data-line="7">target_link_libraries(libfoo PRIVATE fmt::fmt)</span><span class="ln" data-line="8">target_compile_options(libfoo PRIVATE -Wall -Wextra -pedantic -Werror)</span><span class="ln" data-line="9">target_compile_features(libfoo PRIVATE cxx_std_17)</span><span class="ln" data-line="10"></span><span class="ln" data-line="11">### Testing libfoo</span><span class="ln" data-line="12">add_executable(libfoo_tests tests/main.cpp)</span><span class="ln" data-line="13">target_link_libraries(libfoo_tests PRIVATE doctest::doctest)</span><span class="ln" data-line="14">target_compile_options(libfoo_tests PRIVATE -Wall -Wextra -pedantic -Werror)</span><span class="ln" data-line="15">target_compile_features(libfoo_tests PRIVATE cxx_std_17)</span><span class="ln" data-line="16"></span><span class="ln" data-line="17">### Subdirectories</span><span class="ln" data-line="18">add_subdirectory(docs)</span></code></pre>
<p>There are three sections:</p>
<ul>
<li>The library definition and its configuration.</li>
<li>Setting up and creating the target for the unit tests.</li>
<li>Enabling and configuring Doxygen for documentation generation.</li>
</ul>
<p>For clarity, here is the contents of the CMakeLists.txt in the docs folder.</p>
<pre class="has-lang" data-lang="txt"><code class="hljs language-txt"><span class="ln" data-line="1">set(DOXYGEN_ALPHABETICAL_INDEX NO)</span><span class="ln" data-line="2">set(DOXYGEN_BUILTIN_STL_SUPPORT YES)</span><span class="ln" data-line="3">set(DOXYGEN_CASE_SENSE_NAMES NO)</span><span class="ln" data-line="4">set(DOXYGEN_CLASS_DIAGRAMS NO)</span><span class="ln" data-line="5">set(DOXYGEN_DISTRIBUTE_GROUP_DOC YES)</span><span class="ln" data-line="6"># set(DOXYGEN_EXAMPLE_PATH "")</span><span class="ln" data-line="7">set(DOXYGEN_EXCLUDE bin)</span><span class="ln" data-line="8">set(DOXYGEN_EXTRACT_ALL YES)</span><span class="ln" data-line="9">set(DOXYGEN_EXTRACT_LOCAL_CLASSES NO)</span><span class="ln" data-line="10">set(DOXYGEN_FILE_PATTERNS *.hpp)</span><span class="ln" data-line="11">set(DOXYGEN_GENERATE_TREEVIEW YES)</span><span class="ln" data-line="12">set(DOXYGEN_HIDE_FRIEND_COMPOUNDS YES)</span><span class="ln" data-line="13">set(DOXYGEN_HIDE_IN_BODY_DOCS YES)</span><span class="ln" data-line="14">set(DOXYGEN_HIDE_UNDOC_CLASSES YES)</span><span class="ln" data-line="15">set(DOXYGEN_HIDE_UNDOC_MEMBERS YES)</span><span class="ln" data-line="16">set(DOXYGEN_JAVADOC_AUTOBRIEF YES)</span><span class="ln" data-line="17">set(DOXYGEN_QT_AUTOBRIEF YES)</span><span class="ln" data-line="18">set(DOXYGEN_QUIET YES)</span><span class="ln" data-line="19">set(DOXYGEN_RECURSIVE YES)</span><span class="ln" data-line="20">set(DOXYGEN_REFERENCED_BY_RELATION YES)</span><span class="ln" data-line="21">set(DOXYGEN_REFERENCES_RELATION YES)</span><span class="ln" data-line="22">set(DOXYGEN_SORT_BY_SCOPE_NAME YES)</span><span class="ln" data-line="23">set(DOXYGEN_SORT_MEMBER_DOCS NO)</span><span class="ln" data-line="24">set(DOXYGEN_SOURCE_BROWSER YES)</span><span class="ln" data-line="25">set(DOXYGEN_STRIP_CODE_COMMENTS NO)</span><span class="ln" data-line="26"></span><span class="ln" data-line="27">doxygen_add_docs(</span><span class="ln" data-line="28">        libfoo_docs</span><span class="ln" data-line="29">        "../include/"</span><span class="ln" data-line="30">        ALL</span><span class="ln" data-line="31">        COMMENT "Generate HTML documentation for libfoo"</span><span class="ln" data-line="32">)</span></code></pre>
<h3 id="the-standalone-target">The standalone target</h3>
<p>Unlike the libfoo target, this one is much more trivial. The configuration follows.</p>
<pre class="has-lang" data-lang="txt"><code class="hljs language-txt"><span class="ln" data-line="1">add_executable(standalone main.cpp)</span><span class="ln" data-line="2">target_link_libraries(standalone PRIVATE fmt::fmt libfoo::libfoo)</span><span class="ln" data-line="3">target_compile_options(standalone PRIVATE -Wall -Wextra -pedantic -Werror)</span><span class="ln" data-line="4">target_compile_features(standalone PRIVATE cxx_std_17)</span></code></pre>
<h2 id="building">Building</h2>
<pre class="has-lang" data-lang="bash"><code class="hljs language-bash"><span class="ln" data-line="1">cmake -B build -S . -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=conan_provider.cmake -DCMAKE_BUILD_TYPE=Debug</span><span class="ln" data-line="2">cmake --build build --config Debug</span></code></pre>
<p>With the first command we run the out-of-tree configuration phase, generating a build system.
With the second one we build the project by calling the system build tool, <code>make</code> on Unix.</p>
<p>Remember the <code>conan_provider.cmake</code> file mentioned earlier?
Well, by using the <code>-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=conan_provider.cmake</code> flag in the cmake configuration process, cmake automatically invokes the <code>conan install</code> command, simplifying project management.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Now in the build folder, under the project root, we'll find the binaries and libraries we compiled, plus the Doxygen documentation and the unit-test executable.</p>
<p>This structure can easily be expanded by adding a CI/CD pipeline and more.</p>
<p>I hope this was useful. At this <a href="https://github.com/signorenne/cpp_playground/tree/main/cpp_project_structure">link</a> you can find the repository of the project discussed in this article so you can study it.</p>]]></content:encoded>
    </item>
    <item>
      <title>Prelude</title>
      <link>https://nexenne.com/blog/preludio/</link>
      <guid isPermaLink="true">https://nexenne.com/blog/preludio/</guid>
      <pubDate>Tue, 26 Mar 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[The first note on the site: why I decided to start writing publicly.]]></description>
      <category>Update</category>
      <content:encoded><![CDATA[<p>Welcome to my website.</p>
<p>For a long time I wondered whether I really had anything useful to write. Eventually I reached a simple conclusion: if I never started, I would never find out.</p>
<p>So this is my first public post.</p>
<p>It does not contain a technical deep dive yet, but it marks the beginning of a space where I can collect what I learn, the projects I work on, and the ideas worth developing with more care.</p>
<p>I will mostly write about computer science, software development, and technology, with room for personal notes and other topics I find interesting.</p>
<p>Thanks for stopping by. Enjoy the reading.</p>
<p><em>Nicolò</em></p>]]></content:encoded>
    </item>
  </channel>
</rss>
