<?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 — Appunti dal campo</title>
    <link>https://nexenne.com/it/</link>
    <description>Note tecniche su HMI automotive, firmware embedded, Android e lavoro sul campo.</description>
    <language>it</language>
    <atom:link href="https://nexenne.com/it/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/it/blog/strong_type_cpp/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/strong_type_cpp/</guid>
      <pubDate>Fri, 18 Jul 2025 12:00:00 GMT</pubDate>
      <description><![CDATA[Un'introduzione agli strong type in C++ partendo da un errore facile da commettere.]]></description>
      <category>C++</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Quando usiamo tipi primitivi “nudi” (<code>int</code>, <code>double</code> e simili), il significato dei valori non è esplicito. Il codice diventa più fragile: è facile scambiare due parametri e, in alcuni casi, non è nemmeno possibile rappresentare correttamente l'intento.</p>
<p>Per capire il problema, partiamo da un caso molto comune.</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 questo esempio nulla impedisce di invertire accidentalmente larghezza e altezza: <code>rectangle(800, 600)</code> e <code>rectangle(600, 800)</code> sono entrambi validi per il compilatore, ma uno dei due potrebbe essere semanticamente errato. Il problema emerge soltanto durante l'esecuzione.</p>
<p>Proviamo allora a rendere l’intento più chiaro con nomi diversi.</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>Qui i due costruttori hanno la stessa firma, <code>circle(double)</code>: cambiano soltanto i nomi dei parametri, che non partecipano alla risoluzione dell'overload. Il codice quindi non compila e continuiamo a non avere tipi distinti per rappresentare raggio e diametro.</p>
<p>I tipi primitivi non rappresentano la semantica del dominio. Serve un modo per dare un'identità ai valori e rendere esplicito il significato nel punto di chiamata, riducendo errori ed effetti inattesi.</p>
<p>Per questo introduciamo tipi “forti” e auto-esplicativi: Width, Height, Radius, Diameter, e così via. Nelle sezioni che seguono analizzeremo una libreria header-only per C++23, st::strong_type&#x3C;T, Tag, Ability...>, in cui Tag separa gli “assi semantici”, mentre le Ability abilitano in modalità opt-in solo gli operatori desiderati (aritmetici, di stream, bitwise, saturating). Così il compilatore ci aiuta a esprimere l’intento e a prevenire intere classi di bug.</p>
<h2 id="cos-è-uno-strong-type">Cos’è uno strong type?</h2>
<p>Uno <strong>strong type</strong> è un wrapper di tipo che sostituisce un tipo sottostante per rendere esplicita la semantica <strong>a livello di tipo</strong> (non solo nel nome del parametro).
Con st::strong_type&#x3C;T, Tag, Ability...> “marchiamo” un T con un Tag (phantom type) che lo rende incompatibile con altri valori isomorfi ma semanticamente diversi, e abilitiamo <strong>solo</strong> le operazioni dichiarate in modo <strong>opt-in</strong> tramite le Ability.</p>
<p>Lo <strong>strong type</strong> nel codice:</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: tipo sottostante (es. double, int, std::uint32_t, un enum...).</li>
<li>Tag: tipo vuoto che identifica l’asse semantico (es. width_tag, height_tag).</li>
<li>Ability: <strong>feature mixins</strong> (operatori e utility opt-in). È disponibile anche il gruppo pronto all’uso st::arithmetic.</li>
</ul>
<h2 id="come-funziona">Come funziona?</h2>
<ul>
<li>Tag barrier. Gli operatori binari tra strong type sono ammessi solo se condividono lo stesso <code>Tag</code>. Sommare <code>Width</code> e <code>Height</code>? Errore di compilazione.</li>
<li>Ability gating. Ogni operatore/utility è protetto da un <code>requires</code>: entrambi gli operandi devono dichiarare l’abilità richiesta e l’underlying deve supportare davvero l’operazione (verifica sull’espressione).</li>
<li>Costruttori corretti. Da <code>T</code>: il costruttore è <code>explicit</code> per default; diventa implicito solo quando la conversione a <code>T</code> è sicura (nessun narrowing). Da un altro strong type con lo stesso <code>Tag</code>: consentito se gli underlying sono convertibili; valgono le stesse regole di <code>explicit</code>.</li>
<li>Tipo di risultato disciplinato. Gli operatori binari restituiscono un nuovo strong type con: underlying = <code>std::common_type_t&#x3C;UA, UB></code>; abilità = unione delle abilità dei due operandi, filtrate in base al nuovo underlying (es. lo shift solo su unsigned); stesso <code>Tag</code>.</li>
<li>Shift sicuri. I conteggi sono normalizzati modulo la bit width (con <code>assert</code> sui negativi in debug), evitando UB.</li>
<li>Aritmetica saturante. <code>st::sat_add</code> e <code>st::sat_sub</code> fanno clamp a <code>min</code> e <code>max</code> (percorsi dedicati per signed/unsigned) e restituiscono un tipo coerente con le regole di unione/filtraggio sopra.</li>
<li>Accesso e utilità. <code>x.value()</code> (overload lvalue/rvalue), <code>st::to_underlying(x)</code> (preserva la value category), <code>st::clamp</code>, <code>explicit operator bool()</code> in stile bool-testable, <code>st::static_strong_cast(x)</code> (cast intenzionale tra strong type con lo stesso <code>Tag</code>).</li>
<li>Integrazione STL. Specializzazioni coerenti di <code>std::hash</code>, <code>std::formatter</code> e <code>std::common_type</code> in linea con le regole precedenti.</li>
</ul>
<h2 id="un-esempio">Un esempio</h2>
<p>Questo esempio, volutamente completo, mostra l'utilità di un header file dedicato agli strong type.</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="conclusione">Conclusione</h2>
<p>Gli strong type permettono di costruire API più esplicite, rilevare prima gli errori e controllare quali operazioni siano disponibili per ciascun valore. Alias concreti come <code>using Width = st::strong_type&#x3C;double, width_tag, st::arithmetic>;</code> rendono più chiari sia il punto di chiamata sia i test, perché il compilatore può distinguere valori identici nella rappresentazione ma diversi nel significato.</p>]]></content:encoded>
    </item>
    <item>
      <title>C++20 Ranges and Views</title>
      <link>https://nexenne.com/it/blog/cpp20_ranges_and_views/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/cpp20_ranges_and_views/</guid>
      <pubDate>Wed, 11 Sep 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Un esempio per vedere come ranges e views di C++20 rendono più leggibili gli algoritmi sulle collezioni.]]></description>
      <category>C++</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>In questo articolo vediamo come <code>std::ranges</code> e <code>std::views</code>, introdotti in C++20, rendano più chiara la composizione degli algoritmi sulle collezioni.</p>
<h2 id="modello-computazionale">Modello computazionale</h2>
<h3 id="quesito">Quesito</h3>
<p>Vogliamo scrivere un algoritmo che riceva una collezione di numeri interi e produca, in ordine inverso, soltanto quelli divisibili per 3.</p>
<p>La tabella seguente mostra alcuni esempi di input e 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="soluzione-pre-c20">Soluzione pre C++20</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>Questo piccolo frammento di codice esegue questi passaggi:</p>
<ul>
<li>Crea uno <code>std::vector</code> temporaneo di supporto.</li>
<li>Copia in <code>tmp</code> tutti gli elementi di <code>numbers</code> che soddisfano il predicato <code>isDivisibleByThree</code>.</li>
<li>Inverte la sequenza degli elementi di <code>tmp</code>.</li>
</ul>
<p>La soluzione funziona, ma richiede un contenitore temporaneo e più passaggi separati. Con C++20 possiamo descrivere la stessa trasformazione in modo più diretto.</p>
<h3 id="soluzione-con-stdranges">Soluzione con 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>La differenza è evidente; prima di entrare nello specifico, però, conviene chiarire meglio i concetti di range e view.</p>
<h2 id="ranges">Ranges</h2>
<p>Un range rappresenta una sequenza di elementi, o più in generale qualcosa su cui è possibile iterare.</p>
<p>Per definizione, un range è una coppia di iteratori <code>begin</code> e <code>end</code>: il primo punta all'inizio di una collezione o sequenza, il secondo alla fine.</p>
<p>I contenitori della libreria standard soddisfano questa definizione e possono quindi essere usati come range.</p>
<h3 id="classificazione">Classificazione</h3>
<p>I range possono essere classificati in modi diversi. Una delle distinzioni principali riguarda le capacità degli iteratori.</p>
<p>Avendo affrontato i concepts nel <a href="/blog/cpp20_concepts/" data-sveltekit-reload>post precedente</a>, possiamo riassumere i ranges nella tabella seguente.</p>
<table>
<thead>
<tr>
<th>Concept</th>
<th>Descrizione</th>
</tr>
</thead>
<tbody>
<tr>
<td>std::ranges::input_range</td>
<td>Può essere iterato dall'inizio alla fine <code>almeno una volta</code></td>
</tr>
<tr>
<td>std::ranges::forward_range</td>
<td>Può essere iterato dall'inizio alla fine <code>molteplici volte</code></td>
</tr>
<tr>
<td>std::ranges::bidirectional_range</td>
<td>L'iteratore può eseguire l'operazione <code>--</code> (vai all'elemento precedente)</td>
</tr>
<tr>
<td>std::ranges::random_access_range</td>
<td>Esiste l'operatore <code>[]</code> che permette l'accesso agli elementi in tempo costante</td>
</tr>
<tr>
<td>std::ranges::contiguous_range</td>
<td>Gli elementi sono vincolati ad essere memorizzati contiguamente nella memoria</td>
</tr>
</tbody>
</table>
<p>La classificazione corrisponde ai relativi concept degli iteratori, come <code>std::forward_iterator</code>.</p>
<h2 id="views">Views</h2>
<p>Tre proprietà delle view sono particolarmente importanti:</p>
<ul>
<li>Una view è un range.</li>
<li>Una view non possiede i dati a cui accede.</li>
<li>Una view applica le modifiche solo quando un elemento viene richiesto (lazy-evaluation).</li>
</ul>
<h3 id="una-view-è-un-range">Una view è un range</h3>
<p>Per definizione, una view \(w\) è un range definito su un altro range \(r\).
La view può applicare trasformazioni al range osservato tramite algoritmi e altre operazioni, sfruttando la lazy-evaluation.</p>
<h3 id="una-view-non-possiede-i-dati-a-cui-accede">Una view non possiede i dati a cui accede</h3>
<p>Quando si accede a un elemento attraverso una view, si continua a lavorare sui dati gestiti dal range sottostante.</p>
<p>Questo ha due implicazioni:</p>
<ul>
<li>Le viste sono veloci da creare, perché non hanno bisogno di copiare i dati sottostanti.</li>
<li>Le trasformazioni della view non modificano la struttura del contenitore originale.</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>Come si può notare, la view non ha modificato il range <code>numbers</code>.
È vero però il contrario: modificando il contenitore originale, il cambiamento si ripercuote su tutte le view che usano quel range.</p>
<p>Avremo pertanto</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>Una view applica le trasformazioni quando gli elementi vengono richiesti, non necessariamente nel momento in cui viene creata. Questa valutazione differita evita elaborazioni e copie non necessarie.</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">// la view viene valutata qua</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="composizione-e-pipeline">Composizione e pipeline</h3>
<p>Qualcuno potrebbe chiedersi perché ho scritto</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>Anziché utilizzare</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>Il motivo è che <code>std::views::reverse</code> non è una view, ma un adattatore: prende il range sottostante, in questo caso uno <code>std::vector</code>, e restituisce una view su quello <code>std::vector</code>.
Il tipo esatto della view viene nascosto dietro la keyword <code>auto</code>; in questo modo non dobbiamo preoccuparci di scrivere gli argomenti del template della view.
Un ulteriore vantaggio di questa forma è la possibilità di concatenare più adattatori tramite pipe.</p>
<p>Per esempio anziché utilizzare</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>Possiamo scrivere</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="esempi">Esempi</h2>
<p>Vogliamo creare una view dei primi 5 elementi di un std::vector e stampare il risultato.</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>Vogliamo sfruttare un algoritmo range based per stampare un std::vector invertito e ripulito da tutti i valori negativi.</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="concetti-avanzati">Concetti avanzati</h2>
<h3 id="range-factory">Range factory</h3>
<p>La libreria standard include anche adattatori capaci di generare una view senza partire da un range esistente.</p>
<p>Una tra le svariate è std::views::iota, che crea una view incrementale di interi.</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>, introdotta in C++23, permette di combinare più range in un'unica view. Ogni elemento prodotto è una tupla che contiene i valori corrispondenti dei range di origine.</p>
<p>Cerchiamo di capire meglio con un esempio.</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="conclusione">Conclusione</h2>
<p>Range e view permettono di descrivere una pipeline di trasformazioni senza introdurre contenitori temporanei non necessari. Per una panoramica completa degli adattatori e degli algoritmi disponibili, si può consultare la <a href="https://en.cppreference.com/w/cpp/ranges">documentazione della libreria ranges</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>C++20 Concepts</title>
      <link>https://nexenne.com/it/blog/cpp20_concepts/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/cpp20_concepts/</guid>
      <pubDate>Sun, 25 Aug 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Una spiegazione dei concept di C++20: cosa sono, quando servono e come usarli senza appesantire il codice.]]></description>
      <category>C++</category>
      <category>Tutorial</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Questo articolo introduce i <code>concept</code>, una delle funzionalità più utili di C++20 per il codice generico. Vedremo la terminologia essenziale e come usarli per esprimere in modo esplicito i requisiti di un template.</p>
<p>Per un riferimento completo, cppreference raccoglie la <a href="https://en.cppreference.com/w/cpp/language/constraints">documentazione su vincoli e concept</a>.</p>
<h2 id="motivazione">Motivazione</h2>
<p>La prima domanda è semplice: a cosa servono i concept?</p>
<p>Quando scriviamo codice vogliamo, quasi sempre, che gli algoritmi e le strutture dati che implementiamo siano generici, cioè utilizzabili con tipi di dato diversi.
Vogliamo quindi un'unica soluzione generica, senza doverla reimplementare per tipi di dato specifici.
Questo offre diversi vantaggi:</p>
<ul>
<li>maggiore manutenibilità;</li>
<li>riutilizzo dello stesso codice con tipi diversi;</li>
<li>possibilità per chi usa l'API di fornire tipi personalizzati.</li>
</ul>
<p>Gli esempi più comuni sono gli algoritmi generici e le strutture della libreria standard, come <code>std::swap</code> e <code>std::vector</code>.</p>
<p>In C++ questa astrazione viene espressa con un template, che può essere istanziato con tipi diversi. Spesso, però, il tipo non può essere scelto arbitrariamente: l'implementazione può richiedere operazioni o proprietà specifiche.</p>
<p>Supponiamo di avere un algoritmo che, dati due valori dello stesso tipo, voglia eseguire l'operazione di addizione binaria e restituire il risultato.</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>Il parametro generico T del template non è vincolato: dal punto di vista della compilazione può essere istanziato con qualsiasi tipo di dato.
Tuttavia, se si richiede l'istanziazione di questa funzione con certi tipi di dato, il compilatore genererà un errore.</p>
<p>Questo avviene perché il template della funzione in questione richiede implicitamente che i tipi di dato passati come parametro debbano fornire l'operatore binario di addizione.
Quindi passando un tipo di dato che non presenta l'operatore di addizione binaria, il compilatore riscontra l'impossibilità di generare la funzione.</p>
<p>In quale punto fallisce il compilatore?</p>
<p>Non nella dichiarazione del template, ma durante la sua istanziazione, nel punto in cui l'implementazione tenta di usare un'operazione non disponibile.</p>
<p>Questo produce messaggi di errore complessi da decifrare e causa parecchia frustrazione.
In codebase medio-grandi, con strutture dati annidate, la situazione peggiora esponenzialmente.</p>
<p>Per risolvere questo inconveniente dobbiamo introdurre dei <code>vincoli</code>, così da definire in modo esplicito i requisiti dei parametri del template.</p>
<h2 id="terminologia">Terminologia</h2>
<h3 id="modello-template">Modello (template)</h3>
<p>Un modello è un costrutto che genera un tipo o una funzione normale in fase di compilazione in base agli argomenti forniti dall'utente per i parametri del modello.
Gli argomenti di un modello possono essere vincolati.</p>
<h3 id="requisiti">Requisiti</h3>
<p>Sono espressi tramite la parola chiave <code>requires</code>, che descrive le condizioni che un tipo o un'espressione devono soddisfare.
Per i dettagli rimando alla <a href="https://en.cppreference.com/w/cpp/language/requires">documentazione di cppreference su requires</a>.</p>
<h3 id="vincolo-constraint">Vincolo (constraint)</h3>
<p>Un <code>vincolo</code> è un insieme di <code>requisiti</code> sugli argomenti di un modello.</p>
<p>Questi sono usati per:</p>
<ul>
<li>Selezionare correttamente gli overloading delle funzioni.</li>
<li>Decidere la specializzazione più appropriata per un modello.</li>
</ul>
<h3 id="concetti-concepts">Concetti (concepts)</h3>
<p>Un concetto è un predicato che racchiude un insieme di vincoli.
Ogni concetto viene valutato in fase di compilazione e diventa parte dell'interfaccia di un modello in cui viene usato sotto forma di vincolo.</p>
<p>Inoltre:</p>
<ul>
<li>Un tipo di dato che soddisfa tutti i requisiti (e quindi i vincoli) di un concetto si dice che modella tale concetto.</li>
<li>Un concetto che è composto da un altro concetto e da vincoli aggiuntivi si dice che rifinisce il concetto (o i concetti).</li>
</ul>
<h2 id="sintassi">Sintassi</h2>
<p>A seconda della complessità di un vincolo, possiamo usare tre sintassi diverse per esprimerlo.
Tutte le definizioni di seguito sono equivalenti ed è possibile combinarle insieme. Si tenga presente che std::integral è un concetto predefinito.</p>
<h3 id="dichiarazione-completa-ed-esplicita">Dichiarazione completa ed esplicita</h3>
<p>Molto utile se si devono imporre vincoli multipli.</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="dichiarazione-intermedia">Dichiarazione intermedia</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="dichiarazione-compatta">Dichiarazione compatta</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="soluzione">Soluzione</h2>
<p>Per risolvere il problema dichiariamo il concept <code>Addable</code> e lo applichiamo ai parametri della funzione <code>add</code>. In questo caso usiamo la forma compatta.</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">// requisito 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>Il template rifiuta in fase di compilazione qualsiasi tipo che non soddisfi il requisito. Rispetto alla versione non vincolata, il compilatore può produrre un errore più vicino all'interfaccia della funzione e quindi più semplice da interpretare.</p>
<h2 id="conclusione">Conclusione</h2>
<p>Abbiamo visto come usare concept e vincoli per rendere espliciti i requisiti dei template e ottenere errori di compilazione più comprensibili. Per approfondire il tema, cppreference offre una guida completa a <a href="https://en.cppreference.com/w/cpp/language/constraints">vincoli e concept</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Recensione di openSUSE Tumbleweed</title>
      <link>https://nexenne.com/it/blog/opensuse_tumbleweed_review/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/opensuse_tumbleweed_review/</guid>
      <pubDate>Thu, 01 Aug 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[La mia esperienza con openSUSE Tumbleweed tra rolling release, KDE, stabilità e piccoli compromessi quotidiani.]]></description>
      <category>OS</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Dopo aver lasciato Arch Linux sono passato a openSUSE Tumbleweed, che nel tempo è diventato il mio sistema operativo principale. L'ho usato per diversi mesi nello sviluppo software, per il gaming, nelle attività d'ufficio e nell'uso quotidiano.</p>
<p>La caratteristica che apprezzo di più è l'equilibrio tra aggiornamenti frequenti e affidabilità. Tumbleweed è una distribuzione rolling release, ma usa un processo di test che riduce molti dei problemi normalmente associati a questo modello.</p>
<p>Dopo aver provato diverse distribuzioni negli anni, Tumbleweed si distingue soprattutto per l'integrazione con KDE Plasma, gli strumenti di amministrazione e la qualità degli aggiornamenti. In questo articolo raccolgo gli aspetti che ho apprezzato e i compromessi incontrati.</p>
<h2 id="cosa-funziona-bene">Cosa funziona bene</h2>
<h3 id="stabile-e-allavanguardia">Stabile e all'avanguardia</h3>
<p>openSUSE Tumbleweed è una distribuzione rolling release, o a rilascio continuo, che offre agli utenti le caratteristiche e gli sviluppi più recenti del mondo Linux: kernel, driver, ambienti desktop e applicazioni aggiornate.</p>
<p>Questo è utile per utenti avanzati, sviluppatori software e videogiocatori che hanno bisogno di pacchetti recenti.</p>
<p>Tumbleweed viene aggiornato continuamente, quindi non richiede grossi avanzamenti di versione ogni sei mesi come accade con altre distribuzioni GNU/Linux.</p>
<p>Questa distribuzione non utilizza rigidi cicli di rilascio periodici, ma si basa su Factory, la base di sviluppo di openSUSE, aggiornata frequentemente.</p>
<p>Ogni aggiornamento, che segue un rigido standard industriale e un rigoroso processo di garanzia di qualità, viene pubblicato dopo aver superato test approfonditi e controlli di qualità attraverso la piattaforma openQA.</p>
<p>In sostanza, ogni nuova versione di un pacchetto viene testata individualmente e insieme ad altri gruppi di versioni, assicurando così la coerenza complessiva del sistema.</p>
<p>Inoltre, openSUSE utilizza un filesystem moderno come Btrfs, che consente agli utenti di creare istantanee dello stato del sistema e di eseguire il rollback a uno stato precedente in caso di problemi.</p>
<p>Tutto questo rende Tumbleweed un sistema adatto all'uso quotidiano anche se resta una rolling release. Una volta installato e configurato, può essere usato con continuità, a patto di mantenerlo aggiornato.</p>
<h3 id="yast-e-snapper">YaST e Snapper</h3>
<p>Tumbleweed include YaST, un pannello di controllo molto completo.
YaST, acronimo di Yet another Setup Tool, permette di gestire utenti, stampanti, sorgenti software, boot loader, partizioni, servizi, rete, virtualizzazione, AppArmor e altre parti del sistema da un'unica interfaccia.</p>
<p>OST mette a disposizione anche Snapper, creato da Arvin Schnell, per gestire le istantanee dei subvolumi del file system Btrfs e dei volumi LVM thin-provisioned.
Oltre a creare ed eliminare istantanee, anche pianificate e automatiche, Snapper permette di confrontarle e ripristinare le differenze.
In pratica consente di visualizzare versioni precedenti dei file e recuperare modifiche in modo controllato.</p>
<p>Insieme, YaST e Snapper semplificano l'amministrazione del sistema e rendono più semplice recuperare una configurazione funzionante dopo un aggiornamento problematico.</p>
<h3 id="configurazione-intuitiva">Configurazione intuitiva</h3>
<p>Partiamo dall'installazione. L'installer di Tumbleweed è, a mio avviso, il più completo e semplice che ho mai incontrato.
Permette di avere pieno controllo su quello che verrà installato e fornisce una configurazione di default più che sufficiente per la maggior parte degli utenti.
Io prediligo e consiglio sempre di abilitare la cifratura completa del disco e il Logical Volume Management per gestire al meglio le risorse fisiche.</p>
<p>A installazione ultimata, se si dovesse avere bisogno di codec di terze parti, basterà installare opi, un front-end per accedere all'Open Build Service di openSUSE.
Per ottenere direttamente tutti i codec, basterà usare il comando <code>opi codecs</code>, che abiliterà nuovi repository e installerà i codec.</p>
<p>Se dovessero servirvi i driver per la vostra scheda grafica NVIDIA, potranno essere installati direttamente da YaST oppure con il semplice comando <code>zypper install-new-recommends --repo NVIDIA</code>.</p>
<h2 id="i-lati-negativi">I lati negativi</h2>
<p>Uno dei principali svantaggi di Tumbleweed è la possibile incompatibilità temporanea con alcuni moduli del kernel di terze parti o driver proprietari, soprattutto quelli grafici. Il kernel viene aggiornato frequentemente e alcuni componenti esterni potrebbero non essere subito compatibili con la nuova versione.
La soluzione è compilare direttamente da sorgente tali moduli o driver, oppure evitare di usarli completamente.</p>
<p>Inoltre, l'installazione di software di terze parti non presenti nei repository, anche attraverso YaST Software, provoca un errore di integrity check. L'errore può essere facilmente ignorato e il software verrà comunque installato correttamente.</p>
<p>Nota dolente: il continuo scontro tra PackageKit e YaST Software. Rimuovendo il pacchetto PackageKit il problema sparisce.</p>
<p>openSUSE rimane una distribuzione di nicchia, quindi la documentazione di terze parti è meno abbondante. La documentazione ufficiale è ottima, anche se può risultare più essenziale rispetto a quella di distribuzioni come Fedora o Ubuntu.</p>
<p>Chiudo con un fastidio più che con un vero problema. Abilitando repository di terze parti, soprattutto Packman, possono comparire conflitti tra versioni dei pacchetti e aggiornamenti di sistema. Di solito basta aspettare qualche giorno prima di aggiornare oppure usare le versioni Flatpak delle applicazioni interessate.</p>
<h2 id="conclusione">Conclusione</h2>
<p>Nella mia esperienza, openSUSE Tumbleweed offre pacchetti recenti senza sacrificare l'affidabilità del sistema. openQA, Btrfs, Snapper e YaST formano una base solida per una distribuzione rolling release.</p>
<p>I compromessi principali riguardano i moduli esterni al kernel e i possibili conflitti tra repository. Per ridurre questi problemi preferisco limitare i repository di terze parti e usare Flatpak per le applicazioni che richiedono codec proprietari. Con queste accortezze, Tumbleweed si è dimostrata una buona distribuzione per il lavoro e l'uso quotidiano.</p>]]></content:encoded>
    </item>
    <item>
      <title>Aggiornamenti automatici direttamente dalla fonte</title>
      <link>https://nexenne.com/it/blog/autoupdate_your_android_foss_apps_directly_from_source/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/autoupdate_your_android_foss_apps_directly_from_source/</guid>
      <pubDate>Sat, 13 Jul 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Come uso Obtainium per aggiornare app Android open source direttamente dalle fonti ufficiali.]]></description>
      <category>OSS</category>
      <category>Android</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>F-Droid è spesso raccomandato dagli appassionati di sicurezza e privacy, e immagino che molti lo usino per scaricare, installare e aggiornare le proprie app preferite su smartphone.
Anch'io ho usato F-Droid per molto tempo, ma dopo aver letto <a href="https://privsec.dev/posts/android/f-droid-security-issues/">l'articolo di PrivSec</a> sui problemi di sicurezza dell'applicazione ho deciso di cercare un'alternativa.</p>
<p>All'inizio usavo i feed RSS per seguire gli aggiornamenti delle applicazioni che mi interessavano.
Scaricavo l'app direttamente dalla pagina GitHub o GitLab dello sviluppatore, mi iscrivevo al feed RSS e aspettavo le notifiche sugli aggiornamenti.
Quando usciva una nuova release, tornavo alla pagina dedicata, scaricavo l'APK e installavo manualmente l'aggiornamento.
In poco tempo, però, mi sono accorto che era un processo macchinoso, lento e snervante.</p>
<p>Mi serviva uno strumento capace di fare la stessa cosa automaticamente dopo una configurazione iniziale. La soluzione che ho scelto è <code>Obtainium</code>.</p>
<h2 id="cos-è-obtainium">Cos'è Obtainium?</h2>
<p>Obtainium è un'app che automatizza il download e l'installazione degli aggiornamenti delle app Android direttamente dai siti web di origine, cioè dai siti in cui i file sono disponibili per il download diretto.</p>
<h2 id="come-scaricare-obtainium">Come scaricare Obtainium?</h2>
<p>Per installare Obtainium bisogna partire dalla <a href="https://github.com/ImranR98/Obtainium">pagina ufficiale di GitHub</a>.</p>
<p>Nella sezione di installazione selezioniamo "Get it on GitHub", apriamo gli asset della release e scegliamo l'APK adatto al dispositivo. Per un Google Pixel, per esempio, la variante è generalmente <code>app-arm64-v8a</code>.
Al termine del download possiamo procedere con la normale installazione.
Terminata l'installazione, sarà possibile aprire l'app. Consiglio di consentire la ricezione delle notifiche: vengono gestite localmente e non richiedono i servizi di Google Play.</p>
<h2 id="linterfaccia">L'interfaccia</h2>
<p>Quando ho aperto Obtainium per la prima volta, sono rimasto piacevolmente sorpreso dal design.</p>
<p>Il progetto viene aggiornato spesso, quindi nomi e disposizione delle schermate potrebbero cambiare, ma il flusso generale resta simile.</p>
<p>L'app presenta una bottom navigation con queste voci:</p>
<ul>
<li>Apps, l'elenco completo delle app aggiunte a Obtainium.</li>
<li>Add App, per registrare una nuova app da tracciare.</li>
<li>Import/Export, per importare o esportare la lista delle app e la configurazione di Obtainium.</li>
<li>Settings, per configurare l'applicazione.</li>
</ul>
<h2 id="aggiungere-unapp">Aggiungere un'app</h2>
<p>Per aggiungere una nuova applicazione, clicchiamo su "Add App".
In basso c'è un pulsante per vedere le fonti supportate. Alcune, come GitHub e Codeberg, sono etichettate come ricercabili: in questi casi si può cercare direttamente da Obtainium.
Ci sono due campi di testo: nel primo possiamo inserire direttamente l'URL di origine dell'app che vogliamo aggiungere, mentre il secondo permette di cercarla tramite una stringa alfanumerica.</p>
<p>In questo caso cerchiamo NewPipe, dato che il progetto è su GitHub. La ricerca può restituire molti risultati, soprattutto per un progetto popolare; nel mio caso il primo era quello ufficiale del team di NewPipe.
Per sicurezza, suggerisco sempre di verificare che il progetto sia quello corretto; ancora meglio, si può cercare la pagina dal browser e inserire manualmente l'URL.</p>
<p>Una volta convalidata la fonte, possiamo selezionarla premendo "Select".</p>
<p>Appare poi una sezione con opzioni aggiuntive. Tra le più importanti:</p>
<ul>
<li>Include prereleases, da abilitare soltanto se si desidera ricevere versioni di anteprima potenzialmente instabili.</li>
<li>Fallback to older releases, è utile quando gli sviluppatori su GitHub non eseguono correttamente i rilasci e potrebbero avere un rilascio per iPhone e uno per Android. Se sono elencate in release diverse, quando Obtainium va a controllare qual è la versione più recente, se quella per iPhone è stata rilasciata per ultima, vedrà che non c'è un APK per Android disponibile da installare.</li>
<li>Filter Release Titles by Regular Expression, per filtrare i titoli delle versioni in base a un'espressione regolare. È un caso limite, ma può capitare che nell'URL inserito siano presenti più applicazioni scaricabili.</li>
<li>Track Only, solo tracciamento e niente aggiornamenti automatici. Io lascio questa opzione disattivata perché voglio che Obtainium scarichi gli APK per me, così da poterli installare automaticamente.</li>
</ul>
<p>Ora possiamo selezionare "Add". L'applicazione viene scaricata sul dispositivo e, al termine, viene mostrata la schermata con i relativi dettagli.
Cliccando su "Install", Android chiederà di abilitare l'installazione da sorgenti sconosciute per Obtainium. Accettiamo e installiamo l'app.</p>
<p>Terminata l'installazione, tornando all'elenco delle applicazioni possiamo vedere che NewPipe è ora monitorata, con l'ultima versione installata correttamente.</p>
<p>Sembra un processo lungo, ma una volta capiti i passaggi principali si fa tutto in pochi secondi.</p>
<h2 id="e-se-obtainium-fosse-affetto-da-malware">E se Obtainium fosse affetto da malware?</h2>
<p>Come sempre, bisogna essere scettici nei confronti di tutto ciò che si scarica dal web, che sia open source o meno.
Una precauzione aggiuntiva consiste nell'installare manualmente la prima versione dalla fonte ufficiale e aggiungerla soltanto dopo a Obtainium. Android accetta infatti gli aggiornamenti solo quando la firma corrisponde a quella dell'app già installata; un APK firmato con una chiave diversa verrebbe rifiutato.</p>
<h2 id="limitazioni">Limitazioni</h2>
<p>Ci sono alcune limitazioni da tenere presenti.
La prima è che le installazioni delle app avvengono in modo asincrono e non è possibile determinare direttamente il successo o il fallimento di un'installazione.
Questo implica che gli stati e le versioni dell'installazione non siano sincronizzati con il sistema operativo fino al lancio successivo o fino a quando il problema non viene corretto manualmente.
In sostanza, bisogna riavviare l'applicazione.</p>
<p>La seconda è che per alcune fonti, i dati sono raccolti tramite web scraping, che può facilmente fallire a causa di cambiamenti nel design del sito web.
Chiunque abbia usato NewPipe avrà notato che, a ogni cambiamento dell'interfaccia di YouTube, l'extractor di NewPipe rischia di rompersi e di non riuscire più a estrarre dati fondamentali.
Con Obtainium può succedere qualcosa di simile: è una conseguenza della natura fragile del web scraping.</p>
<h2 id="conclusione">Conclusione</h2>
<p>Obtainium permette di seguire gli aggiornamenti direttamente dalle fonti ufficiali e riduce il lavoro manuale necessario per scaricare nuove release. Richiede attenzione nella scelta dei repository e non elimina i rischi legati alla distribuzione degli APK, ma offre un buon compromesso per chi vuole gestire applicazioni open source fuori dagli store tradizionali.</p>]]></content:encoded>
    </item>
    <item>
      <title>Migrare verso applicazioni open source su smartphone</title>
      <link>https://nexenne.com/it/blog/migrate_to_open_source_applications_on_mobile/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/migrate_to_open_source_applications_on_mobile/</guid>
      <pubDate>Sat, 29 Jun 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Dopo il passaggio a GrapheneOS ho iniziato a sostituire le app principali con alternative più rispettose della privacy.]]></description>
      <category>OSS</category>
      <category>Android</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Dopo essere passato a GrapheneOS ho deciso di sostituire molte app con alternative più rispettose della privacy.
Mi sono accorto, però, di quanto dipendessi dalle applicazioni delle grandi piattaforme.</p>
<p>Le applicazioni che usavo di più sul mio vecchio Samsung S10 erano queste:</p>
<ul>
<li>WhatsApp.</li>
<li>Suite Google: Mail, Calendar e Drive.</li>
<li>Suite Google Workspace.</li>
<li>YouTube.</li>
<li>Google Maps.</li>
<li>Google Play Store.</li>
</ul>
<p>Ovviamente queste sono solo alcune delle applicazioni principali che utilizzavo.
In pratica, buona parte della mia vita digitale passava da Google, Microsoft e Meta.</p>
<p>Ho quindi cercato alternative open source e più attente alla privacy, accettando alcuni compromessi rispetto ai servizi che usavo in precedenza.</p>
<h2 id="sostituire-whatsapp">Sostituire WhatsApp</h2>
<p>Questa è stata la fase più difficile, non per la scelta dell'app ma per la migrazione.
Oggi quasi tutti usano WhatsApp, che per molte persone è diventato il canale principale.
Ho dovuto avvisare tutti i miei contatti del cambiamento e del fatto che a breve non sarei più stato reperibile su WhatsApp.
Le domande non sono mancate.
Alla fine sono passato a Signal Messenger e mi hanno seguito in pochi. Per me non era un grande problema: chi voleva contattarmi poteva comunque chiamarmi, invece di mandarmi messaggi o audio interminabili.</p>
<p>Perché Signal e non alternative come Session, Briar, Element o SimpleX Chat?</p>
<p>In breve:</p>
<p>Gli elenchi di contatti su Signal sono crittografati utilizzando il PIN di Signal e il server non ha accesso ad essi.
Anche i profili personali sono crittografati e condivisi solo con i contatti con cui si chatta.
Signal supporta i gruppi privati, in cui il server non ha alcuna traccia dell'appartenenza al gruppo, dei titoli del gruppo, degli avatar del gruppo o degli attributi del gruppo.
Signal ha metadati minimi quando è abilitato il Sealed Sender.
L'indirizzo del mittente è crittografato insieme al corpo del messaggio e solo l'indirizzo del destinatario è visibile al server.
Sealed Sender è abilitato solo per le persone presenti nell'elenco dei contatti, ma può essere abilitato per tutti i destinatari, con un rischio maggiore di ricevere spam.</p>
<p>Inoltre, il protocollo è stato sottoposto a verifiche indipendenti e le sue specifiche sono pubbliche.</p>
<p>Ciliegina sulla torta: l'interfaccia utente di Signal è molto simile a quella di WhatsApp.</p>
<h2 id="sostituire-la-suite-google-mail-calendar-e-drive">Sostituire la suite Google: Mail, Calendar e Drive</h2>
<p>Questi sono alcuni dei provider che ho valutato, suddivisi per categoria.</p>
<p>Servizi email:</p>
<ul>
<li>Proton Mail</li>
<li>Tutanota</li>
<li>Mailbox.org</li>
</ul>
<p>Servizi cloud storage:</p>
<ul>
<li>Proton Drive</li>
<li>Tresorit</li>
<li>Peergos</li>
</ul>
<p>Servizi di calendario:</p>
<ul>
<li>Proton Calendar</li>
<li>Tuta (calendario di Tutanota)</li>
</ul>
<p>Le opzioni erano due: separare tutti i servizi o affidarsi a un'unica azienda.</p>
<p>Ho quindi fatto la mia scelta, passando al pacchetto di Proton.
Le motivazioni che mi hanno portato a questa conclusione sono molteplici.
Ho scelto Proton perché riunisce posta, calendario e spazio cloud in un unico servizio, con applicazioni curate e un modello orientato alla privacy.</p>
<p>In sintesi ho sostituito Gmail, Google Calendar e Google Drive con Proton Mail, Proton Calendar e Proton Drive.</p>
<h2 id="rimpiazzare-la-suite-google-workspace">Rimpiazzare la suite Google Workspace</h2>
<p>Purtroppo per questa non ho trovato una valida alternativa.
La scelta che ho fatto è stata abbinare Collabora Office per l'editing dei documenti a Syncthing per la sincronizzazione.</p>
<p>Questo perché Collabora Online (Office) non è un software autonomo.
Al contrario, la suite per ufficio online si integra in un'infrastruttura esistente e richiede una soluzione cloud come base (NextCloud, Dropbox, ecc...).</p>
<h2 id="rimpiazzare-youtube">Rimpiazzare YouTube</h2>
<p>Uso YouTube principalmente per documentarmi, ma anche per svago.
In questo caso continuo a usare la piattaforma di Google, ma attraverso un client di terze parti.</p>
<p>Fortunatamente non ho dovuto fare molta ricerca: usavo già NewPipe da diversi anni.</p>
<p>NewPipe è un'applicazione open source, ricca di funzioni e rispettosa della privacy, che permette di guardare i video pubblicati su YouTube.
L'app offre molte funzioni dell'esperienza YouTube senza annunci invasivi e senza le autorizzazioni richieste dall'app ufficiale.
In più è open source e il codice è consultabile su GitHub.</p>
<h2 id="rimpiazzare-google-maps">Rimpiazzare Google Maps</h2>
<p>Qui l'unica soluzione è passare a un client basato su OpenStreetMap.</p>
<p>Le alternative sono:</p>
<ul>
<li>OsmAnd</li>
<li>Organic Maps</li>
<li>Magic Earth</li>
</ul>
<p>Le prime due sono eccezionali sia nella consultazione delle mappe che nelle funzionalità offerte, tuttavia peccano nella navigazione in tempo reale. Durante la navigazione, spesso consigliano strade secondarie molto scomode o selezionano percorsi che allungano inutilmente il tragitto.
La terza eccelle dove le altre falliscono, tuttavia il codice sorgente di questo applicativo non è consultabile.</p>
<p>Uso principalmente Organic Maps per l'interfaccia pulita e per le escursioni. Quando devo guidare su percorsi stradali che non conosco, preferisco invece Magic Earth.</p>
<h2 id="rimpiazzare-google-play-store">Rimpiazzare Google Play Store</h2>
<p>Come per YouTube, una soluzione consiste nell'usare un client alternativo per accedere al catalogo del Play Store.
L'unica app che svolge questa mansione impeccabilmente è Aurora Store.
Aurora Store è una alternativa gratuita al Google Play Store.
Con questa app è possibile scaricare applicazioni, aggiornare quelle già esistenti, ricevere dettagli sui tracker in-app, nascondere la propria posizione, cercare applicazioni e fare molto altro.
Gli sviluppatori di Aurora hanno anche curato molto il design e l'interfaccia dell'app.</p>
<h2 id="conclusione">Conclusione</h2>
<p>Sostituire le applicazioni più diffuse richiede compromessi: a volte si perde comodità, altre volte si guadagna controllo. Per me il passaggio ha avuto senso perché ha ridotto la dipendenza dalle piattaforme principali senza rendere lo smartphone scomodo da usare.</p>]]></content:encoded>
    </item>
    <item>
      <title>GrapheneOS, il mio sistema operativo per smartphone preferito</title>
      <link>https://nexenne.com/it/blog/graphene_os_as_my_daily_driver/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/graphene_os_as_my_daily_driver/</guid>
      <pubDate>Fri, 07 Jun 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Dopo mesi di utilizzo quotidiano, GrapheneOS è diventato il mio sistema operativo preferito per smartphone.]]></description>
      <category>OS</category>
      <category>Android</category>
      <category>Smartphone</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Nel giugno del 2023 il mio vecchio Samsung Galaxy S10 ha deciso di passare a miglior vita dopo una rovinosa caduta dalle scale.</p>
<p>Stavo già pensando di sostituirlo e cercavo un dispositivo sul quale avere maggiore controllo, con un sistema orientato alla sicurezza e alla privacy.</p>
<p>Ho quindi confrontato diversi sistemi basati sull'Android Open Source Project (AOSP), modificati con obiettivi differenti in materia di privacy e sicurezza.</p>
<p>Alla fine ho ristretto la scelta a tre alternative: CalyxOS, LineageOS e GrapheneOS.</p>
<p>Dopo averle valutate ho scelto GrapheneOS e acquistato un dispositivo ufficialmente supportato.</p>
<p>Vediamo che cos'è GrapheneOS, come si installa e quali funzionalità mi hanno convinto a sceglierlo.</p>
<h2 id="cos-è-grapheneos">Cos'è GrapheneOS?</h2>
<p>GrapheneOS è un sistema operativo basato su Android Open Source Project, con molte funzionalità aggiuntive per sicurezza e privacy.
È open-source, supporta ufficialmente tutti i dispositivi Google Pixel più recenti e può essere installato con grande facilità per sostituire il sistema operativo del Pixel di Google.</p>
<p>Per impostazione predefinita non include né applicazioni né servizi Google: in sostanza, spezza il controllo che Google esercita sul dispositivo mobile.</p>
<p>Il risultato è un dispositivo spartano, in senso positivo: solo applicazioni stock del progetto AOSP, tutto da configurare e senza vincoli imposti da terzi.</p>
<h2 id="come-installare-grapheneos">Come installare GrapheneOS?</h2>
<p>Il progetto fornisce un'interfaccia web minimale che permette di installare il sistema operativo seguendo istruzioni molto chiare.</p>
<p>Per iniziare si parte dalla <a href="https://grapheneos.org/install/">guida ufficiale</a> e si sceglie tra l'installazione tramite WebUSB e quella classica da terminale.</p>
<p>Successivamente basta seguire la guida passo passo che viene fornita.</p>
<h2 id="alcune-funzionalità-di-grapheneos">Alcune funzionalità di GrapheneOS</h2>
<p>Quella che segue è una panoramica non esaustiva delle funzionalità di GrapheneOS. Per i dettagli conviene consultare la <a href="https://grapheneos.org/features">pagina ufficiale delle funzionalità</a>.</p>
<h3 id="protezione-contro-le-vulnerabilità-zero-day-oltre-a-funzionalità-aggiuntive-per-utenti-e-rete-dot">Protezione contro le vulnerabilità zero-day, oltre a funzionalità aggiuntive per utenti e rete.</h3>
<p>GrapheneOS protegge i suoi utenti dalle vulnerabilità zero-day.
Per ottenere questo risultato GrapheneOS riduce la superficie di attacco rimuovendo il codice non necessario dal sistema operativo.</p>
<p>Per la gestione delle applicazioni, GrapheneOS include toggle dedicati per i permessi di rete e dei sensori, quasi assenti nelle ROM personalizzate basate su AOSP.</p>
<p>A livello di rete, il sistema operativo supporta la randomizzazione dell'indirizzo MAC per ogni connessione e una modalità solo LTE che consente di disattivare le reti meno recenti o più nuove, riducendo la superficie di attacco.
Il Wi-Fi e il bluetooth (come il mobile hotspot) supportano la disattivazione automatica se non sono collegati a un dispositivo, risparmiando la durata della batteria e prevenendo potenziali attacchi wireless esterni.</p>
<p>Degna di nota è la funzione di screenshot privato che disattiva l'inclusione di metadati sensibili.</p>
<h3 id="sandboxing-e-protezione-dalla-corruzione-della-memoria">Sandboxing e protezione dalla corruzione della memoria.</h3>
<p>Per ridurre notevolmente le vulnerabilità del sistema operativo, il gruppo dietro a GrapheneOS dedica notevoli risorse allo sviluppo di linguaggi e librerie sicuri per la memoria, strumenti di analisi statica e dinamica e molto altro.</p>
<p>In GrapheneOS viene applicato il sandboxing a vari livelli, rafforzando il kernel e i componenti del sistema operativo.
In sostanza, i componenti vengono isolati in ambienti separati, limitando l'accesso delle applicazioni e l'impatto di eventuali vulnerabilità.</p>
<h3 id="le-applicazioni">Le applicazioni</h3>
<p>GrapheneOS fornisce una serie di applicazioni rinforzate, pensate per ridurre permessi e superficie di attacco.</p>
<p>Innanzitutto, c'è il WebViewer e il browser Vanadium.
Vanadium è una variante di Chromium rafforzata con ulteriori misure di sicurezza e privacy.</p>
<p>Secure Camera, realizzata dal team di GrapheneOS, è la fotocamera predefinita del sistema. Include le funzioni tradizionali e aggiunge opzioni extra per privacy e sicurezza, come la scansione QR senza permessi di rete o accesso ai media e la rimozione opzionale dei metadati EXIF da foto e video.</p>
<p>Auditor fornisce invece una verifica basata sull'hardware dell'integrità del software e del firmware del dispositivo. È una funzione speciale, utile soprattutto per persone esposte a rischi mirati.</p>
<p>Infine, Secure PDF Viewer è un lettore PDF che non richiede permessi e opera in ambiente sandbox.</p>
<h3 id="gestione-dei-profili-migliorata">Gestione dei profili migliorata</h3>
<p>GrapheneOS ha migliorato la funzionalità dei profili utente e sta migliorando il monitoraggio di altri profili.</p>
<p>Nel dettaglio fornisce le possibilità di:</p>
<ul>
<li>Aggiungere più profili.</li>
<li>Terminare la sessione.</li>
<li>Disabilitare l'installazione di app in determinati profili.</li>
<li>Installare le app disponibili di un profilo verso un altro.</li>
<li>Inoltro delle notifiche dai profili non in uso verso la sessione corrente.</li>
</ul>
<h3 id="google-in-sandbox">Google in sandbox</h3>
<p>Le app di Google possono essere installate su GrapheneOS attraverso un livello di compatibilità dedicato.
Le app verranno private dell'accesso speciale o dei privilegi che di solito hanno.
È possibile utilizzare le applicazioni e i servizi di Google, ma saranno modificati per ottenere elevati standard di privacy e sicurezza.</p>
<h2 id="conclusione">Conclusione</h2>
<p>Uso GrapheneOS da circa un anno e l'esperienza è stata positiva. I prodotti orientati alla privacy spesso richiedono compromessi significativi nell'usabilità; in questo caso l'impatto sull'uso quotidiano è rimasto contenuto.</p>
<p>Non ho dovuto cambiare radicalmente il modo in cui uso il telefono. Il passaggio più scomodo è stato sostituire molte applicazioni legate all'ecosistema Google, tema che merita un post a parte.</p>
<p>Il limite principale riguarda le applicazioni che dipendono dai servizi Google Play. Senza installare il livello di compatibilità dedicato, alcune funzioni, come le notifiche push basate su Firebase, possono non essere disponibili.</p>
<p>Nel complesso ho ottenuto un telefono più controllabile, orientato alla sicurezza e alla privacy, senza rinunciare alle funzioni che uso ogni giorno.</p>]]></content:encoded>
    </item>
    <item>
      <title>Kill switch per wg-quick e nftables</title>
      <link>https://nexenne.com/it/blog/nftables_wg_quick_killswitch/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/nftables_wg_quick_killswitch/</guid>
      <pubDate>Mon, 20 May 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Come ho portato le regole di kill switch da iptables a nftables dentro una configurazione wg-quick.]]></description>
      <category>GNU/Linux</category>
      <category>HowTo</category>
      <category>Administration</category>
      <category>VPN</category>
      <category>Command Line</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Un kill switch impedisce al traffico di uscire attraverso la connessione ordinaria quando il tunnel WireGuard non è attivo, evitando di esporre accidentalmente l'indirizzo IP reale.</p>
<p>Dopo il passaggio da iptables a nftables, ho dovuto tradurre le regole nella configurazione del peer di wg-quick.
Dato che non ho trovato informazioni utili al momento della configurazione, ho deciso di condividere la soluzione che ho usato.</p>
<h2 id="configurazione">Configurazione</h2>
<p>Questa configurazione presuppone che le regole nftables vengano ricaricate tramite il sistema di init.</p>
<p>Nel file di configurazione di wg-quick aggiungiamo queste due righe.</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>Queste righe vanno adattate al vostro sistema e al vostro firewall.</p>
<h3 id="postup">PostUp</h3>
<p>Eseguiamo <code>nft list ruleset</code> e cerchiamo la catena che contiene <code>type filter hook output</code>.</p>
<p>Ora adattiamo la riga <code>PostUp</code> con le informazioni trovate. Nel mio caso questa istruzione si trova nella tabella <code>firewalld</code>, parte della famiglia <code>inet</code>, nella chain <code>filter_OUTPUT</code>.</p>
<h3 id="postdown">PostDown</h3>
<p>Nella riga <code>PostDown</code> inseriamo il comando necessario a ricaricare il firewall. Nell'esempio viene usato systemd.</p>
<h2 id="esempio">Esempio</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/it/blog/iptables_vs_nftables/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/iptables_vs_nftables/</guid>
      <pubDate>Fri, 10 May 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Perché nftables rende più ordinata la gestione delle regole firewall rispetto a iptables e ip6tables.]]></description>
      <category>GNU/Linux</category>
      <category>HowTo</category>
      <category>Administration</category>
      <category>Command Line</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>nftables è il framework del progetto Netfilter introdotto nel kernel Linux come successore di iptables. Nasce per offrire un'interfaccia più coerente e flessibile, sostituendo strumenti separati come <code>iptables</code>, <code>ip6tables</code>, <code>arptables</code> ed <code>ebtables</code>.</p>
<p>Con iptables era necessario mantenere separati i set di regole IPv4 e IPv6, gestiti rispettivamente da <code>iptables</code> e <code>ip6tables</code>.</p>
<p>A parte una nuova sintassi e alcuni aggiornamenti, il funzionamento di nftables è simile a quello del suo predecessore.</p>
<h2 id="catene-e-regole">Catene e regole</h2>
<p>Le tabelle più comuni di iptables usano catene predefinite come <code>INPUT</code>, <code>OUTPUT</code> e <code>FORWARD</code>. Ogni catena contiene regole valutate in ordine; se nessuna corrisponde, viene applicata la politica predefinita, per esempio <code>ACCEPT</code> o <code>DROP</code>.</p>
<p>iptables può diventare inefficiente perché i pacchetti attraversano le catene previste anche quando alcune non sono realmente necessarie, introducendo controlli superflui.</p>
<p>nftables mantiene un modello basato su tabelle, catene e regole, ma non impone catene predefinite: è possibile creare soltanto quelle necessarie e collegarle agli hook del kernel appropriati.</p>
<h2 id="differenza-di-sintassi">Differenza di sintassi</h2>
<p>La sintassi di iptables può diventare difficile da leggere quando le regole crescono. nftables usa una grammatica più uniforme e permette di esprimere molti casi con meno duplicazioni.</p>
<p>Per creare una nuova <code>rule</code> si usa una forma simile a questa:</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>Vediamo alcuni esempi equivalenti tra iptables e nftables.</p>
<h3 id="blocco-di-una-connessione">Blocco di una connessione</h3>
<p>Questo comando blocca il traffico in ingresso dall'indirizzo IP <code>192.168.7.5</code>.</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="abilitare-la-connessione-ssh-in-entrata">Abilitare la connessione SSH in entrata</h3>
<p>Questo comando abilita la connessione SSH in entrata.</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>Questo comando abilita la connessione SSH in entrata per tutta la rete 192.168.155.0/24.</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="abilitare-la-connessione-mysql-sullinterfaccia-di-rete-eth0">Abilitare la connessione MySQL sull'interfaccia di rete eth0</h3>
<p>Con questo comando si abilitano le connessioni sull'interfaccia di rete eth0 verso il server di MySQL.</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="abilitare-il-traffico-http-e-https">Abilitare il traffico HTTP e HTTPS</h3>
<p>Con questo comando si abilita il traffico sulla porta 80 e sulla 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="conclusione">Conclusione</h2>
<p>nftables offre un modello più uniforme per gestire IPv4, IPv6 e altri protocolli, con una sintassi più leggibile e una configurazione più flessibile. Per approfondire la creazione di tabelle e catene si può consultare la <a href="https://wiki.archlinux.org/title/Nftables">documentazione di Arch Linux</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Header guards vs pragma once</title>
      <link>https://nexenne.com/it/blog/header_guards_vs_pragma_once/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/header_guards_vs_pragma_once/</guid>
      <pubDate>Wed, 24 Apr 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Confronto tra header guards e pragma once, con vantaggi e limiti di entrambe le soluzioni.]]></description>
      <category>C++</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Durante lo sviluppo di Enne 2D Engine mi sono chiesto più volte perché continuassi a usare gli header guard quando avrei potuto scrivere semplicemente <code>#pragma once</code>.</p>
<p>Ho quindi confrontato le due soluzioni, i loro vantaggi e i rispettivi limiti.</p>
<h2 id="perché-serve-proteggere-i-file-di-dichiarazione">Perché serve proteggere i file di dichiarazione?</h2>
<h3 id="principio-odr">Principio ODR</h3>
<p>La One Definition Rule è una regola fondamentale del C++.</p>
<p>In forma semplificata, stabilisce che una funzione, una variabile o un tipo debbano rispettare precisi vincoli sul numero di definizioni presenti nel programma.</p>
<p>Violare il principio ODR può causare errori di "multiple definitions" in compilazione o problemi in fase di linking, quando il linker non riesce a stabilire quale definizione usare.</p>
<p>Nell'esempio seguente, con tre file definiti, la compilazione fallisce a causa di una definizione multipla di <code>struct foo</code>.</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>Il preprocessore, terminate le opportune sostituzioni, restituisce il seguente risultato.</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>Come facciamo quindi a rispettare l'ODR?</p>
<p>La prima soluzione che viene in mente, anche se molto spartana, è gestire manualmente la gerarchia delle direttive <code>#include</code>.
Nell'esempio precedente dovremmo evitare l'inclusione di <code>alpha.hpp</code> in <code>charlie.hpp</code>.
È una tecnica fragile e non scala su progetti medio-grandi.</p>
<p>Esistono due soluzioni comuni:</p>
<ul>
<li>Header guards.</li>
<li>Direttiva #pragma once.</li>
</ul>
<h2 id="header-guards">Header guards</h2>
<p>La soluzione prevista dallo standard consiste nell'uso degli header guard.
Queste protezioni impediscono che un header venga incluso più di una volta nella stessa unità di compilazione.
Per ottenere questo risultato utilizzano le macro del preprocessore per verificare se l'intestazione è già stata inclusa in precedenza.
Nel caso in cui fosse già stata inclusa, queste clausole impediscono una successiva reinclusione.</p>
<p>Il #define crea una macro, ovvero l'associazione di un identificatore o un identificatore con parametri con una stringa di token.
Dopo che la macro è stata definita, il compilatore può sostituire la stringa di token per ogni occorrenza dell'identificatore presente nel file di origine.</p>
<p>Riprendendo l'esempio precedente, con poche modifiche otteniamo:</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>Il preprocessore terminate le opportune sostituzioni, restituisce il seguente risultato.</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>Quando si lavora su progetti grandi, è importante definire linee guida chiare per il nome della macro: usare solo il nome del file può portare facilmente a conflitti. Altri problemi nascono quando si copia un header e ci si dimentica di aggiornare la macro, oppure quando manca la direttiva <code>#endif</code>.
Per questo è utile affidarsi anche a strumenti come clang-tidy.</p>
<p>Io ad esempio per definire il nome di una macro seguo questo schema <code>&#x3C;PROJECT_ROOT>_&#x3C;RELATIVE_PATH_TO_HPP_FILE>_&#x3C;FILE_NAME>_HPP_</code>.</p>
<p>Questo schema evita che due file con lo stesso nome producano lo stesso identificatore.</p>
<p>Supponiamo di avere la seguente struttura, con root directory <code>CPP_PROJECT</code>, e due file con lo stesso nome in directory diverse.</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>Il nome della macro sul file <code>alpha.hpp</code> nella directory detail sarà:</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>Il nome della macro sul file <code>alpha.hpp</code> nella directory common sarà:</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>L'alternativa agli header guard, molto diffusa anche se non parte dello standard C++, è la direttiva <code>#pragma once</code>.</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>Il preprocessore terminate le opportune sostituzioni, restituisce il seguente risultato.</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>Si scrive meno codice e si eliminano i possibili conflitti tra i nomi delle macro. Questa soluzione, però, non offre soltanto vantaggi.</p>
<p>Non proprio: questa direttiva non fa parte dello standard, quindi i compilatori non sono obbligati da ISO C++ a supportarla.</p>
<p>Ma perché non fa parte dello standard?</p>
<p>La risposta è nella complessità che un compilatore affronta per rilevare correttamente e coerentemente l'uguaglianza dei file.
Uno dei problemi noti riguarda l'identificazione dello stesso file attraverso percorsi o collegamenti simbolici diversi. In alcuni casi il compilatore potrebbe non riconoscere che si tratta dello stesso header e includerlo più volte. Si veda <a href="https://en.m.wikipedia.org/wiki/Pragma_once#Caveats">https://en.m.wikipedia.org/wiki/Pragma_once#Caveats</a>.</p>
<p>Non vi è, poi, garanzia che il supporto di #pragma once sia lo stesso tra i diversi compilatori, il che può essere un problema per alcuni sviluppatori.</p>
<h2 id="header-guards-o-pragma-once">Header guards o pragma once?</h2>
<p>Dipende dal caso.</p>
<p>La scelta dipende dai requisiti di portabilità e dalle convenzioni del progetto.</p>
<p>Gli header guard sono standard e funzionano ovunque, a costo di qualche riga in più e di una convenzione affidabile per le macro. <code>#pragma once</code> è più conciso ed è supportato dai compilatori più diffusi, ma non fa parte dello standard.</p>
<h2 id="conclusione">Conclusione</h2>
<p>Nei progetti in cui la portabilità è prioritaria preferisco gli header guard. In contesti con una toolchain ben definita, <code>#pragma once</code> resta comunque una scelta pragmatica e ampiamente supportata.</p>]]></content:encoded>
    </item>
    <item>
      <title>Annullare l&apos;ultimo commit Git</title>
      <link>https://nexenne.com/it/blog/git_revert_commit/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/git_revert_commit/</guid>
      <pubDate>Mon, 15 Apr 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Differenze tra git revert e git reset quando bisogna annullare l'ultimo commit.]]></description>
      <category>Git</category>
      <category>Control</category>
      <category>Command Line</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Supponiamo di stare lavorando a un progetto con <code>Git</code>.
Per errore o distrazione può capitare di creare un commit che non doveva esistere.
In quella situazione bisogna annullarlo nel modo corretto, scegliendo tra due approcci molto diversi.</p>
<p>I comandi principali sono:</p>
<ul>
<li>Il comando <code>revert</code></li>
<li>Il comando <code>reset</code></li>
</ul>
<h2 id="comando-revert">Comando revert</h2>
<p>Il comando <code>revert</code> crea un nuovo commit che annulla le modifiche del commit selezionato.</p>
<p>La forma base è:</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>Vediamo i passaggi.</p>
<ol>
<li>Lanciare il comando <code>git log</code>.</li>
<li>Ricercare il commit che si vuole ripristinare.</li>
<li>Copiare l'hash del commit da annullare.</li>
<li>Usare il comando <code>git revert &#x3C;commit to revert></code>.</li>
</ol>
<p>Esempio.</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">### Esempio di stampa post git log</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>Dopo l'esecuzione viene creato un nuovo commit che applica le modifiche inverse, senza riscrivere la cronologia esistente.</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="comando-reset">Comando reset</h2>
<p>L'alternativa è il comando <code>reset</code>.
<code>git reset</code> modifica la cronologia locale e va quindi usato con attenzione, soprattutto se i commit sono già stati condivisi. Il comando sposta <code>HEAD</code> al commit indicato; il comportamento dei file e dell'indice dipende dall'opzione scelta.</p>
<h3 id="soft-reset">Soft reset</h3>
<p>L'opzione <code>--soft</code> sposta <code>HEAD</code> mantenendo nell'indice le modifiche introdotte dai commit rimossi.</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>L'opzione <code>--hard</code> riallinea indice e directory di lavoro al commit indicato, eliminando anche le modifiche locali non salvate.</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="conclusione">Conclusione</h2>
<p><code>git revert</code> è la scelta più sicura per una cronologia già condivisa, perché aggiunge un nuovo commit senza riscrivere quelli esistenti. <code>git reset</code> è utile soprattutto per correggere una cronologia locale, prima che venga pubblicata.</p>]]></content:encoded>
    </item>
    <item>
      <title>Come verificare la versione di un pacchetto su GNU/Linux</title>
      <link>https://nexenne.com/it/blog/check_package_version_on_linux/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/check_package_version_on_linux/</guid>
      <pubDate>Tue, 09 Apr 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Un modo rapido per controllare la versione di un pacchetto installato senza eseguire direttamente il binario.]]></description>
      <category>GNU/Linux</category>
      <category>HowTo</category>
      <category>Administration</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Dopo la scoperta della backdoor in XZ Utils (<a href="https://nvd.nist.gov/vuln/detail/CVE-2024-3094">CVE-2024-3094</a>), in molti suggerivano di eseguire direttamente <code>xz --version</code> per controllare la versione installata.</p>
<p>Se si sospetta che un eseguibile possa essere compromesso, è preferibile non avviarlo nemmeno per conoscerne la versione. Il package manager può fornire la stessa informazione senza eseguire il binario.</p>
<p>Vediamo dunque come verificare correttamente la versione di un pacchetto installato su sistemi GNU/Linux.
Per comodità ho separato le distribuzioni in base al gestore dei pacchetti.</p>
<p>Tenete presente che i comandi seguenti filtrano esplicitamente i pacchetti con <code>xz</code> nel nome.</p>
<h2 id="debian">Debian</h2>
<p>Questi comandi funzionano su tutte le distribuzioni basate su Debian.</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>Questi comandi valgono per Fedora e per le distribuzioni che usano RPM e DNF.
Si può usare anche 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>Questi comandi funzionano anche per le distribuzioni basate su Arch.
Tra cui Manjaro, EndeavourOS e persino 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>I comandi sono validi anche per le distribuzioni basate su SUSE e openSUSE come GeckoLinux e Linux Kamarada.
Si può anche usare 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/it/blog/signals_and_slots/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/signals_and_slots/</guid>
      <pubDate>Mon, 01 Apr 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Partendo dal modello signal/slot di Qt, costruisco una piccola implementazione moderna in C++.]]></description>
      <category>C++</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Da quando ho iniziato a lavorare con Qt, mi sono chiesto come si potesse implementare un meccanismo signal/slot usando il C++ moderno.</p>
<p>Wikipedia lo definisce così.</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 termini più semplici, il meccanismo signal/slot consente agli oggetti di comunicare attraverso eventi.
Esistono già ottime librerie che implementano questo pattern; in questo articolo presento una piccola soluzione personale, costruita soprattutto per comprenderne meglio il funzionamento.</p>
<h2 id="diagramma-uml">Diagramma UML</h2>
<p>Il diagramma seguente riassume la struttura del progetto.</p>
<p><img src="/blog/images/signals_and_slots_uml.png" alt=""></p>
<h2 id="codice">Codice</h2>
<p>Poiché l'implementazione usa classi template, il codice si trova interamente in un file header. I commenti nel sorgente descrivono responsabilità e comportamento dei componenti principali.</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="utilizzo">Utilizzo</h2>
<p>Per comprendere come utilizzare la soluzione implementata vi mostro gli unit tests che ho scritto.</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="analisi">Analisi</h2>
<p>Le classi coinvolte sono:</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>Non serve analizzare ogni classe riga per riga; due elementi, però, meritano una spiegazione più attenta: TrackingSlot e Connection.</p>
<h3 id="classe-trackingslot">Classe TrackingSlot</h3>
<p>Questa classe permette, tramite un std::weak_ptr, di tracciare l'esistenza in memoria di un oggetto specifico.
Per questo la soluzione presuppone l'uso degli smart pointer, in particolare di un std::shared_ptr
per creare l'oggetto da tracciare.</p>
<p>Quando il weak_ptr non riesce più a eseguire il <code>lock</code>, significa che l'oggetto tracciato ha terminato il suo ciclo di vita.</p>
<p>Lo slot non viene rimosso immediatamente quando l'oggetto termina il suo ciclo di vita, ma al successivo <code>emit</code> del signal a cui è connesso.</p>
<p>Va chiarito che questa classe non modifica il ciclo di vita dell'oggetto tracciato.</p>
<h3 id="classe-connection">Classe Connection</h3>
<p>Questa classe non segue il pattern RAII.
Serve come punto di accesso esplicito alla connessione tra signal e slot.
Connection mantiene un riferimento, un weak_ptr, allo slot creato in seguito a una chiamata <code>signal.connect(...)</code>.</p>
<p>La classe può operare sulle seguenti funzioni dello slot:</p>
<ul>
<li>Disconnessione</li>
<li>Blocco/sblocco della ricezione di un'emissione di signal</li>
<li>Verifica dello stato dello slot</li>
</ul>
<p>Va chiarito che il ciclo di vita di questa classe non modifica il ciclo di vita dello slot.</p>
<h2 id="problemi-aperti">Problemi aperti</h2>
<p>Vi sono nell'immediato alcune lacune dal punto di vista funzionale, poiché:</p>
<ol>
<li>Non è thread safe.</li>
<li>Non posso disconnettermi direttamente dallo slot (immaginiamo un segnale che deve essere emesso una sola volta).</li>
<li>Overloading dei metodi.</li>
<li>Metodi con valori default.</li>
<li>Non c'è modo di recuperare il valore di ritorno degli slot.</li>
</ol>
<p>Il punto 1 non viene affrontato qui, data la complessità della gestione thread-safe.</p>
<p>Sul punto 4 non ho ancora trovato una soluzione soddisfacente.</p>
<p>Il problema 5 nasce solo per motivi progettuali, ho preferito non implementarlo perché superfluo per lo scopo della classe Signal.
Nella mia implementazione è possibile connettersi a slot con valore di ritorno, che vengono poi trasformati in slot con ritorno void.
Per implementare il valore di ritorno servirebbe un vettore di appoggio in cui salvare il ritorno di ogni slot e mettere a disposizione
dei metodi per leggerlo. Ogni emit sovrascriverebbe quel vettore.</p>
<h3 id="problema-disconnessione-diretta-dallo-slot">Problema: disconnessione diretta dallo slot</h3>
<p>La classe <code>Connection</code> risolve questo problema in modo diretto.
Basta aggiungere una classe ExtendedSlot che abbia come membro una variabile di tipo Connection, così da poterla iniettare
all'interno della funzione <code>invoke</code>. Tuttavia c'è un compromesso, ovvero dovremmo disporre di funzioni che abbiano come
parametro un tipo Connection.</p>
<p>Vediamo una possibile soluzione.</p>
<p>Aggiungiamo una classe ExtendedSlot.</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>Modifichiamo Signal e aggiungiamo un metodo connectExtended.</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>E per utilizzarlo avremo.</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>Rimane il limite di dover aggiungere sempre quel parametro di tipo Connection a ogni slot, per di più come primo argomento.</p>
<h3 id="problema-overloading">Problema: overloading</h3>
<p>Aggiungendo le seguenti funzioni di supporto possiamo risolvere anche questo caso.</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>Il parameter pack, una volta espanso, permette di identificare il metodo corretto in modo univoco:</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="conclusione">Conclusione</h2>
<p>Abbiamo visto come implementare un meccanismo di signal/slot.
La soluzione ha limiti evidenti, ma offre un'interfaccia d'uso semplice e senza meccanismi nascosti.</p>
<p>Con questa base possiamo implementare l'observer pattern in modo chiaro e controllabile.</p>
<p>A questo <a href="https://github.com/signorenne/cpp_playground/tree/main/signals_and_slots">link</a> trovate la cartella di progetto che potrete compilare con CMake.</p>]]></content:encoded>
    </item>
    <item>
      <title>Come strutturare un progetto C++</title>
      <link>https://nexenne.com/it/blog/cpp_project_structure/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/cpp_project_structure/</guid>
      <pubDate>Thu, 28 Mar 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Una struttura C++ ordinata, con librerie e applicazioni separate e una build più semplice da seguire.]]></description>
      <category>C++</category>
      <category>CMake</category>
      <category>Conan</category>
      <content:encoded><![CDATA[<h2 id="introduzione">Introduzione</h2>
<p>Prima o poi ogni progetto C++ deve fare i conti con CMake, dipendenze, librerie interne e applicazioni costruite sopra quelle librerie.
Nei miei primi progetti perdevo spesso troppo tempo a organizzare le directory e a mantenere coerenti i vari <code>CMakeLists.txt</code>.
Quando la struttura non è chiara, basta aggiungere un modulo o una dipendenza per rendere la build più fragile del necessario.</p>
<p>Dopo aver studiato le pratiche di altri sviluppatori, ho trovato una struttura che mi piace per semplicità e chiarezza.</p>
<p>Quella che segue non è l'unica soluzione possibile. È una struttura pensata per:</p>
<ul>
<li>Evitare schemi che causano conflitti.</li>
<li>Evitare di complicare la compilazione.</li>
<li>Semplificare la lettura.</li>
</ul>
<h2 id="strumenti-usati">Strumenti usati</h2>
<p>Per l'esempio userò tre strumenti:</p>
<h3 id="cmake">CMake</h3>
<blockquote>
<p>CMake è un software libero multipiattaforma per l'automazione dello sviluppo il cui nome è un'abbreviazione di cross platform make. Questo software nasce per rimpiazzare Automake nella generazione dei Makefile, cercando di essere più semplice da usare. Infatti, nella maggior parte dei progetti, non esiste un Makefile incluso nei sorgenti, dato che questo non è portabile.</p>
<p>-- Wikipedia</p>
</blockquote>
<p>Lo useremo per generare la build e compilare il progetto.</p>
<p>Non sarà una guida completa a CMake: qui mi interessa soprattutto mostrare come organizzare il progetto. Per approfondire, il riferimento migliore resta il <a href="https://cmake.org/">sito ufficiale di CMake</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>Lo useremo per gestire dipendenze e pacchetti.</p>
<p>Per approfondire, la documentazione parte dal <a href="https://conan.io/">sito ufficiale di Conan</a>.</p>
<p>Prima di procedere, vediamo i passaggi minimi per usarlo nel progetto.</p>
<p>Si inizia generando un profilo Conan, che descrive compilatore, configurazione di build, architettura e altre impostazioni dell'ambiente.</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>Al termine, sui sistemi Unix, nella home directory sarà presente la cartella <code>.conan2</code> con il profilo generato.</p>
<p>Per installare le dipendenze usiamo il comando seguente.</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 esegue due operazioni principali:</p>
<ul>
<li>Installa le librerie specificate nel <code>conanfile.txt</code> dal server remoto, in genere Conan Center, se disponibili.
Il server contiene sia le recipe Conan, che descrivono come costruire le librerie, sia i pacchetti binari riutilizzabili.</li>
<li>Genera diversi file nella directory <code>build</code>.
<ul>
<li>CMakeDeps genera i file necessari per far sì che CMake trovi le librerie che abbiamo scaricato.</li>
<li>CMakeToolchain genera un file toolchain per CMake per poter costruire il nostro progetto con CMake.</li>
</ul>
</li>
</ul>
<h3 id="doxygen">Doxygen</h3>
<blockquote>
<p>Doxygen è una applicazione per la generazione automatica della documentazione a partire dal codice sorgente di un generico software. È un progetto open source disponibile sotto licenza GPL, scritto per la maggior parte da Dimitri van Heesch a partire dal 1997.</p>
<p>-- Wikipedia</p>
</blockquote>
<p>Doxygen genera la documentazione a partire dai commenti presenti nel codice.</p>
<p>Anche qui non serve una guida completa: per i dettagli rimando al <a href="https://www.doxygen.nl/">sito ufficiale di Doxygen</a>.</p>
<h2 id="struttura">Struttura</h2>
<p>La struttura del progetto ha la seguente forma.</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>Vediamola più da vicino.</p>
<p>L'idea di fondo è separare i componenti del progetto per directory. Ogni cartella contiene un eseguibile o una libreria e definisce il relativo target.</p>
<p><code>standalone</code> è il target eseguibile e usa la libreria <code>libfoo</code>. Rappresenta un'applicazione costruita sopra una o più librerie principali.</p>
<p><code>libfoo</code> è una libreria statica usata da <code>standalone</code>, ma è organizzata come un componente autonomo, con interfaccia pubblica, implementazione, test e documentazione.
Ogni libreria dovrà:</p>
<ul>
<li>Seguire una struttura prevedibile.
<ul>
<li>Directory <code>include</code> per le dichiarazioni pubbliche, cioè l'interfaccia della libreria.</li>
<li>Directory <code>src</code> per le definizioni e gli header privati.</li>
</ul>
</li>
<li>Garantire la generazione della documentazione con Doxygen.</li>
<li>Fornire un ambiente di testing con doctest (o similari).</li>
</ul>
<p>L'immagine seguente mostra l'idea generale: gli eseguibili <code>app</code> usano librerie <code>core</code>, ma ogni componente rimane separato.</p>
<p><img src="/blog/images/cpp_project_structure_example_1.png" alt=""></p>
<h3 id="toplevel-cmakeliststxt">TopLevel CMakeLists.txt</h3>
<p>Il file <code>CMakeLists.txt</code> nella root contiene la configurazione principale del progetto.</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>Abbiamo un solo progetto e specifichiamo a CMake di ricercare altri file di configurazione nelle sottocartelle libfoo e standalone, rispettivamente "core" e "app".
Impostiamo quindi le opzioni comuni e usiamo <code>find_package</code> per individuare le dipendenze necessarie alla compilazione.</p>
<h3 id="gestione-dipendenze">Gestione dipendenze</h3>
<p>Il file <code>conanfile.txt</code> specifica i pacchetti che serviranno ai vari target del nostro progetto. Nel nostro caso <a href="https://github.com/fmtlib/fmt">fmt</a> viene usato sia da
libfoo che da standalone, <a href="https://github.com/doctest/doctest">doctest</a> viene richiesto da libfoo per gli unit tests.
Il contenuto di seguito.</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>Per usare Conan con CMake, utilizziamo il wrapper <a href="https://github.com/conan-io/cmake-conan">cmake-conan</a>, in particolare
siamo interessati al file <code>conan_provider.cmake</code> che salviamo nella root del progetto.
Questo file ci tornerà utile in seguito.</p>
<h3 id="target-libfoo">Target libfoo</h3>
<p>Il target libfoo che nel caso in analisi si presenta come una libreria statica, segue la forma canonica di una libreria standard.
Tornando al concetto di un target per subdirectory, il nostro CMakeLists.txt diventa questo.</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>Abbiamo tre sezioni:</p>
<ul>
<li>Definizione della libreria e della sua configurazione.</li>
<li>Impostazione e creazione del target per gli unit tests.</li>
<li>Abilitazione e configurazione di Doxygen per la generazione della documentazione.</li>
</ul>
<p>Per completezza, questo è il contenuto del <code>CMakeLists.txt</code> nella cartella <code>docs</code>.</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="target-standalone">Target standalone</h3>
<p>La configurazione del target <code>standalone</code> è più semplice:</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="compilazione">Compilazione</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>Con il primo comando eseguiamo la fase di configurazione out-of-tree, generando un sistema di compilazione.
Con il secondo costruiamo il progetto chiamando lo strumento di compilazione di sistema, make su Unix.</p>
<p>Ricordate il file <code>conan_provider.cmake</code> enunciato in precedenza?
Ebbene utilizzando l'impostazione <code>-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=conan_provider.cmake</code> nel processo di configurazione di cmake,
questo invoca automaticamente il comando <code>conan install</code> semplificandoci la gestione del progetto.</p>
<h2 id="conclusione">Conclusione</h2>
<p>Ora nella cartella build, sotto la root del progetto, troveremo i file binari e le librerie che abbiamo compilato, oltre alla documentazione
doxygen e all'eseguibile degli unit tests.</p>
<p>La struttura può essere estesa aggiungendo, per esempio, una pipeline di integrazione continua, altri eseguibili o nuove librerie.</p>
<p>Il <a href="https://github.com/signorenne/cpp_playground/tree/main/cpp_project_structure">repository di esempio</a> contiene il progetto completo descritto nell'articolo.</p>]]></content:encoded>
    </item>
    <item>
      <title>Preludio</title>
      <link>https://nexenne.com/it/blog/preludio/</link>
      <guid isPermaLink="true">https://nexenne.com/it/blog/preludio/</guid>
      <pubDate>Tue, 26 Mar 2024 12:00:00 GMT</pubDate>
      <description><![CDATA[Il primo appunto del sito: perché ho deciso di iniziare a scrivere pubblicamente.]]></description>
      <category>Aggiornamento</category>
      <content:encoded><![CDATA[<p>Benvenuti nel mio sito.</p>
<p>Per molto tempo mi sono chiesto se avessi davvero qualcosa di utile da scrivere. Alla fine sono arrivato a una conclusione semplice: senza iniziare non avrei mai potuto scoprirlo.</p>
<p>Questo è quindi il mio primo articolo pubblico.</p>
<p>Non contiene ancora un approfondimento tecnico, ma segna l'inizio di uno spazio in cui raccogliere ciò che imparo, i progetti su cui lavoro e le idee che meritano di essere sviluppate con più calma.</p>
<p>Scriverò soprattutto di informatica, sviluppo software e tecnologia, lasciando spazio anche ad appunti personali e ad altri argomenti che trovo interessanti.</p>
<p>Grazie per essere passati. Buona lettura.</p>
<p><em>Nicolò</em></p>]]></content:encoded>
    </item>
  </channel>
</rss>
