<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pigeon Codeur</title>
    <description>The latest articles on DEV Community by Pigeon Codeur (@pigeoncodeur).</description>
    <link>https://dev.to/pigeoncodeur</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2318375%2Faeee256c-f265-4d48-891d-291a3c8fb126.png</url>
      <title>DEV Community: Pigeon Codeur</title>
      <link>https://dev.to/pigeoncodeur</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pigeoncodeur"/>
    <language>en</language>
    <item>
      <title>How I Run Local AI with n8n on a Schedule (No Server, No API Costs)</title>
      <dc:creator>Pigeon Codeur</dc:creator>
      <pubDate>Wed, 25 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/pigeoncodeur/how-i-run-local-ai-with-n8n-on-a-schedule-no-server-no-api-costs-4d6g</link>
      <guid>https://dev.to/pigeoncodeur/how-i-run-local-ai-with-n8n-on-a-schedule-no-server-no-api-costs-4d6g</guid>
      <description>&lt;p&gt;&lt;em&gt;Most AI workflows run 24/7 and waste resources. Mine runs once a day, on my own machine, processes everything, and shuts down before I even wake up.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why always-on AI is wasteful
&lt;/h2&gt;

&lt;p&gt;A lot of AI workflows are set up to run continuously by default, but plenty of them don’t actually need real-time execution. If what you’re doing is batch-style monitoring, scraping, content processing, or scheduled analysis, keeping a server alive all day is mostly wasted uptime. You end up paying for idle compute, ongoing server costs, and infrastructure that just sits there between runs.&lt;/p&gt;

&lt;p&gt;That’s the problem this setup is meant to solve. Not every AI system needs to be on 24/7. For batch-style workflows, it often makes more sense to run on a schedule, finish the work, and shut down.&lt;/p&gt;

&lt;p&gt;I wanted to avoid paying for a server that would sit idle most of the day, so I started running everything on my own machine instead. Once I did that, the question changed. It stopped being “how do I keep this AI workflow running all the time?” and became “when does it actually need to run?” In my case, the answer was simple: wake up at 5AM, process everything in one batch, then shut down cleanly. That’s really the whole idea here. For a lot of AI automations, better timing beats more infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete example: my Reddit monitoring pipeline
&lt;/h2&gt;

&lt;p&gt;In my case, the pipeline is for Reddit monitoring. Once a day, n8n wakes up, fetches posts from the sources I care about, and runs them through a local AI step that filters noise and keeps the signal.&lt;/p&gt;

&lt;p&gt;I don’t need to watch Reddit in real time. I don’t need a system running all day to track every new post or comment. I just need one scheduled pass that gathers fresh content, sorts through the volume, and leaves me with a short list of posts worth reading.&lt;/p&gt;

&lt;p&gt;That’s why this works well as a batch job. Fetch posts, filter noise, keep signal.&lt;/p&gt;

&lt;p&gt;The workflow is simple once you see the shape of it: Reddit -&amp;gt; fetch, AI -&amp;gt; analyze/filter, Store -&amp;gt; results. First, the scheduled job starts the local model server. Then n8n fetches the Reddit posts, sends them through the model to filter out the noise, and keeps the useful items. After that, the workflow stores the results and stops the model cleanly.&lt;/p&gt;

&lt;p&gt;That order matters because the AI server only exists for the duration of the batch. Start the model, process everything in one pass, store the output, and shut it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat your machine like a scheduled worker
&lt;/h2&gt;

&lt;p&gt;The basic mental model is simple: let the machine wake up on a schedule, do the job, and shut down. Instead of keeping AI infrastructure alive all day, you bring your own computer online only when there’s work to do. That’s a much better fit for batch jobs, monitoring, scraping, and other tasks that don’t need constant uptime.&lt;/p&gt;

&lt;p&gt;This works best when the job already has a natural batch shape. Monitoring, scraping, and batch processing are good examples because they can run on a schedule, finish, and get out of the way. If a workflow only needs a daily insight, then daily compute is enough.&lt;/p&gt;

&lt;p&gt;There’s an obvious boundary here. This is not a fit for chat apps, live agents, or anything else that depends on low-latency interaction. Those workloads need something that stays responsive all the time. This setup is for doing one focused pass on a local machine, processing the data, and shutting everything down cleanly. Here’s your section rewritten with the &lt;strong&gt;actual working setup (Windows + n8n + schtasks + llama.cpp)&lt;/strong&gt; while keeping your tone and flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What n8n needs for this to work
&lt;/h2&gt;

&lt;p&gt;One n8n capability makes this possible: &lt;strong&gt;Execute Command&lt;/strong&gt;. You need it for local automation because it lets the workflow trigger processes on your own machine. In practical terms, this is what allows n8n to start your local LLM server at the right moment, use it during the workflow, and shut it down afterward.&lt;/p&gt;

&lt;p&gt;It’s disabled by default, so you have to enable it first. If you’re running n8n locally on Windows, set this environment variable before starting n8n:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;NODES_EXCLUDE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;n8n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That removes &lt;code&gt;executeCommand&lt;/code&gt; and &lt;code&gt;readWriteFile&lt;/code&gt; from the excluded nodes list and gives you what matters here: the ability to run local scripts from inside your workflow.&lt;/p&gt;

&lt;p&gt;This setup only works in the right environment. n8n Cloud won’t allow it, and Docker setups are usually restricted for this kind of direct system access. The configuration that works is a &lt;strong&gt;local n8n instance running on the same Windows machine&lt;/strong&gt; that will launch the LLM server, run the workflow, and shut it down again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting the local AI server from n8n
&lt;/h2&gt;

&lt;p&gt;Once n8n can execute commands locally, the next step is launching your LLM server on demand. In my case, that’s &lt;code&gt;llama.cpp&lt;/code&gt;, but the exact model doesn’t matter. What matters is the pattern: n8n triggers something, the server starts, and the rest of the workflow uses it.&lt;/p&gt;

&lt;p&gt;The important detail is that you don’t call the script directly from n8n. Instead, you wrap it in a &lt;strong&gt;Windows scheduled task&lt;/strong&gt; , and n8n only triggers that task.&lt;/p&gt;

&lt;p&gt;Here’s the actual start script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# start-llama.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="bp"&gt;$Error&lt;/span&gt;&lt;span class="n"&gt;ActionPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stop"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Z:\Ai\llama.cpp"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Join-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"build\bin\Release\llama-server.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$PidFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Join-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llama-server.pid"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Start-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-FilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-ArgumentList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-m models\mistral-7b.gguf --port 8081 -ngl 999 -np 1 -cb -fa --mlock --no-mmap -t 4"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-WorkingDirectory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-WindowStyle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Hidden&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# wait until server is ready&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$ready&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-lt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Start-Sleep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Seconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invoke-WebRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8081/health"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-UseBasicParsing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-TimeoutSec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StatusCode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$ready&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kr"&gt;break&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ready&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llama-server did not become ready"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# resolve PID after startup&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$proc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-CimInstance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Win32_Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llama-server.exe"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CommandLine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--port 8081"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-First&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProcessId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Set-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PidFile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script does three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;starts the server in the background,&lt;/li&gt;
&lt;li&gt;waits until it’s actually reachable via HTTP,&lt;/li&gt;
&lt;li&gt;saves the PID so it can be stopped later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That middle step is critical. Instead of guessing with a fixed delay, the script waits until the server is actually ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  The key technical constraint
&lt;/h2&gt;

&lt;p&gt;This was the part that broke my first version. n8n’s Execute Command node waits for the command to finish before continuing the workflow. That’s fine for short scripts, but a local LLM server is a long-running process by design.&lt;/p&gt;

&lt;p&gt;If you try to start the server directly from n8n, the workflow just sits there. From n8n’s point of view, the command never finishes, so nothing else runs.&lt;/p&gt;

&lt;p&gt;The problem isn’t the model. It’s that a server is not a one-shot command.&lt;/p&gt;

&lt;p&gt;That’s the core constraint: &lt;strong&gt;Execute Command expects something that exits, but your LLM server is meant to stay alive&lt;/strong&gt;. Until you handle that mismatch, the workflow stalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: delegate to Windows
&lt;/h2&gt;

&lt;p&gt;The fix was to stop letting n8n manage the process directly and let Windows handle it instead.&lt;/p&gt;

&lt;p&gt;Instead of calling the PowerShell script directly, I created a scheduled task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;schtasks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/TN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LlamaServerStart"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/TR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"\"&lt;/span&gt;&lt;span class="nx"&gt;C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe\&lt;/span&gt;&lt;span class="s2"&gt;" -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"&lt;/span&gt;&lt;span class="nx"&gt;Z:\Ai\llama.cpp\start-llama.ps1\&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/SC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ONCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/ST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;00:00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/F&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then from n8n, I only run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;schtasks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/Run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/TN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LlamaServerStart"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the key difference.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;schtasks /Run&lt;/code&gt; returns immediately&lt;/li&gt;
&lt;li&gt;Windows runs the script independently&lt;/li&gt;
&lt;li&gt;n8n doesn’t get stuck waiting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of trying to force a long-running server into a short-lived command model, I hand off execution to the OS and let n8n just trigger it.&lt;/p&gt;

&lt;p&gt;That’s what makes the workflow actually usable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shutting the server down cleanly
&lt;/h2&gt;

&lt;p&gt;Stopping the server uses the same pattern.&lt;/p&gt;

&lt;p&gt;First, the stop script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# stop-llama.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Z:\Ai\llama.cpp"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$PidFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Join-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llama-server.pid"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$pidValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PidFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SilentlyContinue&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pidValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$pidValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SilentlyContinue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Stop-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$pidValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Remove-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PidFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SilentlyContinue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create the task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;schtasks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/TN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LlamaServerStop"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/TR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"\"&lt;/span&gt;&lt;span class="nx"&gt;C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe\&lt;/span&gt;&lt;span class="s2"&gt;" -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"&lt;/span&gt;&lt;span class="nx"&gt;Z:\Ai\llama.cpp\stop-llama.ps1\&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/SC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ONCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/ST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;00:00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/F&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And call it from n8n:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;schtasks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/Run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/TN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LlamaServerStop"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why this setup works
&lt;/h2&gt;

&lt;p&gt;The key idea is simple once you see it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;n8n orchestrates&lt;/li&gt;
&lt;li&gt;Windows executes&lt;/li&gt;
&lt;li&gt;the LLM runs independently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The PID file connects start and stop, the HTTP check guarantees readiness, and &lt;code&gt;schtasks&lt;/code&gt; prevents n8n from blocking on a long-running process.&lt;/p&gt;

&lt;p&gt;That combination is what makes the start–use–stop cycle reliable.&lt;/p&gt;

&lt;p&gt;Without it, you’re fighting the execution model of n8n. With it, everything behaves like a normal service lifecycle: bring it up, use it, shut it down cleanly, and start fresh on the next run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 5AM is the real trick
&lt;/h2&gt;

&lt;p&gt;The timing is what makes this setup practical instead of just clever on paper. I scheduled mine for 5AM, when my machine is idle and nothing else I’m doing is competing with it. That gives the workflow a quiet compute window to start the local LLM server, run the batch job, and shut everything back down before the day begins.&lt;/p&gt;

&lt;p&gt;And 5AM is just my choice, not a rule. The broader idea is to schedule around your own idle time. For batch work, scraping, monitoring, or any once-a-day automation, that shift makes local compute a lot more practical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results and trade-offs
&lt;/h2&gt;

&lt;p&gt;What this setup buys me is pretty simple: zero infrastructure cost beyond the machine I already own, automated daily insights, and time saved. In practice, the machine wakes up, checks Reddit once a day, filters out the noise, and leaves me with a small set of posts worth reading. I don’t have to pay for an extra always-on server or babysit the process. It’s not magic, and it won’t fit every use case, but for a scheduled batch job like this, it’s genuinely useful.&lt;/p&gt;

&lt;p&gt;The trade-offs are real too. This only works if the machine is on at the scheduled time, so it’s best for a local-only setup where you control the hardware. There’s also some scripting complexity, since you need to start the LLM server in the background, track its process, and stop it cleanly afterward. That’s a good fit for batch jobs, monitoring, and other workflows that don’t need 24/7 uptime, but it’s not the right fit for everything.&lt;/p&gt;




&lt;p&gt;That’s the main lesson I took from this: most AI automations don’t need a bigger stack, a cloud bill, or a server running all day. They need better timing. If the job is batchable, run it in the quiet window, spin up the local model just long enough to do the work, then shut it down.&lt;/p&gt;

&lt;p&gt;Once you stop treating every workflow like a 24/7 service, the whole problem gets simpler. You don’t need more infrastructure. You need better timing.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>automatisation</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Persistent Storage in C++ Web Apps: WASMFS + OPFS with Emscripten</title>
      <dc:creator>Pigeon Codeur</dc:creator>
      <pubDate>Wed, 11 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/pigeoncodeur/persistent-storage-in-c-web-apps-wasmfs-opfs-with-emscripten-334h</link>
      <guid>https://dev.to/pigeoncodeur/persistent-storage-in-c-web-apps-wasmfs-opfs-with-emscripten-334h</guid>
      <description>&lt;h1&gt;
  
  
  How to use WASMFS to get persistent storage for a web app in C++
&lt;/h1&gt;

&lt;p&gt;Ever wanted to create an app in C++ and serve it on the web, but you need to store some data of the user and you can't find any guide on the web? Here I will show you how to use the OPFS backend of the new WASMFS storage layer for any app built with emscripten.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;I am making a game engine fully written in C++ and I want to use it to create some small scope games for game jams and such, so a web export is pretty much mandatory to have players test and play those games on itch. So for my web builds I naturally use emscripten for this.&lt;/p&gt;

&lt;p&gt;What is emscripten? Emscripten is a cross compiling tool that enables devs to port a C++ app to webassembly so that it can run in browsers. It works really well with my usecase as I use SDL as the backend for all of my render calls and input handling and it is backed into emscripten so it pretty much builds out of the box without doing too much handling on my part.&lt;/p&gt;

&lt;p&gt;But one of the major issues I faced using emscripten was the handling of files and more specifically the persistent storage of my saves. There are multiple ways given by this tool to access files. First, for all the static files (such as resources or shaders), you can precompile them directly into the binary with the linker flag &lt;code&gt;--preload-file&lt;/code&gt;. This directly compiles the files into the wasm and you can access them with any basic file accessor such as &lt;code&gt;fopen&lt;/code&gt; or &lt;code&gt;fstream&lt;/code&gt;. This works really well for all the files known at compile time and is quite easy to use for the tradeoff of having a bigger &lt;code&gt;.wasm&lt;/code&gt; size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;set_target_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; PROPERTIES
    LINK_FLAGS &lt;span class="s2"&gt;"--preload-file res \
                --preload-file shader \
                --preload-file scripts"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those files get baked in and are read-only, which is fine for shaders, scripts or any asset that doesn't change at runtime.&lt;/p&gt;

&lt;p&gt;The main issue comes from files that are not present during compile time or need to evolve during runtime such as &lt;strong&gt;save&lt;/strong&gt; files. For those we need to use a different kind of file handling.&lt;/p&gt;

&lt;p&gt;Thats where WASMFS comes into play.&lt;/p&gt;

&lt;h2&gt;
  
  
  WASMFS
&lt;/h2&gt;

&lt;p&gt;So if you skim through the emscripten docs, you will see that there are a lot of different ways to access, manage and store files in emscripten. For a quick runthrough you have access to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IDBFS&lt;/strong&gt; — the original persistent storage backend, backed by the browser's IndexedDB. It works but requires you to synchronize explicitly using &lt;code&gt;EM_ASM&lt;/code&gt; and JavaScript callbacks every time you want to flush data to disk. It's now being deprecated in favor of WASMFS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PROXYFS&lt;/strong&gt; — lets you proxy filesystem calls to another Emscripten worker or shared memory. Useful for sharing a filesystem between workers but not really relevant if you just want to persist save data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WASMFS&lt;/strong&gt; — the new filesystem layer. It's a fully C++ API, supports multiple backends (including OPFS), and handles threading out of the box. This is the one we want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here the two mains filesystem solutions that interest us are the IDBFS backend and the newer WASMFS + OPFS backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why WASMFS over IDBFS
&lt;/h2&gt;

&lt;p&gt;First, IDBFS is being deprecated for the newer WASMFS so if you want to future-proof your app and you don't know which to choose, it is better to onboard with the more user friendly WASMFS. Next is a simpler and fully C++ API for WASMFS so the integration is faster and easier with existing C++ code, whereas before the IDBFS functions like mounting or flushing a directory required you to drop into &lt;code&gt;EM_ASM&lt;/code&gt; and JavaScript callbacks to manage the filesystem. Finally you get better threading support as WASMFS supports it out of the box, something that was not supported by the previous implementation.&lt;/p&gt;

&lt;p&gt;That said, be careful: WASMFS still doesn't have support for all backends, so if you are targeting browsers that don't support OPFS you will still need to work with the old API if you want to have access to persistent storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get into it
&lt;/h2&gt;

&lt;p&gt;Now that we know why we want WASMFS, let's go through the actual setup step by step.&lt;/p&gt;

&lt;h3&gt;
  
  
  CMake configuration
&lt;/h3&gt;

&lt;p&gt;So first things first, before you can even think about mounting OPFS you need to have the right linker flags in your CMake. The three that matter here are &lt;code&gt;-sWASMFS&lt;/code&gt; to enable the new filesystem layer, &lt;code&gt;-s FORCE_FILESYSTEM=1&lt;/code&gt; so the filesystem actually gets linked even if emscripten doesn't detect any explicit FS calls in your code, and then &lt;code&gt;-pthread&lt;/code&gt; with &lt;code&gt;-sPTHREAD_POOL_SIZE&lt;/code&gt; for threading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;set_target_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; PROPERTIES
    LINK_FLAGS &lt;span class="s2"&gt;"-sWASMFS \
                -s FORCE_FILESYSTEM=1 \
                -sPTHREAD_POOL_SIZE=4 \
                -pthread \
                -s ALLOW_MEMORY_GROWTH=1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing to be careful about with the thread pool size: WASMFS internally defers its async operations to a background thread, so you always need to account for that extra thread on top of whatever your app already uses. If you don't allocate enough threads your app can just stall waiting on IO with no obvious error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mounting the OPFS backend
&lt;/h3&gt;

&lt;p&gt;Now for the actual mounting, first you need to include the wasmfs header, under an &lt;code&gt;__EMSCRIPTEN__&lt;/code&gt; guard of course:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#ifdef __EMSCRIPTEN__
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;emscripten/wasmfs.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#endif
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then before you start your main loop you create the backend and mount it to a directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#ifdef __EMSCRIPTEN__
&lt;/span&gt;    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;savePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;saveFolder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// e.g. "/save"&lt;/span&gt;
    &lt;span class="n"&gt;backend_t&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wasmfs_create_opfs_backend&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wasmfs_create_directory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_str&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mo"&gt;0777&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;errno&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;EEXIST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Warning: OPFS mount returned %d (errno=%d)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errno&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to watch out for here. First the path needs to start with &lt;code&gt;/&lt;/code&gt;, that's just how emscripten's virtual filesystem works, the root is always &lt;code&gt;/&lt;/code&gt;. On desktop you use relative paths but on web everything has to be absolute. Second don't worry about &lt;code&gt;EEXIST&lt;/code&gt; in your error check, that just means the directory was already there from a previous session which is actually the happy path for save data.&lt;/p&gt;

&lt;p&gt;Since the path format differs between builds you need a small conditional when you construct it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;constructSavePath&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="cp"&gt;#ifdef __EMSCRIPTEN__
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;saveFolder&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;saveSystemFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#else
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;saveFolder&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;saveSystemFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On desktop you get &lt;code&gt;save/system.sz&lt;/code&gt;, on web &lt;code&gt;/save/system.sz&lt;/code&gt;. Same logical location, just the web one maps into the OPFS-backed virtual directory you just mounted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading and writing files
&lt;/h3&gt;

&lt;p&gt;This is actually the nicest part of WASMFS compared to IDBFS: once the backend is mounted you don't need any special API to read or write to it. Standard C++ file I/O just works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Write&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ofstream&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"/save/system.sz"&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Read&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ifstream&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"/save/system.sz"&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;istreambuf_iterator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                     &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;istreambuf_iterator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WASMFS transparently routes anything under &lt;code&gt;/save/&lt;/code&gt; to the OPFS backend and persists it across sessions. No manual flush, no sync call, no JS callbacks. That's pretty much the whole point of switching to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Saving on browser lifecycle events
&lt;/h3&gt;

&lt;p&gt;One thing that can catch you off guard: the browser doesn't guarantee your writes make it through if the user just slams the tab closed. To cover that you need to hook into two browser lifecycle events.&lt;/p&gt;

&lt;p&gt;The first is &lt;code&gt;visibilitychange&lt;/code&gt; which fires when the tab becomes hidden, so things like switching tabs or minimizing the window. The second is &lt;code&gt;beforeunload&lt;/code&gt; which fires synchronously right before the page unloads on refresh or close. Together they cover pretty much all the cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#ifdef __EMSCRIPTEN__
&lt;/span&gt;    &lt;span class="c1"&gt;// Tab hidden: user switched tabs, minimized, etc.&lt;/span&gt;
    &lt;span class="n"&gt;emscripten_set_visibilitychange_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;EmscriptenVisibilityChangeEvent&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EM_BOOL&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;saveNow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;EM_TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Page close / refresh&lt;/span&gt;
    &lt;span class="n"&gt;emscripten_set_beforeunload_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;saveNow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// nullptr = no "Are you sure?" dialog&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;beforeunload&lt;/code&gt; fires synchronously so a blocking write is fine there. &lt;code&gt;visibilitychange&lt;/code&gt; is the one that covers mobile browsers where &lt;code&gt;beforeunload&lt;/code&gt; is unreliable, so you really want both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Threading and initialization order
&lt;/h3&gt;

&lt;p&gt;The last piece of the puzzle is the initialization order, and this one matters more than it looks. The full sequence needs to go like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exec()
  ├─ wasmfs_create_opfs_backend() ← must happen before the main loop
  ├─ wasmfs_create_directory(...)
  ├─ SDL_Init(...)
  ├─ SDL_CreateWindow(...)
  ├─ std::thread initThread { initializeWindow() } ← window/ECS init in background
  └─ emscripten_set_main_loop_arg(mainLoopCallback) ← hands control to the browser

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OPFS mount has to happen before &lt;code&gt;emscripten_set_main_loop_arg&lt;/code&gt; because WASMFS internally creates promises that need a running browser event loop to resolve. If you mount after handing control to the browser you can end up in a situation where the promises never resolve.&lt;/p&gt;

&lt;p&gt;The window initialization goes into a background thread so the main loop can start right away and give those promises room to process. Then in the main loop callback you just check an atomic flag before doing anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;mainLoopCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Engine&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;windowReady&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// still waiting for initThread&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;initialized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;initializeECS&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;initialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// normal render / event loop&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what avoids the deadlock where all your pthread pool threads are sitting blocked on OPFS operations with nobody left to run the event loop and resolve the pending promises. If you ever find your app just hanging on startup with no error, this is probably why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;That's all there is to it. WASMFS with the OPFS backend is honestly a much cleaner solution than what came before — no JS callbacks, no manual syncing, just standard C++ file I/O that persists across sessions. The only real gotchas are the thread pool size and the initialization order, and now you know about both.&lt;/p&gt;

&lt;p&gt;If this was useful to you or you ran into a different issue with the setup feel free to leave a comment, I'd be happy to extend the article with more edge cases.&lt;/p&gt;




&lt;p&gt;If you are curious about the engine this was built for, PgEngine is a C++ game engine targeting both desktop and web. You can check it out and leave a star on the repo here: &lt;a href="https://github.com/gallasko/ColumbaEngine" rel="noopener noreferrer"&gt;ColumbaEngine repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Live demos at: &lt;a href="https://columbaengine.org/demos" rel="noopener noreferrer"&gt;columbaengine.org/demos&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discord:&lt;/strong&gt; &lt;a href="https://discord.gg/un4VtehX3W" rel="noopener noreferrer"&gt;Join our community&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you played one of the games made with it and want to leave some feedback or just say hi, you can find them on my itch page here: &lt;a href="https://pigeoncodeur.itch.io/" rel="noopener noreferrer"&gt;Itch&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>wasmfs</category>
      <category>emscripten</category>
      <category>web</category>
    </item>
    <item>
      <title>CMake for Complex Projects (Part 1): Building a C++ Game Engine from Scratch for Desktop and WebAssembly</title>
      <dc:creator>Pigeon Codeur</dc:creator>
      <pubDate>Thu, 07 Aug 2025 09:42:06 +0000</pubDate>
      <link>https://dev.to/pigeoncodeur/cmake-for-complex-projects-part-1-building-a-c-game-engine-from-scratch-for-desktop-and-ied</link>
      <guid>https://dev.to/pigeoncodeur/cmake-for-complex-projects-part-1-building-a-c-game-engine-from-scratch-for-desktop-and-ied</guid>
      <description>&lt;p&gt;&lt;em&gt;A practical guide to modern CMake through a real-world C++ game engine project - Compilation &amp;amp; Build Management&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;If you've ever tried to build a C++ project with multiple dependencies and cross-platform support, you know the pain. Makefiles become unwieldy, Visual Studio solutions don't work on Linux, and don't even get me started on trying to distribute your library for others to use.&lt;/p&gt;

&lt;p&gt;This is where CMake shines. But most CMake tutorials show you toy examples with a single &lt;code&gt;hello.cpp&lt;/code&gt; file. Today, we're going deep with a &lt;strong&gt;complex project&lt;/strong&gt; - a complete game engine called ColumbaEngine (source code available &lt;a href="https://github.com/Gallasko/ColumbaEngine" rel="noopener noreferrer"&gt;here&lt;/a&gt;) with 80+ source files, multiple platforms (including web compilation), and a sophisticated build system.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;Part 1&lt;/strong&gt; of a two-part series. In this article, we'll focus on the &lt;strong&gt;compilation and build management&lt;/strong&gt; aspects: setting up the project, handling dependencies, cross-platform builds, and testing. In Part 2, we'll cover the &lt;strong&gt;deployment side&lt;/strong&gt;: installation systems, package generation, and distribution.&lt;/p&gt;

&lt;p&gt;By the end of this article, you'll understand how to structure CMake for medium to large projects, handle complex dependencies, and create professional build systems that compile reliably across platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes This Project Interesting?
&lt;/h2&gt;

&lt;p&gt;Before we dive into CMake, let's understand what we're building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;80+ C++ source files&lt;/strong&gt; organized in modules (ECS, Renderer, Audio, UI, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform&lt;/strong&gt;: Windows, Linux, and Web (via Emscripten)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple dependencies&lt;/strong&gt;: SDL2, OpenGL, FreeType, custom libraries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Installable library&lt;/strong&gt;: Other projects can use it with &lt;code&gt;find_package()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example applications&lt;/strong&gt;: Several games and tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package generation&lt;/strong&gt;: Creates &lt;code&gt;.deb&lt;/code&gt;, &lt;code&gt;.rpm&lt;/code&gt;, and other installers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't a toy project - it's a real game engine that needs a robust build system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure: The Foundation
&lt;/h2&gt;

&lt;p&gt;Here's how our game engine is organized:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ColumbaEngine/
├── CMakeLists.txt              # Main build file
├── src/
│   ├── Engine/                 # Core engine code
│   │   ├── ECS/               # Entity-Component-System
│   │   ├── Renderer/          # Graphics rendering
│   │   └── Audio/             # Sound system
│   └── main.cpp               # Editor application
├── examples/                   # Example games
├── import/                     # Vendored dependencies
├── cmake/                      # CMake modules
└── test/                      # Unit tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CMakeLists.txt&lt;/code&gt; is our blueprint. Let's build it step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Project Setup and Configuration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;cmake_minimum_required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;VERSION 3.18&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine VERSION 1.0&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# User-configurable options&lt;/span&gt;
&lt;span class="nb"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;BUILD_EXAMPLES &lt;span class="s2"&gt;"Build example applications"&lt;/span&gt; ON&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;BUILD_STATIC_LIB &lt;span class="s2"&gt;"Build static library only"&lt;/span&gt; OFF&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ENABLE_TIME_TRACE &lt;span class="s2"&gt;"Add -ftime-trace to Clang builds"&lt;/span&gt; OFF&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Global settings&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_EXPORT_COMPILE_COMMANDS ON&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# For IDE support&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_CXX_STANDARD 17&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_CXX_STANDARD_REQUIRED True&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key concepts here:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cmake_minimum_required&lt;/code&gt;&lt;/strong&gt;: We use 3.18+ for modern features like precompiled headers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;option()&lt;/code&gt;&lt;/strong&gt;: Creates user-configurable boolean flags. Users can run &lt;code&gt;cmake -DBUILD_EXAMPLES=OFF ..&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CMAKE_EXPORT_COMPILE_COMMANDS&lt;/code&gt;&lt;/strong&gt;: Generates &lt;code&gt;compile_commands.json&lt;/code&gt; for IDEs and tools like clangd&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: The Dependency Challenge
&lt;/h2&gt;

&lt;p&gt;Real projects have dependencies. Lots of them. Our game engine needs SDL2 for windowing, FreeType for text rendering, OpenGL for graphics, and more.&lt;/p&gt;

&lt;p&gt;We use a &lt;strong&gt;vendoring approach&lt;/strong&gt; - including dependencies directly in our source tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Dependency paths - everything is self-contained&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;SDL2_DIR &lt;span class="s2"&gt;"import/SDL2-2.28.5"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;SDL2MIXER_DIR &lt;span class="s2"&gt;"import/SDL2_mixer-2.6.3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;GLM_DIR &lt;span class="s2"&gt;"import/glm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;TASKFLOW_DIR &lt;span class="s2"&gt;"import/taskflow-3.6.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;GTEST_DIR &lt;span class="s2"&gt;"import/googletest-1.14.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Configure dependencies before building them&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;TF_BUILD_EXAMPLES OFF&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# Don't build taskflow examples&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;TF_BUILD_TESTS OFF&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# Don't build taskflow tests&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;BUILD_SHARED_LIBS OFF&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# Force static linking&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;SDL2_DISABLE_INSTALL ON&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# We'll handle installation ourselves&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why vendor dependencies?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Pros&lt;/strong&gt;: Exact version control, works offline, simplified build&lt;br&gt;
❌ &lt;strong&gt;Cons&lt;/strong&gt;: Larger repo size, manual updates&lt;/p&gt;

&lt;p&gt;For a game engine where stability is crucial, vendoring makes sense. For web services that need frequent security updates, you might prefer package managers.&lt;/p&gt;


&lt;h3&gt;
  
  
  Deep Dive: Dependency Management Strategies
&lt;/h3&gt;

&lt;p&gt;Choosing how to handle dependencies is one of the most critical architectural decisions in C++. Let's compare the approaches:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Vendoring (Our Current Approach)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Everything is self-contained in import/&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;SDL2_DIR &lt;span class="s2"&gt;"import/SDL2-2.28.5"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;GLM_DIR &lt;span class="s2"&gt;"import/glm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SDL2_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GLM_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reproducible builds&lt;/strong&gt;: Exact same versions everywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline builds&lt;/strong&gt;: No internet required after initial clone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control&lt;/strong&gt;: Can patch dependencies if needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: No external tools or package managers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository size&lt;/strong&gt;: Our import/ folder is 500MB+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update overhead&lt;/strong&gt;: Manual process to update dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Must manually track and update vulnerable dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License complexity&lt;/strong&gt;: Must distribute all licenses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Package Managers (Conan, vcpkg)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# With Conan&lt;/span&gt;
&lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;conan&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;conan_cmake_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;REQUIRES 
    SDL2/2.28.5
    glm/0.9.9.8
    BASIC_SETUP CMAKE_TARGETS
    BUILD missing
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PRIVATE CONAN_PKG::SDL2 CONAN_PKG::glm&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smaller repos&lt;/strong&gt;: Dependencies downloaded on-demand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy updates&lt;/strong&gt;: &lt;code&gt;conan install --update&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Binary packages&lt;/strong&gt;: Pre-built binaries for faster builds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ecosystem&lt;/strong&gt;: Thousands of packages available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;External dependency&lt;/strong&gt;: Requires internet and package manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version conflicts&lt;/strong&gt;: Diamond dependency problems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform support&lt;/strong&gt;: Not all packages support all platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning curve&lt;/strong&gt;: Another tool to learn and maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. FetchContent (Modern CMake)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;FetchContent&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;FetchContent_Declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;glm
    GIT_REPOSITORY https://github.com/g-truc/glm.git
    GIT_TAG 0.9.9.8
    GIT_SHALLOW TRUE
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;FetchContent_Declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;SDL2
    URL https://github.com/libsdl-org/SDL/releases/download/release-2.28.5/SDL2-2.28.5.tar.gz
    URL_HASH SHA256=332cb37d0be20cb9541739c61f79bae5a477427d79ae85e352089afdaf6666e4
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;FetchContent_MakeAvailable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;glm SDL2&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PRIVATE glm SDL2::SDL2&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CMake native&lt;/strong&gt;: No external tools required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible sources&lt;/strong&gt;: Git repos, URLs, local paths&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version pinning&lt;/strong&gt;: Exact commits/tags&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform&lt;/strong&gt;: Works everywhere CMake works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build time&lt;/strong&gt;: Downloads and builds on first configure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internet required&lt;/strong&gt;: At least for first build&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No binary caching&lt;/strong&gt;: Always builds from source&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hybrid Approach: The Best of Both Worlds
&lt;/h3&gt;

&lt;p&gt;For ColumbaEngine, we could evolve to a hybrid approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Critical, stable dependencies: Vendor them&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;SDL2_DIR &lt;span class="s2"&gt;"import/SDL2-2.28.5"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SDL2_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Development/testing dependencies: FetchContent&lt;/span&gt;
&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;BUILD_TESTS&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;FetchContent&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;FetchContent_Declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;googletest
        GIT_REPOSITORY https://github.com/google/googletest.git
        GIT_TAG v1.14.0
        GIT_SHALLOW TRUE
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;FetchContent_MakeAvailable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;googletest&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Optional dependencies: find_package with fallback&lt;/span&gt;
&lt;span class="nb"&gt;find_package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;Doxygen&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;NOT Doxygen_FOUND AND ENABLE_DOCS&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;FetchContent_Declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;doxygen
        URL https://github.com/doxygen/doxygen/releases/download/Release_1_9_8/doxygen-1.9.8.src.tar.gz
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;FetchContent_MakeAvailable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;doxygen&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Cross-Platform Reality Check
&lt;/h2&gt;

&lt;p&gt;Our engine supports three platforms, with different flags and need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native&lt;/strong&gt; (Windows/Linux): Build everything from source&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emscripten&lt;/strong&gt; (Web): Use system-provided libraries
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_SYSTEM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; MATCHES &lt;span class="s2"&gt;"Emscripten"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Web build - use Emscripten's built-in libraries&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;USE_FLAGS &lt;span class="s2"&gt;"-O3 -sUSE_SDL=2 -sUSE_SDL_MIXER=2 -sUSE_FREETYPE=1 -fwasm-exceptions"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_EXE_LINKER_FLAGS &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_EXE_LINKER_FLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;USE_FLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;else&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Native build - compile dependencies from source&lt;/span&gt;
    &lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SDL2_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SDL2MIXER_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GLEW_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TTF_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# GLM is header-only, works everywhere&lt;/span&gt;
&lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GLM_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Platform detection patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CMAKE_SYSTEM_NAME&lt;/code&gt;: Target platform (Windows, Linux, Darwin, Emscripten)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CMAKE_HOST_SYSTEM&lt;/code&gt;: Build machine platform&lt;/li&gt;
&lt;li&gt;Use generator expressions for conditional compilation: &lt;code&gt;$&amp;lt;$&amp;lt;PLATFORM_ID:Windows&amp;gt;:WIN32_CODE&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Creating the Main Target
&lt;/h2&gt;

&lt;p&gt;Now for the heart of our build - the engine library itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# List all source files (80+ files in our case!)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ENGINESOURCE
    src/Engine/window.cpp
    src/Engine/configuration.cpp
    src/Engine/logger.cpp
    &lt;span class="c1"&gt;# ... 70+ more files&lt;/span&gt;
    src/Engine/UI/uisystem.cpp
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create the static library&lt;/span&gt;
&lt;span class="nb"&gt;add_library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine STATIC &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ENGINESOURCE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Modern CMake magic - precompiled headers for faster builds&lt;/span&gt;
&lt;span class="nb"&gt;target_precompile_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PUBLIC src/Engine/stdafx.h&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Precompiled headers&lt;/strong&gt; can cut build times by 50%+ in large projects. They compile commonly used headers once and reuse the result.&lt;/p&gt;




&lt;h3&gt;
  
  
  Precompiled Headers - The Build Speed Game Changer
&lt;/h3&gt;

&lt;p&gt;Precompiled headers (PCH) are one of the most impactful optimizations for C++ build times, yet they're often overlooked. Let's understand how they work:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;: Header Parsing Overhead&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Every .cpp file typically includes these&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;    // ~15,000 lines when fully expanded  &lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;      // ~8,000 lines&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;      // ~12,000 lines&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;memory&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;      // ~6,000 lines&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;SDL2/SDL.h&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;  // ~25,000 lines&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// Total: ~66,000 lines to parse per source file!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With 80 source files, that's &lt;strong&gt;5.2 million lines&lt;/strong&gt; of redundant header parsing!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution&lt;/strong&gt;: Precompiled Headers&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;target_precompile_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PUBLIC src/Engine/stdafx.h&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;stdafx.h&lt;/code&gt; contains the most commonly used headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// stdafx.h - precompiled header&lt;/span&gt;
&lt;span class="cp"&gt;#pragma once
&lt;/span&gt;
&lt;span class="c1"&gt;// Standard library&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;memory&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;unordered_map&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;algorithm&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// Third-party libraries&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;SDL2/SDL.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;glm/glm.hpp&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;glm/gtc/matrix_transform.hpp&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// Engine fundamentals used everywhere&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"types.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"logger.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"configuration.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How It Works&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Compilation&lt;/strong&gt;: CMake compiles &lt;code&gt;stdafx.h&lt;/code&gt; once into &lt;code&gt;stdafx.h.gch&lt;/code&gt; (GCC) or &lt;code&gt;stdafx.pch&lt;/code&gt; (MSVC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reuse&lt;/strong&gt;: Every source file automatically uses the precompiled version&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: 66,000 lines → 0 lines to parse per file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Build Time Results&lt;/strong&gt; (80 source files):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Without PCH&lt;/strong&gt;: ~8 minutes clean build&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With PCH&lt;/strong&gt;: ~3 minutes clean build
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental&lt;/strong&gt;: Single file changes drop from 15s to 3s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;PUBLIC vs PRIVATE PCH&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# PUBLIC: Users of your library get the PCH benefits too&lt;/span&gt;
&lt;span class="nb"&gt;target_precompile_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PUBLIC stdafx.h&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# PRIVATE: Only internal compilation uses PCH&lt;/span&gt;
&lt;span class="nb"&gt;target_precompile_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PRIVATE stdafx.h&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best Practices for PCH&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Good PCH content: Stable, frequently used headers&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;          // Used in 90% of files&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;SDL2/SDL.h&amp;gt;&lt;/span&gt;&lt;span class="c1"&gt;      // Core dependency&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// Bad PCH content: Frequently changing headers  &lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"GameLogic.h"&lt;/span&gt;&lt;span class="c1"&gt;     // Changes often, invalidates PCH&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"debug_temp.h"&lt;/span&gt;&lt;span class="c1"&gt;    // Temporary debugging code&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PCH Pitfalls&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Overuse&lt;/strong&gt;: Adding changing headers defeats the purpose&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform differences&lt;/strong&gt;: MSVC vs GCC handle PCH differently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;: PCH creates implicit dependencies between files&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 5: Usage Requirements - The Secret Sauce
&lt;/h2&gt;

&lt;p&gt;This is where modern CMake really shines. Instead of users manually figuring out include paths and linking, we specify &lt;strong&gt;usage requirements&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;target_include_directories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PUBLIC
    &lt;span class="c1"&gt;# When building this library&lt;/span&gt;
    $&amp;lt;BUILD_INTERFACE:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_CURRENT_SOURCE_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/src/Engine&amp;gt;
    $&amp;lt;BUILD_INTERFACE:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_CURRENT_SOURCE_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/src/GameElements&amp;gt;
    &lt;span class="c1"&gt;# When using installed version&lt;/span&gt;
    $&amp;lt;INSTALL_INTERFACE:include/ColumbaEngine&amp;gt;
    $&amp;lt;INSTALL_INTERFACE:include/ColumbaEngine/GameElements&amp;gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Generator expressions&lt;/strong&gt; (&lt;code&gt;$&amp;lt;...&amp;gt;&lt;/code&gt;) are evaluated during build generation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BUILD_INTERFACE&lt;/code&gt;: Only when building this project&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INSTALL_INTERFACE&lt;/code&gt;: Only when using the installed version&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$&amp;lt;CONFIG:Debug&amp;gt;&lt;/code&gt;: Only in Debug builds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$&amp;lt;PLATFORM_ID:Windows&amp;gt;&lt;/code&gt;: Only on Windows&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Why Generator Expressions Matter
&lt;/h3&gt;

&lt;p&gt;Let's understand why this is so powerful. Consider this traditional approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The old, problematic way&lt;/span&gt;
&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_BUILD_TYPE STREQUAL &lt;span class="s2"&gt;"Debug"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_CXX_FLAGS &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_CXX_FLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -DDEBUG_MODE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problems with this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build-time evaluation&lt;/strong&gt;: The condition is checked when CMake configures, not when you build&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global pollution&lt;/strong&gt;: Affects ALL targets in the project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-config generators&lt;/strong&gt;: Breaks with Visual Studio, Xcode which support multiple configs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Generator expressions solve this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Modern, correct approach&lt;/span&gt;
&lt;span class="nb"&gt;target_compile_definitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyTarget PRIVATE 
    $&amp;lt;$&amp;lt;CONFIG:Debug&amp;gt;:DEBUG_MODE&amp;gt;
    $&amp;lt;$&amp;lt;CONFIG:Release&amp;gt;:NDEBUG&amp;gt;
    $&amp;lt;$&amp;lt;PLATFORM_ID:Windows&amp;gt;:WIN32_LEAN_AND_MEAN&amp;gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is evaluated &lt;strong&gt;per-target&lt;/strong&gt;, &lt;strong&gt;per-configuration&lt;/strong&gt;, &lt;strong&gt;at build time&lt;/strong&gt;. Much more flexible and robust.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Generator Expression Patterns
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Conditional compilation flags&lt;/span&gt;
&lt;span class="nb"&gt;target_compile_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyTarget PRIVATE
    $&amp;lt;$&amp;lt;CXX_COMPILER_ID:GNU,Clang&amp;gt;:-Wall -Wextra&amp;gt;
    $&amp;lt;$&amp;lt;CXX_COMPILER_ID:MSVC&amp;gt;:/W4&amp;gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Debug vs Release libraries&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyTarget PRIVATE
    $&amp;lt;$&amp;lt;CONFIG:Debug&amp;gt;:MyLib_d&amp;gt;
    $&amp;lt;$&amp;lt;CONFIG:Release&amp;gt;:MyLib&amp;gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Platform-specific linking&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyTarget PRIVATE
    $&amp;lt;$&amp;lt;PLATFORM_ID:Windows&amp;gt;:ws2_32&amp;gt;
    $&amp;lt;$&amp;lt;PLATFORM_ID:Linux&amp;gt;:pthread&amp;gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Dependency Linking - Getting Scope Right
&lt;/h2&gt;

&lt;p&gt;Here's a crucial concept: &lt;strong&gt;PUBLIC vs PRIVATE vs INTERFACE&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_SYSTEM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; MATCHES &lt;span class="s2"&gt;"Emscripten"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Web build&lt;/span&gt;
    &lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PUBLIC glm&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;else&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Native build - note the scoping!&lt;/span&gt;
    &lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PUBLIC 
        SDL2::SDL2-static      &lt;span class="c1"&gt;# Users need SDL2 headers&lt;/span&gt;
        glm                    &lt;span class="c1"&gt;# Header-only math library&lt;/span&gt;
        OpenGL::GL            &lt;span class="c1"&gt;# Graphics API&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PRIVATE 
        libglew_static        &lt;span class="c1"&gt;# Internal OpenGL extension loading&lt;/span&gt;
        freetype              &lt;span class="c1"&gt;# Internal text rendering&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Scope meanings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PUBLIC&lt;/strong&gt;: "I use this, and my users will need it too"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PRIVATE&lt;/strong&gt;: "I use this internally, but my users don't need to know"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INTERFACE&lt;/strong&gt;: "I don't use this, but my users will need it"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get this right, and users of your library automatically get the right dependencies. Get it wrong, and you'll have angry developers filing issues.&lt;/p&gt;




&lt;h3&gt;
  
  
  Understanding Transitive Dependencies
&lt;/h3&gt;

&lt;p&gt;Let's look at a real example from our engine. Imagine this dependency chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyGame → ColumbaEngine → SDL2 → OpenGL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we declare SDL2 as &lt;code&gt;PRIVATE&lt;/code&gt; to ColumbaEngine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PRIVATE SDL2::SDL2-static&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: &lt;code&gt;MyGame&lt;/code&gt; won't be able to use SDL2 types in the public API. If ColumbaEngine's headers include &lt;code&gt;&amp;lt;SDL2/SDL.h&amp;gt;&lt;/code&gt;, users get compile errors.&lt;/p&gt;

&lt;p&gt;If we declare SDL2 as &lt;code&gt;PUBLIC&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine PUBLIC SDL2::SDL2-static&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: &lt;code&gt;MyGame&lt;/code&gt; automatically gets SDL2 headers and libraries. Perfect for our engine where users need SDL2 types.&lt;/p&gt;

&lt;h3&gt;
  
  
  The INTERFACE Scope Mystery
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;INTERFACE&lt;/code&gt; is the trickiest to understand. It means "I don't use this, but my users will."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world example&lt;/strong&gt;: Header-only libraries with dependencies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# HeaderOnlyMath library depends on Eigen, but is itself header-only&lt;/span&gt;
&lt;span class="nb"&gt;add_library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;HeaderOnlyMath INTERFACE&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;HeaderOnlyMath INTERFACE Eigen3::Eigen&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Users of HeaderOnlyMath automatically get Eigen&lt;/span&gt;
&lt;span class="nb"&gt;add_executable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyApp main.cpp&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyApp PRIVATE HeaderOnlyMath&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Gets Eigen too!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mixing Scopes: The Real World
&lt;/h3&gt;

&lt;p&gt;Most real projects use mixed scopes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngine 
    &lt;span class="c1"&gt;# Users need these APIs&lt;/span&gt;
    PUBLIC 
        SDL2::SDL2-static      &lt;span class="c1"&gt;# Window/input handling in public API&lt;/span&gt;
        glm                    &lt;span class="c1"&gt;# Math types in public headers&lt;/span&gt;
        OpenGL::GL            &lt;span class="c1"&gt;# Graphics API exposed to users&lt;/span&gt;

    &lt;span class="c1"&gt;# Internal implementation details  &lt;/span&gt;
    PRIVATE
        libglew_static        &lt;span class="c1"&gt;# OpenGL extension loading (wrapped)&lt;/span&gt;
        freetype              &lt;span class="c1"&gt;# Text rendering (abstracted away)&lt;/span&gt;
        &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_DL_LIBS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;      &lt;span class="c1"&gt;# Dynamic library loading (platform detail)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Multiple Executables and Examples
&lt;/h2&gt;

&lt;p&gt;A game engine isn't just a library - it includes tools and examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;NOT BUILD_STATIC_LIB AND BUILD_EXAMPLES&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Main editor application&lt;/span&gt;
    &lt;span class="nb"&gt;add_executable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngineEditor src/main.cpp&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;target_sources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngineEditor PRIVATE
        src/application.cpp
        src/Editor/Gui/inspector.cpp
        src/Editor/Gui/projectmanager.cpp
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ColumbaEngineEditor PRIVATE ColumbaEngine&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Game examples&lt;/span&gt;
    &lt;span class="nb"&gt;add_executable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;GameOff examples/GameOff/main.cpp&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;target_sources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;GameOff PRIVATE
        examples/GameOff/application.cpp
        examples/GameOff/character.cpp
        examples/GameOff/inventory.cpp
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;GameOff PRIVATE ColumbaEngine&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pattern&lt;/strong&gt;: Create multiple executables that all link to your main library. This way, the library code is compiled once and reused.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Testing Infrastructure
&lt;/h2&gt;

&lt;p&gt;Professional projects need tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;NOT BUILD_STATIC_LIB&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;enable_testing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GTEST_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;add_executable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;t1 test/maintest.cc&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;target_sources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;t1 PRIVATE
        test/collision2d.cc
        test/ecssystem.cc
        test/interpreter.cc
        test/renderer.cc
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;t1 PRIVATE gtest gtest_main ColumbaEngine&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Auto-discover tests&lt;/span&gt;
    &lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;GoogleTest&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;gtest_discover_tests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;t1&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;gtest_discover_tests()&lt;/code&gt; automatically finds all test cases and creates individual CTest entries. Run with &lt;code&gt;ctest&lt;/code&gt; or &lt;code&gt;make test&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Build Pitfalls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Global Variables vs Target Properties
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# BAD: Affects everything globally&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_CXX_FLAGS &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_CXX_FLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -DDEBUG"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# GOOD: Only affects specific target  &lt;/span&gt;
&lt;span class="nb"&gt;target_compile_definitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyTarget PRIVATE DEBUG&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Not Using Generator Expressions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# BAD: Debug symbols in release builds&lt;/span&gt;
&lt;span class="nb"&gt;target_compile_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyTarget PRIVATE -g&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# GOOD: Only in debug builds&lt;/span&gt;
&lt;span class="nb"&gt;target_compile_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyTarget PRIVATE $&amp;lt;$&amp;lt;CONFIG:Debug&amp;gt;:-g&amp;gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Wrong Dependency Scopes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# If users of your library need OpenGL headers:&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyLib PUBLIC OpenGL::GL&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# If only your implementation uses OpenGL:&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyLib PRIVATE OpenGL::GL&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We've Achieved: Build System Results
&lt;/h2&gt;

&lt;p&gt;After implementing all these concepts, here's what we achieved:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer Experience:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Simple build&lt;/span&gt;
git clone https://github.com/user/ColumbaEngine
&lt;span class="nb"&gt;cd &lt;/span&gt;ColumbaEngine
&lt;span class="nb"&gt;mkdir &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build
cmake ..
make &lt;span class="nt"&gt;-j8&lt;/span&gt;

&lt;span class="c"&gt;# Custom configuration&lt;/span&gt;
cmake &lt;span class="nt"&gt;-DBUILD_EXAMPLES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;OFF &lt;span class="nt"&gt;-DCMAKE_BUILD_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Release ..

&lt;span class="c"&gt;# Run tests&lt;/span&gt;
ctest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cross-platform Support:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same CMakeLists.txt works on Windows, Linux, and web&lt;/li&gt;
&lt;li&gt;Automatic dependency resolution per platform&lt;/li&gt;
&lt;li&gt;Platform-specific optimizations where needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Build Performance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precompiled headers cut build times by 60%&lt;/li&gt;
&lt;li&gt;Parallel compilation across all targets&lt;/li&gt;
&lt;li&gt;Incremental builds under 10 seconds for single-file changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Coming Up in Part 2
&lt;/h2&gt;

&lt;p&gt;In the next article, we'll cover the &lt;strong&gt;deployment and distribution&lt;/strong&gt; side of CMake:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Installation Systems&lt;/strong&gt;: How to make your library usable by others with &lt;code&gt;find_package()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export/Import Mechanisms&lt;/strong&gt;: The complex but powerful system that makes modern CMake libraries work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package Configuration&lt;/strong&gt;: Creating relocatable, dependency-aware packages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPack Integration&lt;/strong&gt;: Generating professional installers for multiple platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-world Distribution&lt;/strong&gt;: Getting your library into the hands of users&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a robust CMake build system for complex projects requires understanding several key concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Target-centric thinking&lt;/strong&gt; - Everything revolves around targets and their usage requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proper dependency management&lt;/strong&gt; - Choose the right strategy for your project's needs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform abstractions&lt;/strong&gt; - Let CMake handle platform differences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generator expressions&lt;/strong&gt; - Enable conditional behavior without complex if/else logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build optimization&lt;/strong&gt; - Use precompiled headers and other modern features&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;a href="https://github.com/Gallasko/ColumbaEngine" rel="noopener noreferrer"&gt;game engine&lt;/a&gt; we've built demonstrates these concepts in action with a real, production-ready build system that handles complex, multi-platform C++ projects.&lt;/p&gt;

&lt;p&gt;Remember: CMake is about expressing &lt;strong&gt;intent&lt;/strong&gt;, not &lt;strong&gt;implementation details&lt;/strong&gt;. Focus on what you want to achieve, and let CMake figure out how to do it on each platform.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Don't miss [Part 2] where we'll cover installation, packaging, and distribution - making your library actually usable by the world!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's your biggest CMake build challenge?&lt;/strong&gt; Share your experiences in the comments below!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The complete source code for ColumbaEngine with all the CMake patterns from this series is available on &lt;a href="https://github.com/Gallasko/ColumbaEngine" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. These patterns scale from small libraries to large, complex applications.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>cmake</category>
      <category>webassembly</category>
      <category>compiling</category>
    </item>
    <item>
      <title>Building a Custom C++ Serializer for Efficient Data Handling</title>
      <dc:creator>Pigeon Codeur</dc:creator>
      <pubDate>Wed, 06 Nov 2024 01:07:36 +0000</pubDate>
      <link>https://dev.to/pigeoncodeur/building-a-custom-c-serializer-for-efficient-data-handling-27oc</link>
      <guid>https://dev.to/pigeoncodeur/building-a-custom-c-serializer-for-efficient-data-handling-27oc</guid>
      <description>&lt;p&gt;Serialization is fundamental to engine development and data-driven applications, enabling complex objects to be saved, transferred, and loaded easily. In my custom game engine, I needed a serializer that could manage various types of data structures while ensuring efficient and readable output. This post explores the design and functionality of the C++ serializer I built, capable of handling basic and custom data types with ease.&lt;/p&gt;

&lt;p&gt;This guide will help you understand how to create a flexible serializer, including practical examples and test cases to ensure reliability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Converting Complex Data Structures to a Readable Format
&lt;/h2&gt;

&lt;p&gt;Serialization allows developers to convert complex C++ structs into a format that’s both human-readable and easy to restore. This is particularly useful in game engines where game objects, configurations, and states need to be saved and reloaded frequently. &lt;/p&gt;

&lt;p&gt;For example, consider the following &lt;code&gt;Texture2DComponent&lt;/code&gt; struct in a game engine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Texture2DComponent&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Ctor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;textureName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector3D&lt;/span&gt; &lt;span class="n"&gt;overlappingColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;overlappingColorRatio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When serialized, this struct might look like this in YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Texture2DComponent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;textureName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exampleTexture"&lt;/span&gt;
    &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.8&lt;/span&gt;
    &lt;span class="na"&gt;overlappingColor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;1.0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;0.0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;0.5&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;overlappingRatio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This transformation makes the data easy to read and edit manually, which can be a huge advantage during development, it also helps a lot in transferring data across the internet. In this post, we’ll walk through how to build this serializer, exploring its design and extensibility.&lt;/p&gt;




&lt;h3&gt;
  
  
  Core Design and Structure of the Serializer
&lt;/h3&gt;

&lt;p&gt;In this chapter, I’ll walk through the core design of the serializer, which includes three main classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Archive&lt;/code&gt;&lt;/strong&gt;: The core class responsible for constructing serialized data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;UnserializedObject&lt;/code&gt;&lt;/strong&gt;: A class for handling deserialized data, allowing easy access to serialized attributes and nested structures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Serializer&lt;/code&gt;&lt;/strong&gt;: The main manager that coordinates file handling, saving, and loading operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these classes plays a critical role in ensuring that data can be serialized and deserialized in a structured, reliable manner. Let’s dive into each component.&lt;/p&gt;




&lt;h4&gt;
  
  
  The Archive Class: Building the Serialized Data
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;Archive&lt;/code&gt; class acts as a container for the serialized string. It manages formatting, indentation, and data flow, ensuring that the serialized output is both readable and parsable.&lt;/p&gt;

&lt;h5&gt;
  
  
  Key Design Features of &lt;code&gt;Archive&lt;/code&gt;:
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Indentation Control&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To keep the serialized output clean and readable, the &lt;code&gt;Archive&lt;/code&gt; class uses an indentation level (&lt;code&gt;indentLevel&lt;/code&gt;) that increases or decreases depending on the depth of the data structure. Each new line starts with the appropriate number of tabs based on &lt;code&gt;indentLevel&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;End of Line Struct&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Archive&lt;/code&gt; class includes an &lt;code&gt;EndOfLine&lt;/code&gt; helper struct that handles line breaks and resets formatting flags (&lt;code&gt;requestNewline&lt;/code&gt; and &lt;code&gt;requestComma&lt;/code&gt;). This struct ensures that each data entry is properly formatted, with commas and line breaks applied where necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Operator Overloading&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overloading the &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; operator allows &lt;code&gt;Archive&lt;/code&gt; to handle multiple data types seamlessly. The template-based &lt;code&gt;operator&amp;lt;&amp;lt;&lt;/code&gt; ensures that any type of data can be serialized, provided it has a matching &lt;code&gt;serialize&lt;/code&gt; function or template specialization. This feature gives the serializer the flexibility to handle simple data types, custom components, and complex structures alike.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s a snippet of the &lt;code&gt;Archive&lt;/code&gt; class in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;EndOfLine&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;requestComma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;requestNewline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestNewline&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestComma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;endOfLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indentLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'\t'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;requestNewline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these design choices, &lt;code&gt;Archive&lt;/code&gt; keeps serialized data clean, ensuring that even complex structures are readable and formatted correctly.&lt;/p&gt;




&lt;h4&gt;
  
  
  The UnserializedObject Class: Parsing Deserialized Data
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;UnserializedObject&lt;/code&gt; class represents the deserialized data, allowing us to access fields by name and handle nested structures. It holds metadata such as object names and types, making it easier to retrieve individual fields from serialized data.&lt;/p&gt;

&lt;h5&gt;
  
  
  Key Features of &lt;code&gt;UnserializedObject&lt;/code&gt;:
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Attribute Handling&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;UnserializedObject&lt;/code&gt; includes a helper method, &lt;code&gt;getAsAttribute&lt;/code&gt;, that retrieves individual attributes within serialized data. This is particularly useful for objects containing a mix of fields, as each attribute is stored with a unique name for easy access.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Error Checking and Logging&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;UnserializedObject&lt;/code&gt; class performs checks to validate serialized data during deserialization, including checks for missing or mismatched braces, delimiters, and attribute names. Errors are logged to simplify debugging.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Overloaded Operators for Field Access&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operator overloads, such as &lt;code&gt;operator[]&lt;/code&gt;, make it easy to access attributes by name or index. This approach simplifies the code for handling nested data, allowing for intuitive retrieval of deserialized objects.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s an example of how &lt;code&gt;UnserializedObject&lt;/code&gt; handles attribute retrieval:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UnserializedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;UnserializedObject&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;isObjectName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;UnserializedObject&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objectName&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; 
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;find_if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;isObjectName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;LOG_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Requested child '"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"' not present in the object"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;UnserializedObject&lt;/code&gt;, parsing serialized data becomes straightforward, supporting intuitive access to data fields while maintaining error handling and flexibility.&lt;/p&gt;




&lt;h4&gt;
  
  
  The Serializer Class: Managing Files and Serialization Flow
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;Serializer&lt;/code&gt; class orchestrates the entire serialization and deserialization process. It handles file input and output, reading and writing serialized data, and storing serialized objects in a &lt;code&gt;serializedMap&lt;/code&gt; for easy retrieval.&lt;/p&gt;

&lt;h5&gt;
  
  
  Key Functions in &lt;code&gt;Serializer&lt;/code&gt;:
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;File Management&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Serializer&lt;/code&gt; can read from and write to files, supporting both direct paths and file objects. It also has an optional auto-save feature (&lt;code&gt;autoSave&lt;/code&gt;) that automatically writes serialized data to file when the program exits.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Version Control&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each serialized file includes a version header. This allows the &lt;code&gt;Serializer&lt;/code&gt; to parse files according to different serialization formats if needed, supporting backward compatibility.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Serializing and Deserializing Objects&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Serializer&lt;/code&gt; class includes methods for serializing (&lt;code&gt;serializeObject&lt;/code&gt;) and deserializing (&lt;code&gt;deserializeObject&lt;/code&gt;) various data types. These methods use &lt;code&gt;Archive&lt;/code&gt; and &lt;code&gt;UnserializedObject&lt;/code&gt; instances to manage the data flow, ensuring that objects are serialized and deserialized consistently.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s how &lt;code&gt;Serializer&lt;/code&gt; initializes a file read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Serializer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LOG_THIS_MEMBER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;LOG_MILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Reading an empty file"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;istringstream&lt;/span&gt; &lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// First line of the file should always be the version number&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;stringData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gulp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;LOG_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stringData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;serializedMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stringData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Serializer&lt;/code&gt;’s ability to handle file-based storage and version control makes it ideal for game development, where serialized data needs to be saved, loaded, and versioned consistently.&lt;/p&gt;

&lt;p&gt;The combination of &lt;code&gt;Archive&lt;/code&gt;, &lt;code&gt;UnserializedObject&lt;/code&gt;, and &lt;code&gt;Serializer&lt;/code&gt; provides a powerful and flexible system for managing data in a structured, human-readable format. By controlling indentation, handling errors, and managing complex structures with ease, this serializer is a valuable tool for game development. It enables the efficient saving and loading of game state, assets, and configurations, making the development process smoother and more efficient.&lt;/p&gt;

&lt;p&gt;In the next chapters, we’ll dive deeper into each component, exploring specific features like attribute handling, error checking, and practical examples for custom components.&lt;/p&gt;




&lt;h3&gt;
  
  
  Data Types and Extensibility
&lt;/h3&gt;

&lt;p&gt;This custom C++ serializer is built to handle both basic data types and complex structures, a feature that’s particularly useful in game engines where data persistence and readability are essential. In this chapter, we’ll explore how basic types are serialized and deserialized, and how this system can be extended to support custom types.&lt;/p&gt;

&lt;p&gt;The serializer achieves flexibility through &lt;strong&gt;template specializations&lt;/strong&gt; for each type, allowing us to control precisely how different types of data are stored and retrieved. Let’s go through the handling of basic types first, and then look at how the system can easily accommodate custom types like vectors and models.&lt;/p&gt;




&lt;h4&gt;
  
  
  Basic Type Serialization and Deserialization
&lt;/h4&gt;

&lt;p&gt;Each basic type has its own &lt;code&gt;serialize&lt;/code&gt; and &lt;code&gt;deserialize&lt;/code&gt; template specialization. This allows the serializer to convert each type to a string representation with a label, which is then used to identify the type during deserialization.&lt;/p&gt;

&lt;p&gt;Below are some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boolean&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UnserializedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getAsAttribute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;LOG_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Serialized string is not a bool ("&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;bool&lt;/code&gt; serializer converts the value to &lt;code&gt;"true"&lt;/code&gt; or &lt;code&gt;"false"&lt;/code&gt;, which can be easily checked during deserialization.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Integer&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UnserializedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getAsAttribute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;LOG_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Serialized string is not an int ("&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;stringstream&lt;/span&gt; &lt;span class="nf"&gt;sstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;sstream&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, integers are converted to strings, with type validation during deserialization to ensure that data remains consistent.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Floating Point Types&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"float"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UnserializedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getAsAttribute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"float"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;LOG_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Serialized string is not a float ("&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;stringstream&lt;/span&gt; &lt;span class="nf"&gt;sstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;sstream&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The floating-point serializers handle both &lt;code&gt;float&lt;/code&gt; and &lt;code&gt;double&lt;/code&gt;, converting these to strings for easy readability and conversion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;String&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UnserializedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;stringAttribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getAsAttribute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stringAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;LOG_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String attribute name is not 'string' ("&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;stringAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stringAttribute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Strings are handled directly, stored without conversion, and labeled as &lt;code&gt;"string"&lt;/code&gt; for consistency during deserialization.&lt;/p&gt;




&lt;h4&gt;
  
  
  Custom Type Support with Template Specializations
&lt;/h4&gt;

&lt;p&gt;For game engines, custom data types like vectors and models are essential. Using template specializations, we can handle these types in the same way as basic types by creating specific &lt;code&gt;serialize&lt;/code&gt; and &lt;code&gt;deserialize&lt;/code&gt; functions for each custom type.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vector2D&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector2D&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;vec2D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startSerialization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector 2D"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec2D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec2D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endSerialization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector2D&lt;/span&gt; &lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UnserializedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
       &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector2D&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;Vector2D&lt;/code&gt;, each component (&lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;) is serialized individually. During deserialization, each component is retrieved and used to reconstruct the vector.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vector3D&lt;/strong&gt; and &lt;strong&gt;Vector4D&lt;/strong&gt;:
Similar functions are defined for &lt;code&gt;Vector3D&lt;/code&gt; and &lt;code&gt;Vector4D&lt;/code&gt;, with each component (&lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;z&lt;/code&gt;, and &lt;code&gt;w&lt;/code&gt;) serialized as a separate attribute. Here’s an example for &lt;code&gt;Vector3D&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector3D&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;vec3D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startSerialization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector 3D"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec3D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec3D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec3D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endSerialization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector3D&lt;/span&gt; &lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UnserializedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
       &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
       &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"z"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector3D&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ModelInfo&lt;/strong&gt;:
The &lt;code&gt;ModelInfo&lt;/code&gt; struct represents a more complex custom type. Each attribute, such as &lt;code&gt;vertices&lt;/code&gt; and &lt;code&gt;indices&lt;/code&gt;, is serialized as a list. Here’s an example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;   &lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ModelInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;modelInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startSerialization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Model Info"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"[ "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;modelInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nbVertices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modelInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"]"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Vertices"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"[ "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;modelInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nbIndices&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modelInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"]"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Indices"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endSerialization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function serializes arrays as lists, which makes it easy to store and retrieve large datasets.&lt;/p&gt;




&lt;h4&gt;
  
  
  Extending the Serializer with New Data Types
&lt;/h4&gt;

&lt;p&gt;The template-based design of this serializer makes it highly extensible. To add support for new types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define the &lt;code&gt;serialize&lt;/code&gt; Function&lt;/strong&gt;: Create a template specialization for &lt;code&gt;serialize&lt;/code&gt; to store each field of the custom type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define the &lt;code&gt;deserialize&lt;/code&gt; Function&lt;/strong&gt;: Create a corresponding specialization for &lt;code&gt;deserialize&lt;/code&gt;, retrieving each field from the serialized data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the New Type&lt;/strong&gt;: Once defined, new data types can be serialized and deserialized like any other, ensuring seamless integration.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This design allows new data types to be added with minimal changes to the core serializer, making it ideal for game engines where data structures evolve frequently.&lt;/p&gt;

&lt;p&gt;With specialized functions for both basic and custom types, this serializer can handle diverse data structures in a game engine. Its template-based extensibility allows new types to be added easily, and each serialized field remains labeled and readable. By supporting types from simple integers to complex vectors, the serializer is robust, flexible, and ready for any type of data management needs in game development. &lt;/p&gt;




&lt;h3&gt;
  
  
  Practical Example – Serializing a &lt;code&gt;Texture2DComponent&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Serialization becomes particularly valuable in scenarios where complex game components need to be stored, loaded, and modified easily. In this chapter, we’ll go through a detailed example of serializing and deserializing a &lt;code&gt;Texture2DComponent&lt;/code&gt;, a struct in our game engine that represents a 2D texture with properties like opacity, color, and a texture name.&lt;/p&gt;

&lt;p&gt;By using template specializations, we can define custom serialization and deserialization functions for &lt;code&gt;Texture2DComponent&lt;/code&gt;, making it possible to save this component’s data in a readable format, such as YAML, and retrieve it as needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;Texture2DComponent&lt;/code&gt; Struct
&lt;/h4&gt;

&lt;p&gt;Here’s the structure of &lt;code&gt;Texture2DComponent&lt;/code&gt; that we want to serialize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Texture2DComponent&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;textureName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                     &lt;span class="c1"&gt;// Name of the texture&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                        &lt;span class="c1"&gt;// Opacity level&lt;/span&gt;
    &lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector3D&lt;/span&gt; &lt;span class="n"&gt;overlappingColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Overlapping color&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;overlappingColorRatio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// Intensity of the overlapping color&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This struct includes various fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;textureName&lt;/code&gt;&lt;/strong&gt;: The name of the texture as a string.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;opacity&lt;/code&gt;&lt;/strong&gt;: A float representing the texture’s opacity level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;overlappingColor&lt;/code&gt;&lt;/strong&gt;: A custom &lt;code&gt;Vector3D&lt;/code&gt; struct, representing RGB color values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;overlappingColorRatio&lt;/code&gt;&lt;/strong&gt;: A float that indicates the overlapping color's intensity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To serialize and deserialize &lt;code&gt;Texture2DComponent&lt;/code&gt;, we’ll create template specializations for each operation.&lt;/p&gt;




&lt;h4&gt;
  
  
  Step 1: Serializing &lt;code&gt;Texture2DComponent&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;serialize&lt;/code&gt; function for &lt;code&gt;Texture2DComponent&lt;/code&gt; needs to convert each field into a human-readable format. Here’s the code for serializing &lt;code&gt;Texture2DComponent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Archive&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Texture2DComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Logging for debugging and tracking&lt;/span&gt;

    &lt;span class="c1"&gt;// Start the serialization process, labeling the component type&lt;/span&gt;
    &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startSerialization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Texture2DComponent"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Serialize each field by its name&lt;/span&gt;
    &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"textureName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textureName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"opacity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"overlappingColor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overlappingColor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"overlappingRatio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overlappingColorRatio&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// End the serialization&lt;/span&gt;
    &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endSerialization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start Serialization&lt;/strong&gt;: &lt;code&gt;archive.startSerialization("Texture2DComponent")&lt;/code&gt; begins the serialization process, identifying this block of data as a &lt;code&gt;Texture2DComponent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serialize Fields&lt;/strong&gt;: Each field in &lt;code&gt;Texture2DComponent&lt;/code&gt; is serialized with a label. For instance, &lt;code&gt;textureName&lt;/code&gt; is serialized as &lt;code&gt;"textureName"&lt;/code&gt; so that it can be easily identified during deserialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End Serialization&lt;/strong&gt;: We call &lt;code&gt;archive.endSerialization()&lt;/code&gt; to mark the end of this component’s data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a structured, readable output that might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;Texture2DComponent {&lt;/span&gt;
    &lt;span class="s"&gt;textureName&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exampleTexture"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.8,&lt;/span&gt;
    &lt;span class="na"&gt;overlappingColor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;1.0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;0.0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;z&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;0.5&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;overlappingRatio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each field is clearly labeled and indented, making it easy to understand and even edit directly if needed.&lt;/p&gt;




&lt;h4&gt;
  
  
  Step 2: Deserializing &lt;code&gt;Texture2DComponent&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Deserialization is the reverse process, where we reconstruct a &lt;code&gt;Texture2DComponent&lt;/code&gt; from its serialized representation. Here’s the code for deserializing this component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;Texture2DComponent&lt;/span&gt; &lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UnserializedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LOG_THIS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Logging for debugging&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if the serialized object is valid&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isNull&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;LOG_ERROR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Element is null"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Texture2DComponent&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;  &lt;span class="c1"&gt;// Return an empty Texture2DComponent if null&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;LOG_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DOM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Deserializing a Texture2DComponent"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Extract each field from the serialized object by its name&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;textureName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"textureName"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"opacity"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;overlappingColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Vector3D&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"overlappingColor"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;overlappingColorRatio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializedString&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"overlappingRatio"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Construct and populate the Texture2DComponent&lt;/span&gt;
    &lt;span class="n"&gt;Texture2DComponent&lt;/span&gt; &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;textureName&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overlappingColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overlappingColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overlappingColorRatio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overlappingColorRatio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what each part of this function does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Null Check&lt;/strong&gt;: &lt;code&gt;if (serializedString.isNull())&lt;/code&gt; checks if the serialized data is valid. If not, an empty &lt;code&gt;Texture2DComponent&lt;/code&gt; is returned, and an error is logged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve Fields&lt;/strong&gt;: Each field is extracted from &lt;code&gt;serializedString&lt;/code&gt; using the field’s label, ensuring it matches the serialized format. This process allows the component’s data to be restored to its original values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconstruct the Component&lt;/strong&gt;: After all fields are retrieved, they’re used to populate a new &lt;code&gt;Texture2DComponent&lt;/code&gt; object, which is then returned.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach allows us to reconstitute the &lt;code&gt;Texture2DComponent&lt;/code&gt; from its serialized form with minimal effort.&lt;/p&gt;




&lt;h4&gt;
  
  
  Sample Usage
&lt;/h4&gt;

&lt;p&gt;Here’s a quick example of how you might use the serializer to handle a &lt;code&gt;Texture2DComponent&lt;/code&gt; in code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Creating a Texture2DComponent instance&lt;/span&gt;
&lt;span class="n"&gt;Texture2DComponent&lt;/span&gt; &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textureName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"grass_texture"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overlappingColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overlappingColorRatio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Serializing the component&lt;/span&gt;
&lt;span class="n"&gt;Archive&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Display the serialized output&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Deserializing the component from serialized data&lt;/span&gt;
&lt;span class="n"&gt;UnserializedObject&lt;/span&gt; &lt;span class="nf"&gt;unserializedObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"Texture2DComponent"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Texture2DComponent&lt;/span&gt; &lt;span class="n"&gt;deserializedTexture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Texture2DComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unserializedObj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Verifying the deserialized component&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Deserialized texture name: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;deserializedTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textureName&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Opacity: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;deserializedTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We create a &lt;code&gt;Texture2DComponent&lt;/code&gt;, fill it with data, and serialize it.&lt;/li&gt;
&lt;li&gt;The serialized output can be printed, edited, or saved to a file.&lt;/li&gt;
&lt;li&gt;We then create an &lt;code&gt;UnserializedObject&lt;/code&gt; from the serialized data and use &lt;code&gt;deserialize&lt;/code&gt; to reconstruct the &lt;code&gt;Texture2DComponent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finally, we confirm the fields to ensure they match the original values.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Key Benefits of This Approach
&lt;/h3&gt;

&lt;p&gt;Using this serializer for &lt;code&gt;Texture2DComponent&lt;/code&gt; provides several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Readability&lt;/strong&gt;: The serialized format is organized and easy to understand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modifiability&lt;/strong&gt;: Fields are labeled, making it possible to edit values directly in serialized files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: The serializer enforces a structure that can be easily parsed back into C++ objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Adding new fields or even new types of components requires minimal code changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This example illustrates how custom template specializations allow complex components like &lt;code&gt;Texture2DComponent&lt;/code&gt; to be serialized and deserialized easily. The process ensures data consistency and flexibility, making it simple to manage game components, configurations, and state information.&lt;/p&gt;

&lt;p&gt;With this serializer, you have a powerful tool to handle everything from game assets to state data in a clear, readable format. The custom serializer is adaptable, handling both primitive and custom data types, and is a robust solution for data persistence in game development.&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion and Final Thoughts
&lt;/h3&gt;

&lt;p&gt;This custom serializer provides flexibility, readability, and efficiency, all of which are crucial for game development. By supporting both basic and custom types, it adapts to evolving game mechanics and data structures. Through rigorous testing, we ensure that the serializer can handle complex scenarios reliably.&lt;/p&gt;

&lt;p&gt;If you’re building a game engine or data-driven application, this approach can streamline data management, improve debugging, and make saved data easily accessible.&lt;br&gt;
The complete code is open-source — check it out &lt;a href="https://github.com/Gallasko/ColumbaEngine" rel="noopener noreferrer"&gt;here&lt;/a&gt; leave a star or a reaction if you found this post interesting, explore the examples, and consider contributing new features or improvements!&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>performance</category>
      <category>gamedev</category>
      <category>data</category>
    </item>
    <item>
      <title>Optimizing C++ Memory Management with a Custom Memory Pool</title>
      <dc:creator>Pigeon Codeur</dc:creator>
      <pubDate>Fri, 01 Nov 2024 02:26:34 +0000</pubDate>
      <link>https://dev.to/pigeoncodeur/optimizing-c-memory-management-with-a-custom-memory-pool-1o3b</link>
      <guid>https://dev.to/pigeoncodeur/optimizing-c-memory-management-with-a-custom-memory-pool-1o3b</guid>
      <description>&lt;h2&gt;
  
  
  Optimizing C++ Memory Management with a Custom Memory Pool
&lt;/h2&gt;

&lt;p&gt;In performance-critical applications like game engines and real-time systems, frequent memory allocations can become a bottleneck, leading to increased latency and memory fragmentation. To tackle this, I developed a custom memory pool allocator that preallocates memory, minimizes allocation overhead, and optimizes memory reuse.&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk you through the design, implementation, and performance benchmarks of this custom memory pool allocator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features of the Memory Pool
&lt;/h2&gt;

&lt;p&gt;The memory pool is designed with a few key objectives in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Efficient Memory Allocation&lt;/strong&gt;: By managing memory in preallocated chunks, the pool reduces the need for frequent allocations and minimizes memory fragmentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exponential Growth&lt;/strong&gt;: The pool can grow dynamically, either in fixed blocks or exponentially, depending on configuration. This helps avoid frequent resizing, especially in applications where objects are created in batches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable Memory&lt;/strong&gt;: The memory pool uses a linked-list structure to recycle memory. When an object is deallocated, its memory block is returned to the pool for future use, ensuring efficient reallocation.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Chapter 1: Chunk and Memory Storage
&lt;/h2&gt;

&lt;p&gt;At the core of this memory pool is the &lt;strong&gt;chunk&lt;/strong&gt;, a small memory block that can either store an object or point to the next available space in the pool. This dual functionality allows us to optimize memory usage by using each chunk as either storage or a free list entry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chunk Design
&lt;/h3&gt;

&lt;p&gt;The chunk structure is defined as a union, which provides the flexibility needed for managing memory efficiently. Here’s how it looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="n"&gt;Chunk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/** Storage for a single object */&lt;/span&gt;
    &lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;aligned_storage&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;alignof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;::&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/** Pointer to the next free space in the pool */&lt;/span&gt;
    &lt;span class="n"&gt;Chunk&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;std::aligned_storage&lt;/code&gt; ensures that &lt;code&gt;element&lt;/code&gt; is aligned and sized properly for any type &lt;code&gt;T&lt;/code&gt;, allowing objects to be constructed in-place. When the chunk is not in use, &lt;code&gt;next&lt;/code&gt; points to the next available chunk in the pool, creating a linked list of free memory blocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free List Management
&lt;/h3&gt;

&lt;p&gt;The free list keeps track of available chunks. When a chunk is released, it is returned to the free list, enabling rapid reallocation. This approach avoids the overhead of constantly calling &lt;code&gt;new&lt;/code&gt; and &lt;code&gt;delete&lt;/code&gt;, making the memory pool more efficient than standard allocation methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chapter 2: Reserve and Allocation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reserve Method: Expanding the Pool
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;reserve&lt;/code&gt; method ensures that enough space is preallocated for additional objects, dynamically expanding as needed. Depending on the configuration, the pool can grow in fixed-size chunks or exponentially.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;reserveSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reserveSize&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reserveSize&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;blockSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;newBlock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Chunk&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;blockSize&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;chunkList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newBlock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;blockSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Initial Check&lt;/strong&gt;: If the requested size is less than the current pool size, no expansion is needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Block Sizing&lt;/strong&gt;: When &lt;code&gt;N == 1&lt;/code&gt;, the pool grows exponentially, doubling the block size each time. This strategy reduces the need for frequent expansions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Tracking&lt;/strong&gt;: Each new block of &lt;code&gt;Chunk&amp;lt;T&amp;gt;&lt;/code&gt; is added to &lt;code&gt;chunkList&lt;/code&gt; for easier deallocation later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Allocation Method: Constructing Objects in the Pool
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;allocate&lt;/code&gt; function constructs objects within the memory pool. It first checks for available memory in the free list, and if no chunks are available, it expands the pool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="nc"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;allocate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freeList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;freeList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;freeList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)...);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;reinterpret_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nbElements&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Chunk&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;*&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;chunkList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;listPos&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;vectorPos&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)...);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;reinterpret_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free List Check&lt;/strong&gt;: If &lt;code&gt;freeList&lt;/code&gt; is not null, the allocator retrieves a chunk from the free list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Placement New&lt;/strong&gt;: Constructs the object in-place within the chunk, avoiding the need for a secondary allocation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Index Calculation&lt;/strong&gt;: Uses logarithmic indexing to manage exponential growth.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Chapter 3: Benchmarking the Memory Pool vs. Standard Allocation
&lt;/h2&gt;

&lt;p&gt;To validate the performance benefits, I conducted several benchmarks comparing the memory pool to standard &lt;code&gt;new&lt;/code&gt;/&lt;code&gt;delete&lt;/code&gt; operations. Tests were conducted across different object counts to observe how each method scales.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Memory Pool Allocation/Deallocation&lt;/strong&gt;: Measures time for allocation and deallocation in the memory pool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard Allocation/Deallocation&lt;/strong&gt;: Measures equivalent operations using &lt;code&gt;new&lt;/code&gt; and &lt;code&gt;delete&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Pool Reallocation&lt;/strong&gt;: Reallocates each object after deallocation to test efficiency of memory reuse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard Reallocation&lt;/strong&gt;: Reallocates each object using standard methods.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Test Sizes&lt;/strong&gt;: Object counts ranged from 10 to 100 million, allowing us to observe scaling behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment&lt;/strong&gt;: Each operation was timed in nanoseconds using &lt;code&gt;std::chrono&lt;/code&gt;. Averages were taken over multiple runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results and Analysis
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Allocation and Deallocation
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Allocation Time Comparison&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The memory pool demonstrates faster allocation times, especially with large object counts.&lt;/li&gt;
&lt;li&gt;Standard allocation time grows quickly with object count due to frequent heap allocations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Deallocation Time Comparison&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The memory pool performs deallocation efficiently by linking chunks back into the free list, while standard deallocation becomes slower at high object counts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1o1fkp4hbesdfl8n9vy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1o1fkp4hbesdfl8n9vy.png" alt="Allocation Time Comparison" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Reallocation
&lt;/h4&gt;

&lt;p&gt;Reallocation in the memory pool is faster due to its ability to reuse memory blocks, avoiding new heap allocations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reallocation Time Comparison&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory pool reallocation shows a considerable advantage in speed, especially at large object counts.&lt;/li&gt;
&lt;li&gt;Standard reallocation incurs high overhead from continuous heap allocations, making it significantly slower at scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg66auqoi83y6mb3n684f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg66auqoi83y6mb3n684f.png" alt="Reallocation Time Comparison" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Insights and Conclusions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: The memory pool allocator scales efficiently across large object counts, showing significant time savings in allocation and reallocation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficiency in Reuse&lt;/strong&gt;: The memory pool is ideal for scenarios requiring frequent allocation and deallocation, such as object pooling in game engines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fragmentation Reduction&lt;/strong&gt;: Managing memory in contiguous chunks reduces fragmentation, enhancing performance compared to standard heap allocation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These benchmarks illustrate the potential of a custom memory pool allocator to improve memory management in performance-critical applications.&lt;/p&gt;

&lt;p&gt;Thanks for reading :)&lt;br&gt;
To view the full code and benchmark data, check out the project repository: &lt;a href="https://github.com/Gallasko/ColumbaEngine" rel="noopener noreferrer"&gt;ColumbaEngine&lt;/a&gt;.&lt;br&gt;
I hope you this article interesting. If so, please consider following me here and on social media.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>programming</category>
      <category>performance</category>
      <category>gamedev</category>
    </item>
  </channel>
</rss>
