dwrswr/cpp-module-06-guide
0
1<!DOCTYPE html>2<html lang="en" dir="ltr" data-theme="dark">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width,initial-scale=1">6<title>C++ Module 06 — casts, explained line by line</title>7<meta name="description" content="A complete walkthrough of the 42 C++ Module 06 project: static_cast, reinterpret_cast and dynamic_cast, every file of ex00/ex01/ex02 explained line by line, with a live ScalarConverter simulator.">8<meta name="color-scheme" content="dark light">9<link rel="preconnect" href="https://fonts.googleapis.com">10<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>11<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&family=Noto+Kufi+Arabic:wght@400;600;700&display=swap" rel="stylesheet">12<link rel="stylesheet" href="assets/style.css">13<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><rect width='64' height='64' rx='14' fill='%238b6dff'/><text x='32' y='44' font-family='monospace' font-size='30' font-weight='700' fill='%23fff' text-anchor='middle'>06</text></svg>">14</head>15<body>16<div id="prog" role="presentation"></div>17<a class="skip" href="#start">Skip to content</a>18 19<div class="top">20 <div class="top-in">21 <a class="brand" href="#start"><span class="mk">06</span>22 <span>cast lab<small> / 42 cpp</small></span></a>23 <span class="spacer"></span>24 <div class="seg" role="group" aria-label="Language">25 <button type="button" data-lang-btn="en">EN</button>26 <button type="button" data-lang-btn="ar">العربية</button>27 </div>28 <div class="seg" role="group" aria-label="Theme">29 <button type="button" data-theme-btn="dark" aria-pressed="true" title="Dark">◐</button>30 <button type="button" data-theme-btn="light" aria-pressed="false" title="Light">◑</button>31 </div>32 </div>33</div>34 35<div class="shell">36 <header class="hero">37 <span class="eyebrow"><i></i><span data-i18n="hero.eyebrow">42 · 1337 · CPP Module 06 · C++ casts</span></span>38 <h1><span data-i18n="hero.h1a">Four casts.</span><br><span class="g" data-i18n="hero.h1b">Three exercises. No guessing.</span></h1>39 <p class="lede" data-i18n="hero.lede">Module 06 is the module where C++ stops letting you convert things by accident. This page walks <b>the exact code in this repository</b> — every file of <code>ex00</code>, <code>ex01</code> and <code>ex02</code>, line by line — and shows what each cast really does, what the peer-evaluation sheet asks, and where the points get lost.</p>40 <div class="chips">41 <span class="chip s">static_cast</span>42 <span class="chip r">reinterpret_cast</span>43 <span class="chip d">dynamic_cast</span>44 <span class="chip">C++98</span>45 <span class="chip">-Wall -Wextra -Werror</span>46 <span class="chip">no leaks</span>47 </div>48 <noscript><div class="cal warn"><span class="ic">!</span><p>JavaScript is off, so the syntax colouring, the line numbers and the three interactive panels are disabled. Every word of the guide and every line of code is still here.</p></div></noscript>49 </header>50 51 <div class="cols">52 <nav class="toc" aria-label="Contents">53 <h6 data-i18n="toc.h">On this page</h6>54 <div class="toc-list">55 <a href="#start"><b>00</b><span data-i18n="toc.0">The mission</span></a>56 <a href="#rules"><b>01</b><span data-i18n="toc.1">Rules that fail you</span></a>57 <a href="#casts"><b>02</b><span data-i18n="toc.2">The four casts</span></a>58 <a href="#ex00"><b>03</b><span data-i18n="toc.3">ex00 · ScalarConverter</span></a>59 <a href="#ex01"><b>04</b><span data-i18n="toc.4">ex01 · Serializer</span></a>60 <a href="#ex02"><b>05</b><span data-i18n="toc.5">ex02 · identify</span></a>61 <a href="#build"><b>06</b><span data-i18n="toc.6">Build & run</span></a>62 <a href="#leaks"><b>07</b><span data-i18n="toc.7">Memory & sanitizers</span></a>63 <a href="#traps"><b>08</b><span data-i18n="toc.8">Ten ways to lose points</span></a>64 <a href="#defend"><b>09</b><span data-i18n="toc.9">The defence</span></a>65 <a href="#check"><b>10</b><span data-i18n="toc.10">Pre-defence checklist</span></a>66 </div>67 </nav>68 69 <main>70 71<section id="start" class="rv">72 <div class="sec-tag"><b>00</b> <span data-i18n="s0.tag">The mission</span></div>73 <h2 data-i18n="s0.h">What module 06 is actually about</h2>74 <p data-i18n="s0.p1">Up to now you converted values by writing <code>(int)x</code> and hoping. C++ replaces that one blunt tool with four named ones, and module 06 makes you use three of them for real. The point is not the syntax — it is learning <b>which conversions the compiler can prove safe, which it can only check at runtime, and which it simply obeys without checking anything at all</b>.</p>75 <div class="grid g3">76 <div class="card"><span class="k">ex00</span><h4><span class="bd s">static_cast</span></h4><p data-i18n="s0.c1">A string like <code>42.0f</code> arrives. Work out what type it is, parse it once, then show the same value as <code>char</code>, <code>int</code>, <code>float</code> and <code>double</code> — or say <i>impossible</i>.</p></div>77 <div class="card"><span class="k">ex01</span><h4><span class="bd r">reinterpret_cast</span></h4><p data-i18n="s0.c2">Turn a pointer into a plain integer, turn it back, and prove the round trip returns the very same object.</p></div>78 <div class="card"><span class="k">ex02</span><h4><span class="bd d">dynamic_cast</span></h4><p data-i18n="s0.c3">A <code>Base *</code> points at an <code>A</code>, a <code>B</code> or a <code>C</code> and will not tell you which. Ask the runtime, twice: once through a pointer, once through a reference.</p></div>79 </div>80 <h3 data-i18n="s0.h2">What is in this project</h3>81 <div class="tree">cpp06/82├── <b>ex00/</b> <i>Makefile</i> main.cpp ScalarConverter.hpp ScalarConverter.cpp → <u>./convert</u>83├── <b>ex01/</b> <i>Makefile</i> main.cpp Serializer.hpp Serializer.cpp Data.hpp → <u>./serialize</u>84└── <b>ex02/</b> <i>Makefile</i> main.cpp Base.hpp Base.cpp A.hpp B.hpp C.hpp85 identify.hpp identify.cpp → <u>./identify</u></div>86 <div class="cal ok"><span class="ic">✓</span><p data-i18n="s0.ver"><b>Everything on this page was run, not remembered.</b> The three exercises were compiled with <code>c++ -Wall -Wextra -Werror -std=c++98</code> (g++ 13.3.0) with zero warnings, re-compiled under <code>-fsanitize=address,undefined</code> and run clean, and every terminal block below is pasted from that run. The live converter panel further down was diffed against the real <code>./convert</code> binary on <b>46 different literals</b> and agrees on all 46.</p></div>87</section>88 89<section id="rules" class="rv">90 <div class="sec-tag"><b>01</b> <span data-i18n="s1.tag">Rules that fail you</span></div>91 <h2 data-i18n="s1.h">The rules that decide your grade before your code is read</h2>92 <p data-i18n="s1.p1">These come straight from the subject's general rules and from the correction sheet your evaluator will have open. They are not style advice — each one has a number attached to it.</p>93 <div class="tw"><table>94 <thead><tr><th data-i18n="s1.t1">Rule</th><th data-i18n="s1.t2">Cost if broken</th><th data-i18n="s1.t3">How this project obeys</th></tr></thead>95 <tbody>96 <tr><td data-i18n="s1.r1a">Compiles with <code>c++</code> and <code>-Wall -Wextra -Werror</code>, and still with <code>-std=c++98</code></td><td><span class="bd bad" data-i18n="s1.r1b">not graded</span></td><td data-i18n="s1.r1c">All three Makefiles hard-code <code>CXXFLAGS = -Wall -Wextra -Werror -std=c++98</code>.</td></tr>97 <tr><td data-i18n="s1.r2a"><code>*printf()</code>, <code>*alloc()</code> or <code>free()</code> anywhere</td><td><span class="bd bad" data-i18n="s1.r2b">grade 0</span></td><td data-i18n="s1.r2c">Output is <code>std::cout</code> only; the single allocation is <code>new</code> in <code>generate()</code>.</td></tr>98 <tr><td data-i18n="s1.r3a"><code>using namespace</code> or <code>friend</code></td><td><span class="bd bad" data-i18n="s1.r3b">grade -42</span></td><td data-i18n="s1.r3c">Neither keyword appears; every name is spelled <code>std::</code>.</td></tr>99 <tr><td data-i18n="s1.r4a">STL containers or <code><algorithm></code> before module 08</td><td><span class="bd bad" data-i18n="s1.r4b">grade -42</span></td><td data-i18n="s1.r4c">Nothing but <code>std::string</code>, which is not a container in this rule's sense.</td></tr>100 <tr><td data-i18n="s1.r5a">A function body inside a header (templates excepted)</td><td><span class="bd bad" data-i18n="s1.r5b">exercise 0</span></td><td data-i18n="s1.r5c">All seven headers declare only; every body lives in a <code>.cpp</code>.</td></tr>101 <tr><td data-i18n="s1.r6a">Missing include guards</td><td><span class="bd bad" data-i18n="s1.r6b">grade 0</span></td><td data-i18n="s1.r6c">Every header opens with <code>#ifndef X_HPP</code> / <code>#define X_HPP</code> and closes with <code>#endif</code>.</td></tr>102 <tr><td data-i18n="s1.r7a">Leaking heap memory</td><td><span class="bd warn" data-i18n="s1.r7b">Leaks flag</span></td><td data-i18n="s1.r7c">Each <code>generate()</code> is matched by a <code>delete p</code> in the same loop turn.</td></tr>103 <tr><td data-i18n="s1.r8a"><code><typeinfo></code> in ex02</td><td><span class="bd bad" data-i18n="s1.r8b">ex02 0</span></td><td data-i18n="s1.r8c">Not included — the reference version catches <code>std::exception &</code> instead of naming <code>std::bad_cast</code>.</td></tr>104 </tbody>105 </table></div>106 <div class="cal warn"><span class="ic">!</span><p data-i18n="s1.ocf"><b>Orthodox Canonical Form, with a twist.</b> The subject demands OCF from module 02 to 09, but ex00 and ex01 ask for classes that <i>cannot be instantiated at all</i>. The answer to both demands at once: declare all four members — default constructor, copy constructor, assignment operator, destructor — and make them <b>private</b>. The form is respected and the class is unusable, which is exactly what was asked. ex02 is explicitly excused from OCF by the subject, which is why <code>A</code>, <code>B</code> and <code>C</code> are empty.</p></div>107</section>108 109<section id="casts" class="rv">110 <div class="sec-tag"><b>02</b> <span data-i18n="s2.tag">The four casts</span></div>111 <h2 data-i18n="s2.h">One C cast became four C++ casts</h2>112 <p data-i18n="s2.p1">In C, <code>(T)x</code> means “do whatever it takes”. It might widen a number, it might reinterpret raw bytes, it might strip a <code>const</code> — and the reader cannot tell which. C++ splits that single operator into four, each one narrow enough that its <i>name</i> tells you what risk you just took.</p>113 <div class="tw"><table>114 <thead><tr><th data-i18n="s2.t1">Cast</th><th data-i18n="s2.t2">What it may do</th><th data-i18n="s2.t3">Checked</th><th data-i18n="s2.t4">Fails by</th><th data-i18n="s2.t5">Here</th></tr></thead>115 <tbody>116 <tr>117 <td class="mono"><span class="bd s">static_cast</span></td>118 <td data-i18n="s2.a2">Conversions between <b>related</b> types the compiler can already reason about: number to number, enum to int, derived pointer up to base, <code>void *</code> back to its real type.</td>119 <td data-i18n="s2.a3"><b>Compile time</b></td>120 <td data-i18n="s2.a4">Refusing to compile</td>121 <td class="mono">ex00</td>122 </tr>123 <tr>124 <td class="mono"><span class="bd r">reinterpret_cast</span></td>125 <td data-i18n="s2.b2">Re-labels the same bits as an <b>unrelated</b> type: pointer to integer, integer back to pointer, one pointer type to another.</td>126 <td data-i18n="s2.b3"><b>Nothing</b> is checked</td>127 <td data-i18n="s2.b4">Silence, then undefined behaviour</td>128 <td class="mono">ex01</td>129 </tr>130 <tr>131 <td class="mono"><span class="bd d">dynamic_cast</span></td>132 <td data-i18n="s2.c2">Walks <b>down</b> or across a hierarchy that has at least one virtual function, asking the object what it really is.</td>133 <td data-i18n="s2.c3"><b>Run time</b>, via the vtable</td>134 <td data-i18n="s2.c4"><code>NULL</code> for pointers, <code>std::bad_cast</code> for references</td>135 <td class="mono">ex02</td>136 </tr>137 <tr>138 <td class="mono"><span class="bd">const_cast</span></td>139 <td data-i18n="s2.d2">Adds or removes <code>const</code> / <code>volatile</code> and nothing else.</td>140 <td data-i18n="s2.d3"><b>Compile time</b></td>141 <td data-i18n="s2.d4">Compiles, then undefined behaviour if you write to a truly const object</td>142 <td data-i18n="s2.d5">unused</td>143 </tr>144 </tbody>145 </table></div>146 <div class="cal"><span class="ic">i</span><p data-i18n="s2.note"><b>The rule of thumb an evaluator will accept:</b> reach for <code>static_cast</code> first, because the compiler still gets a vote. Use <code>dynamic_cast</code> when the answer only exists at runtime. Use <code>reinterpret_cast</code> when you have decided to take responsibility yourself. And if you are reaching for <code>const_cast</code>, first check whether the <code>const</code> you are fighting was a mistake.</p></div>147 148 <h3 data-i18n="s2.h2">Which one do I need?</h3>149 <div class="picker" id="picker">150 <p class="q" data-i18n="s2.q">Pick the situation you are in:</p>151 <div class="pick-opts">152 <button type="button" data-cast="s" data-q="q1" data-i18n="s2.q1">I have a <code>double</code> and I want the <code>int</code> part of it.</button>153 <button type="button" data-cast="d" data-q="q2" data-i18n="s2.q2">I have a <code>Base *</code> and I need to know whether it really points at an <code>A</code>.</button>154 <button type="button" data-cast="r" data-q="q3" data-i18n="s2.q3">I have to keep a pointer inside an integer and rebuild it later.</button>155 <button type="button" data-cast="k" data-q="q4" data-i18n="s2.q4">A function wants <code>char *</code> but all I hold is <code>const char *</code>.</button>156 <button type="button" data-cast="s" data-q="q5" data-i18n="s2.q5">I have a <code>Derived *</code> and the function takes a <code>Base *</code>.</button>157 <button type="button" data-cast="r" data-q="q6" data-i18n="s2.q6">I want to read the bytes of a <code>float</code> as an <code>unsigned int</code>.</button>158 </div>159 <div class="pick-out" data-i18n="s2.pickhint">Choose one and the answer appears here, with the reason.</div>160 </div>161 <div hidden>162 <div id="ans-q1"><p data-i18n="s2.a.q1">Both are arithmetic types, so the conversion is well defined and the compiler can do it: <code>static_cast<int>(d)</code>. It truncates toward zero — it does not round — and if the value does not fit in an <code>int</code> the result is undefined, which is exactly why <code>ex00</code> range-checks <b>before</b> casting.</p></div>163 <div id="ans-q2"><p data-i18n="s2.a.q2">Only the running object knows its own type, so this is the one question a compile-time cast cannot answer: <code>dynamic_cast<A *>(p)</code>. It needs <code>Base</code> to be polymorphic — one virtual function is enough, and a virtual destructor is the natural one.</p></div>164 <div id="ans-q3"><p data-i18n="s2.a.q3">A pointer and an integer are unrelated types, so nothing weaker will do: <code>reinterpret_cast<uintptr_t>(p)</code> on the way in and <code>reinterpret_cast<Data *>(raw)</code> on the way back. Use <code>uintptr_t</code>, not <code>int</code> — it is the only integer the standard promises is wide enough. This is <code>ex01</code>.</p></div>165 <div id="ans-q4"><p data-i18n="s2.a.q4"><code>const_cast<char *>(s)</code> compiles, but stop and read the next sentence: it is only safe if the object behind that pointer was never really <code>const</code>. Casting away <code>const</code> and then writing to a genuinely constant object is undefined behaviour, not a trick. Module 06 does not need it.</p></div>166 <div id="ans-q5"><p data-i18n="s2.a.q5">Upcasting is safe by construction, so you do not need a cast at all — the conversion is implicit. If you want it visible in the code, <code>static_cast<Base *>(d)</code> is the honest way to write it. <code>dynamic_cast</code> here would be paying for a runtime check whose answer is already known.</p></div>167 <div id="ans-q6"><p data-i18n="s2.a.q6"><code>reinterpret_cast</code> is the only cast that will do it, because you are asking to see the same bytes through a different type. Note what <code>static_cast<unsigned int>(f)</code> would do instead: convert the <i>value</i>, so <code>1.0f</code> becomes <code>1</code>, not <code>0x3F800000</code>.</p></div>168 </div>169</section>170 171<section id="ex00" class="rv" data-acc="s">172 <div class="sec-tag"><b>03</b> <span data-i18n="s3.tag">ex00 · ScalarConverter</span></div>173 <h2 data-i18n="s3.h">ex00 — one string in, four types out</h2>174 <p data-i18n="s3.p1">The subject gives a class with a single job: take the <b>text</b> of a C++ literal, work out which of the four scalar types it is written as, parse it once, and then print that same value as all four — printing <i>impossible</i> when the value cannot survive the trip. The class stores nothing, so the subject also demands it cannot be instantiated.</p>175 <div class="term">176 <div class="term-head"><i></i><i></i><i></i><span>the three subject examples, run against this build</span></div>177<pre><span class="p">$</span> <span class="c">./convert 0</span>178char: Non displayable179int: 0180float: 0.0f181double: 0.0182<span class="p">$</span> <span class="c">./convert nan</span>183char: impossible184int: impossible185float: nanf186double: nan187<span class="p">$</span> <span class="c">./convert 42.0f</span>188char: <span class="g">'*'</span>189int: 42190float: 42.0f191double: 42.0</pre>192 </div>193 194 <h3 data-i18n="s3.h2">The header — how a class refuses to exist</h3>195{{CODE ex00/ScalarConverter.hpp|h-sc}}196 <ol class="notes" data-for="h-sc">197 <li data-line="1-2" data-i18n="s3.n1"><b>Include guard.</b> Missing this is an automatic zero, and the macro should match the file name: <code>SCALARCONVERTER_HPP</code>.</li>198 <li data-line="4" data-i18n="s3.n2"><b>The header includes what it uses.</b> <code>convert</code> takes a <code>std::string</code>, so <code><string></code> belongs here, not in the <code>.cpp</code> — the subject requires every header to compile on its own.</li>199 <li data-line="9" data-i18n="s3.n3"><b><code>static</code> is what makes it callable without an object</b>: <code>ScalarConverter::convert("42")</code>. It is also why the class needs no state at all.</li>200 <li data-line="11-14" data-i18n="s3.n4"><b>The four canonical members, made private.</b> This answers “Orthodox Canonical Form” and “must not be instantiable” in one move: they exist, so the form is honoured; they are private, so <code>ScalarConverter c;</code> will not compile. The correction sheet asks this first — “private constructor and static methods?”</li>201 <li data-line="6-16" data-i18n="s3.n5"><b>No function bodies here.</b> All four are defined in the <code>.cpp</code>. A body in a header, templates excepted, is a zero for the exercise.</li>202 </ol>203 204 <h3 data-i18n="s3.h3">The implementation, line by line</h3>205 <p data-i18n="s3.p2">Click any note below to light up the lines it is talking about.</p>206{{CODE ex00/ScalarConverter.cpp|c-sc|tall}}207 <ol class="notes" data-for="c-sc">208 <li data-line="2-7" data-i18n="s3.m1"><b>Six headers, one reason each.</b> <code><cctype></code> for <code>isdigit</code> and <code>isprint</code>, <code><climits></code> for <code>CHAR_MIN</code>…<code>INT_MAX</code>, <code><cstdlib></code> for <code>strtod</code>, <code><iomanip></code> for <code>setprecision</code>, <code><iostream></code> for <code>cout</code>, <code><limits></code> for <code>numeric_limits<float>::max()</code>.</li>209 <li data-line="9-16" data-i18n="s3.m2"><b>Five outcomes, one enum.</b> <code>INVALID_LIT</code> is a member like the rest, so <code>detect</code> always returns an <code>e_type</code> and never needs a sentinel or a second output parameter.</li>210 <li data-line="18-31" data-i18n="s3.m3"><b>The canonical four, defined but unreachable.</b> They need a definition or the linker complains the moment something references them; they are private, so nothing ever can. <code>(void)src;</code> silences <code>-Wunused-parameter</code>, which is fatal under <code>-Werror</code>.</li>211 <li data-line="33-52" data-i18n="s3.m4"><b><code>isNumber</code> is the entire grammar.</b> One optional sign, then digits and at most one dot, and it only says yes if it actually saw a digit. Every number the exercise accepts passes through here exactly once.</li>212 <li data-line="39-40" data-i18n="s3.m5"><b>The sign is consumed once, before the loop.</b> That single <code>i++</code> is why <code>--42</code> and <code>4-2</code> are rejected: inside the loop, a <code>-</code> matches nothing.</li>213 <li data-line="45-46" data-i18n="s3.m6"><b><code>&& !dot</code> is the second-dot guard.</b> Remove it and <code>1.2.3</code> becomes a valid double. The same variable doubles as the int/double switch, since it is passed back out by reference.</li>214 <li data-line="51" data-i18n="s3.m7"><b><code>digits > 0</code>, not “I reached the end”.</b> This is what rejects <code>"."</code>, <code>"+"</code> and <code>"+."</code> — strings made of nothing but sign and dot.</li>215 <li data-line="54-72" data-i18n="s3.m8"><b><code>detect</code> is ordered on purpose.</b> Quoted char, bare char, float pseudo-literals, double pseudo-literals, plain number, then <code>f</code>-suffixed number. Put the bare-char test after the pseudo-literals and the single letter <code>f</code> stops being a character.</li>216 <li data-line="60-61" data-i18n="s3.m9"><b>A lone character is a char literal — unless it is a digit.</b> That <code>!std::isdigit</code> is what keeps <code>./convert 7</code> an <code>int</code> worth seven instead of a <code>char</code> worth fifty-five.</li>217 <li data-line="62-65" data-i18n="s3.m10"><b>Pseudo-literals are matched as exact strings</b>, before any parsing, with the <code>f</code> forms first. That is the cheapest correct way to honour the six names the subject lists.</li>218 <li data-line="68-70" data-i18n="s3.m11"><b>The <code>f</code> suffix is peeled off, then re-checked.</b> <code>42.0f</code> becomes <code>42.0</code> and must pass <code>isNumber</code> on its own, so <code>f</code>, <code>4-2f</code> and <code>.f</code> cannot sneak through.</li>219 <li data-line="77" data-i18n="s3.m12"><b><code>value != value</code> is the C++98 way to ask “is this NaN?”.</b> <code>std::isnan</code> is C++11. NaN is the only value not equal to itself, so this idiom needs no header at all.</li>220 <li data-line="79" data-i18n="s3.m13"><b>Two casts, and the order matters.</b> <code>static_cast<char></code> produces the character; <code>static_cast<unsigned char></code> is then required because handing a <i>negative</i> <code>char</code> to <code>isprint</code> is undefined behaviour. That is why <code>./convert -42</code> calmly answers “Non displayable”.</li>221 <li data-line="77-82" data-i18n="s3.m14"><b>Range first, cast second.</b> The bounds are tested on the <code>double</code>, while the value is still intact. Casting first and checking the result afterwards is already undefined behaviour — this is the single most important habit in the exercise.</li>222 <li data-line="94-104" data-i18n="s3.m15"><b><code>pseudo</code> is a licence to skip the range test.</b> Infinity sits outside every finite bound, so without that flag <code>+inf</code> would print “impossible” instead of <code>inff</code>. It is computed from the <i>stripped</i> body, which is how <code>nanf</code> and <code>nan</code> end up sharing one path.</li>223 <li data-line="102-103" data-i18n="s3.m16"><b><code>std::fixed</code> with <code>setprecision(1)</code> is the format the subject prints</b> — <code>0.0f</code>, <code>42.0f</code>. It is also why <code>3.14159</code> comes out as <code>3.1</code>: one decimal, always. The <code>"f"</code> is a literal appended by hand; no stream flag adds it.</li>224 <li data-line="117-138" data-i18n="s3.m17"><b>One parse, four prints.</b> Everything funnels into a single <code>double</code> and the four printers are four <code>static_cast</code>s of it. Detect, parse once, cast explicitly — that is the shape being tested.</li>225 <li data-line="124-129" data-i18n="s3.m18"><b>Invalid input returns before any cast runs</b>, printing one line and nothing else — never a half-filled block of output.</li>226 <li data-line="130-131" data-i18n="s3.m19"><b>A character becomes a number by promotion</b>, written out as an explicit <code>static_cast<double></code>. The correction sheet permits implicit casts for promotions, but spelling it out costs nothing and shows intent.</li>227 <li data-line="137" data-i18n="s3.m20"><b><code>std::strtod</code> is the “any function to convert a string” the subject authorises.</b> It lives in <code><cstdlib></code> and is not on the forbidden list, which contains only <code>*printf</code>, <code>*alloc</code> and <code>free</code>. If an evaluator wants a C++-flavoured alternative, the answer is <code>std::istringstream</code>.</li>228 </ol>229 230 <h3 data-i18n="s3.h4">The test program</h3>231{{CODE ex00/main.cpp|m-sc|plain}}232 <p data-i18n="s3.p3">One argument, one call. Rejecting <code>argc != 2</code> matters more than it looks: with no argument <code>argv[1]</code> is <code>NULL</code>, and building a <code>std::string</code> from a null pointer is undefined behaviour — a crash your evaluator can trigger just by pressing Enter.</p>233 234 <h3 data-i18n="s3.h5">Try it — the same logic, running in your browser</h3>235 <p data-i18n="s3.p4">This panel is a line-for-line port of the C++ above: same <code>detect</code>, same order of tests, same bounds, same one-decimal formatting. It was checked against the compiled <code>./convert</code> on 46 literals and matches all of them, so what you see here is what the binary prints.</p>236 <div class="sim" id="sim">237 <div class="sim-head">238 <span class="lbl">./convert</span>239 <input class="sim-in" type="text" value="42.0f" spellcheck="false" aria-label="literal" data-i18n-ph="sim" placeholder="type a literal">240 </div>241 <div class="sim-body">242 <div class="sim-pane">243 <h6 data-i18n="sim.out">output</h6>244 <div class="sim-out"></div>245 </div>246 <div class="sim-pane">247 <h6 data-i18n="sim.how">what happened</h6>248 <ol class="steps"></ol>249 </div>250 </div>251 <div class="presets">252 <button type="button" data-v="0">0</button>253 <button type="button" data-v="42">42</button>254 <button type="button" data-v="'a'">'a'</button>255 <button type="button" data-v="a">a</button>256 <button type="button" data-v="7">7</button>257 <button type="button" data-v="128">128</button>258 <button type="button" data-v="-42">-42</button>259 <button type="button" data-v="3.14159">3.14159</button>260 <button type="button" data-v="2147483647">2147483647</button>261 <button type="button" data-v="2147483648">2147483648</button>262 <button type="button" data-v="nan">nan</button>263 <button type="button" data-v="nanf">nanf</button>264 <button type="button" data-v="+inff">+inff</button>265 <button type="button" data-v="-inf">-inf</button>266 <button type="button" data-v=".5">.5</button>267 <button type="button" data-v="12.">12.</button>268 <button type="button" data-v="1f">1f</button>269 <button type="button" data-v="1e40">1e40</button>270 <button type="button" data-v="1.2.3">1.2.3</button>271 <button type="button" data-v="hello">hello</button>272 </div>273 </div>274 275 <h3 data-i18n="s3.h6">The inputs worth knowing about</h3>276 <p data-i18n="s3.p5">Every row below is copied from the real binary, not predicted.</p>277 <div class="tw"><table>278 <thead><tr><th data-i18n="s3.k1">literal</th><th data-i18n="s3.k2">detected</th><th data-i18n="s3.k3">what comes out</th><th data-i18n="s3.k4">why it matters</th></tr></thead>279 <tbody>280 <tr><td class="mono">7</td><td class="mono">INT_LIT</td><td class="mono">int: 7</td><td data-i18n="s3.w1">A single <b>digit</b> is a number, not a character. That is the whole job of <code>!isdigit</code> in <code>detect</code>.</td></tr>281 <tr><td class="mono">a</td><td class="mono">CHAR_LIT</td><td class="mono">char: 'a' · int: 97</td><td data-i18n="s3.w2">Unquoted single characters are accepted too, which is friendlier than the subject strictly requires.</td></tr>282 <tr><td class="mono">127</td><td class="mono">INT_LIT</td><td class="mono">char: Non displayable</td><td data-i18n="s3.w3">127 is <code>DEL</code>: inside <code>char</code> range, still not printable. The two tests are separate for a reason.</td></tr>283 <tr><td class="mono">128</td><td class="mono">INT_LIT</td><td class="mono">char: impossible</td><td data-i18n="s3.w4">One past <code>CHAR_MAX</code> on Linux, so it is a range failure rather than a display failure.</td></tr>284 <tr><td class="mono">-42</td><td class="mono">INT_LIT</td><td class="mono">char: Non displayable</td><td data-i18n="s3.w5">A negative <code>char</code> becomes 214 as <code>unsigned char</code> — the cast that keeps <code>isprint</code> defined.</td></tr>285 <tr><td class="mono">2147483647</td><td class="mono">INT_LIT</td><td class="mono">int: 2147483647<br>float: 2147483648.0f</td><td data-i18n="s3.w6"><b>Not a bug.</b> A <code>float</code> carries 24 bits of mantissa, so <code>INT_MAX</code> is not representable and rounds up. Expect to be asked about this one.</td></tr>286 <tr><td class="mono">2147483648</td><td class="mono">INT_LIT</td><td class="mono">int: impossible<br>double: 2147483648.0</td><td data-i18n="s3.w7">Overflow is caught by the range test, not by the cast — which is the point of testing before casting.</td></tr>287 <tr><td class="mono">3.14159</td><td class="mono">DOUBLE_LIT</td><td class="mono">float: 3.1f · double: 3.1</td><td data-i18n="s3.w8"><code>setprecision(1)</code> is the format the subject's own examples use. It rounds the display, never the value.</td></tr>288 <tr><td class="mono">.5 / 12.</td><td class="mono">DOUBLE_LIT</td><td class="mono">0.5f / 12.0f</td><td data-i18n="s3.w9">A dot with digits on only one side is still a valid decimal, and <code>isNumber</code> accepts it.</td></tr>289 <tr><td class="mono">1f</td><td class="mono">FLOAT_LIT</td><td class="mono">int: 1 · float: 1.0f</td><td data-i18n="s3.w10">The suffix rule does not require a dot, exactly like real C++ source.</td></tr>290 <tr><td class="mono">nanf</td><td class="mono">FLOAT_LIT</td><td class="mono">float: nanf · double: nan</td><td data-i18n="s3.w11">The <code>f</code> belongs to the float line only; the double line prints the bare name.</td></tr>291 <tr><td class="mono">+inff</td><td class="mono">FLOAT_LIT</td><td class="mono">float: inff · double: inf</td><td data-i18n="s3.w12">The <code>+</code> is consumed by <code>strtod</code> and never printed back — matching the subject's sample.</td></tr>292 <tr><td class="mono">1e40</td><td class="mono">INVALID_LIT</td><td data-i18n="s3.w13a">the “is not a…” line</td><td data-i18n="s3.w13"><b>Deliberate.</b> The subject says “except for char parameters, only the decimal notation will be used”, so exponents are out of scope.</td></tr>293 <tr><td class="mono">1.2.3 / --42 / +.</td><td class="mono">INVALID_LIT</td><td data-i18n="s3.w14a">the “is not a…” line</td><td data-i18n="s3.w14">Two dots, two signs, and no digit at all: the three ways <code>isNumber</code> says no.</td></tr>294 </tbody>295 </table></div>296 <div class="cal warn"><span class="ic">?</span><p data-i18n="s3.probe"><b>Two answers to have ready.</b> If asked why <code>1e40</code> is rejected: the subject restricts input to decimal notation, and accepting exponents would also mean deciding whether <code>1e40</code> is a float or a double. If asked why <code>3.14159</code> prints <code>3.1</code>: the subject's expected output shows exactly one decimal, and <code>setprecision(1)</code> reproduces it. Both are choices, and saying so is worth more than pretending they are laws.</p></div>297</section>298 299<section id="ex01" class="rv" data-acc="r">300 <div class="sec-tag"><b>04</b> <span data-i18n="s4.tag">ex01 · Serializer</span></div>301 <h2 data-i18n="s4.h">ex01 — a pointer smuggled through an integer</h2>302 <p data-i18n="s4.p1">This is the shortest exercise in the module and the one people explain worst. The task: turn the address of an object into a plain unsigned integer, hand that integer around as data, then turn it back into a usable pointer to the same object. Two <code>reinterpret_cast</code>s, and a proof that the round trip is lossless.</p>303 <div class="cal"><span class="ic">i</span><p data-i18n="s4.why"><b>Why this is a real technique, not a puzzle.</b> Anywhere a pointer has to travel through something that only understands integers — a C callback's <code>void *</code> slot, a hardware register, an ID field in a message — the address is stored as a number and rebuilt later. <code>uintptr_t</code> exists precisely so that the round trip is guaranteed: it is the integer type the standard promises is wide enough to hold any object pointer.</p></div>304{{CODE ex01/Data.hpp|h-data|plain}}305 <p data-i18n="s4.p2">The subject insists the struct be <b>non-empty</b>, and it is doing that for a reason: after the round trip you have to read something back out. A <code>std::string</code> plus an <code>int</code> also proves the pointer still refers to a live, fully constructed object rather than a plausible-looking address.</p>306{{CODE ex01/Serializer.hpp|h-ser}}307 <ol class="notes" data-for="h-ser">308 <li data-line="4" data-i18n="s4.n1"><b><code><stdint.h></code> for <code>uintptr_t</code>.</b> The tidy C++ spelling would be <code><cstdint></code>, but that header is C++11 — in C++98 the C header is the correct choice, and <code>-std=c++98</code> will remind you if you forget.</li>309 <li data-line="5" data-i18n="s4.n2"><b>The header includes <code>Data.hpp</code> itself</b>, because its own declarations mention <code>Data</code>. Every header has to stand alone.</li>310 <li data-line="10-11" data-i18n="s4.n3"><b>The two static methods, with the exact signatures the subject prints.</b> <code>uintptr_t serialize(Data *)</code> and <code>Data *deserialize(uintptr_t)</code> — do not improve on the names or the parameter types.</li>311 <li data-line="13-17" data-i18n="s4.n4"><b>The same private-canonical trick as ex00.</b> “Not initializable by the user in any way” means all four members declared and none of them reachable.</li>312 </ol>313{{CODE ex01/Serializer.cpp|c-ser}}314 <ol class="notes" data-for="c-ser">315 <li data-line="20" data-i18n="s4.m1"><b>Pointer to integer.</b> No other cast can do this: <code>static_cast</code> refuses because the types are unrelated, and it is not a <code>const</code> question. The bit pattern is copied verbatim into the integer.</li>316 <li data-line="25" data-i18n="s4.m2"><b>Integer back to pointer.</b> The standard guarantees this specific round trip — pointer to <code>uintptr_t</code> and back — yields the original value. Round-tripping through a <i>narrower</i> integer is where the guarantee dies, which is the whole reason for <code>uintptr_t</code>.</li>317 <li data-line="3-16" data-i18n="s4.m3"><b>Four canonical members again</b>, defined here so the linker is satisfied, private in the header so nobody can reach them.</li>318 <li data-line="18-26" data-i18n="s4.m4"><b>Notice how little code this is.</b> The exercise is not about volume — the evaluator is checking that <code>reinterpret_cast</code> appears exactly twice, in these two directions, and that you can say why.</li>319 </ol>320{{CODE ex01/main.cpp|m-ser}}321 <ol class="notes" data-for="m-ser">322 <li data-line="12-13" data-i18n="s4.q1"><b>The round trip, in two lines.</b> <code>serialize(&data)</code> then <code>deserialize(raw)</code> — the subject asks for exactly this chain, not two independent calls.</li>323 <li data-line="17" data-i18n="s4.q2"><b>This line is the proof the subject demands</b>: “ensure the return value compares equal to the original pointer”. <code>std::boolalpha</code> makes it print <code>true</code> instead of <code>1</code>.</li>324 <li data-line="18-19" data-i18n="s4.q3"><b>And this is the proof that matters more</b>: reading <code>name</code> and <code>id</code> back through the rebuilt pointer shows the object is genuinely usable, not merely a matching address.</li>325 </ol>326 <div class="term">327 <div class="term-head"><i></i><i></i><i></i><span>./serialize</span></div>328<pre><span class="p">$</span> <span class="c">make && ./serialize</span>329original : <span class="g">0x7fffdf12c260</span>330serialized : <span class="g">140736935936608</span>331deserialized : <span class="g">0x7fffdf12c260</span>332same pointer : <span class="p">true</span>333name : forty-two334id : 42</pre>335 </div>336 <p data-i18n="s4.p3">The first and third lines are the same address printed in hex; the middle line is that address in decimal. Nothing is encoded, encrypted or copied — <code>reinterpret_cast</code> only changes how the compiler <i>labels</i> those bytes. The addresses differ between runs because the stack moves; what never differs is that the three lines agree.</p>337 <div class="cal bad"><span class="ic">×</span><p data-i18n="s4.trap"><b>The trap in this exercise is <code>int</code>.</b> On a 64-bit machine an <code>int</code> is 4 bytes and a pointer is 8, so <code>reinterpret_cast<int>(ptr)</code> throws half the address away. Depending on the compiler you get a hard error or a silent truncation, and the second is far worse — a program that prints <code>same pointer : false</code> once in a while.</p></div>338</section>339 340<section id="ex02" class="rv" data-acc="d">341 <div class="sec-tag"><b>05</b> <span data-i18n="s5.tag">ex02 · identify</span></div>342 <h2 data-i18n="s5.h">ex02 — asking an object what it really is</h2>343 <p data-i18n="s5.p1">A factory hands you a <code>Base *</code> that secretly points at an <code>A</code>, a <code>B</code> or a <code>C</code>. No member tells you which, and <code><typeinfo></code> is forbidden, so <code>typeid</code> is off the table. The only tool left is <code>dynamic_cast</code>, and the exercise makes you use both of its faces: the pointer form and the reference form.</p>344{{CODE ex02/Base.hpp|h-base|plain}}345{{CODE ex02/Base.cpp|c-base|plain}}346 <p data-i18n="s5.p1b">The destructor needs a definition somewhere, and that somewhere cannot be the header — so this one-line <code>.cpp</code> exists purely to obey the rule that no function body lives in a <code>.hpp</code>. It also has to be listed in the Makefile, which is why <code>ex02</code> builds three objects instead of two.</p>347 <div class="cal warn"><span class="ic">!</span><p data-i18n="s5.virt"><b>That one <code>virtual</code> is load-bearing.</b> <code>dynamic_cast</code> reads the runtime type out of the object's vtable pointer, and a class only gets a vtable if it has at least one virtual function. Delete the <code>virtual</code> keyword and the code stops compiling — <i>“source type is not polymorphic”</i>. A virtual destructor is the natural candidate anyway: without it, <code>delete p</code> through a <code>Base *</code> would not run the derived destructor. Two problems, one keyword.</p></div>348 <p data-i18n="s5.p2"><code>A</code>, <code>B</code> and <code>C</code> are three copies of the same four lines — <code>class A : public Base {};</code> — and the subject explicitly excuses them from Orthodox Canonical Form. Their emptiness is the point: nothing distinguishes them except their identity.</p>349{{CODE ex02/A.hpp|h-a|plain}}350 <p data-i18n="s5.p2b"><code>B.hpp</code> and <code>C.hpp</code> are this file with two letters changed — the class name and the include guard. Nothing is being hidden from you: all three are in the <code>code/</code> folder beside this page.</p>351{{CODE ex02/identify.hpp|h-id|plain}}352 <p data-i18n="s5.p3">Two functions with the same name and different parameter types — plain overloading. <code>identify(p)</code> picks the pointer version, <code>identify(*p)</code> picks the reference version, and the compiler decides at the call site.</p>353{{CODE ex02/identify.cpp|c-id|tall}}354 <ol class="notes" data-for="c-id">355 <li data-line="5-8" data-i18n="s5.n1"><b>Four headers, and one of them is the interesting one.</b> <code><cstdlib></code> for <code>rand</code>/<code>srand</code>, <code><ctime></code> for the seed, <code><iostream></code> for output — and <code><exception></code> instead of <code><typeinfo></code>. That swap is what keeps the forbidden header out of the file.</li>356 <li data-line="12-18" data-i18n="s5.n2"><b>Seeded once, on first call.</b> A function-local <code>static</code> survives between calls, so <code>srand</code> runs exactly once no matter how often <code>generate()</code> is used. Re-seeding on every call with a one-second-resolution clock would return the <i>same</i> class for a whole second — the classic version of this bug.</li>357 <li data-line="16" data-i18n="s5.n3"><b><code>static_cast<unsigned int>(std::time(NULL))</code>.</b> <code>time_t</code> is signed and usually wider than <code>unsigned int</code>, so the conversion is real and <code>-Wconversion</code>-style warnings are waiting for anyone who leaves it implicit. Even in the cast module, an explicit cast is the cheapest way to keep <code>-Werror</code> quiet.</li>358 <li data-line="19-27" data-i18n="s5.n4"><b><code>rand() % 3</code> and a <code>switch</code> with <code>default</code>.</b> Using <code>default</code> rather than <code>case 2</code> is what stops the compiler warning that the function might end without returning a value.</li>359 <li data-line="30-38" data-i18n="s5.n5"><b>The pointer form is a plain if / else-if chain.</b> A failed <code>dynamic_cast</code> on a pointer is not an error — it evaluates to <code>NULL</code>, which is false. So the cast <i>is</i> the test: <code>if (dynamic_cast<A *>(p))</code>. The correction sheet asks for exactly this: “should check if the cast return is NULL”.</li>360 <li data-line="40-67" data-i18n="s5.n6"><b>The reference form has to be written completely differently.</b> There is no such thing as a null reference, so failure cannot be a return value — it is an exception. Each guess therefore needs its own <code>try</code> / <code>catch</code>, which is the second thing the correction sheet asks for.</li>361 <li data-line="44" data-i18n="s5.n7"><b><code>(void)</code> on the cast result.</b> The cast is performed only for its side effect — succeed or throw — so the result is deliberately discarded, and the <code>(void)</code> says so to the compiler and to the reader.</li>362 <li data-line="48" data-i18n="s5.n8"><b><code>catch (std::exception &)</code>, not <code>catch (std::bad_cast &)</code>.</b> <code>std::bad_cast</code> is declared in <code><typeinfo></code>, the one header this exercise forbids. Catching its base class from <code><exception></code> is exactly as correct and keeps the file legal. The parameter has no name because it is never used — an unnamed one cannot trip <code>-Wunused</code>.</li>363 <li data-line="46" data-i18n="s5.n9"><b>The <code>return</code> inside the <code>try</code></b> is what stops the function after a hit. Without it, a successful <code>A</code> cast would fall through and then throw twice more while trying <code>B</code> and <code>C</code>.</li>364 </ol>365{{CODE ex02/main.cpp|m-id|plain}}366 <p data-i18n="s5.p4">Six rounds, and each round identifies the same object twice — once by pointer, once by reference — so the two lines of every pair must always agree. <code>delete p</code> in the same iteration is what keeps the leak flag off your evaluation.</p>367 <div class="term">368 <div class="term-head"><i></i><i></i><i></i><span>./identify — one real run</span></div>369<pre><span class="p">$</span> <span class="c">make && ./identify</span>370<span class="g">B</span>371<span class="g">B</span>372<span class="g">A</span>373<span class="g">A</span>374<span class="g">C</span>375<span class="g">C</span>376<span class="g">B</span>377<span class="g">B</span>378<span class="g">B</span>379<span class="g">B</span>380<span class="g">A</span>381<span class="g">A</span>382<span class="o"># the letters change every run; the pairs never disagree</span></pre>383 </div>384 385 <h3 data-i18n="s5.h2">Pointer or reference — watch the difference</h3>386 <div class="sim" id="dyn">387 <div class="sim-head">388 <span class="lbl" data-i18n="dyn.real">real object</span>389 <button class="btn on" type="button" data-real="A">new A()</button>390 <button class="btn" type="button" data-real="B">new B()</button>391 <button class="btn" type="button" data-real="C">new C()</button>392 <span class="spacer"></span>393 <span class="lbl" data-i18n="dyn.form">form</span>394 <button class="btn on" type="button" data-mode="ptr">identify(Base *p)</button>395 <button class="btn" type="button" data-mode="ref">identify(Base &p)</button>396 </div>397 <div class="sim-body">398 <div class="sim-pane">399 <h6 data-i18n="dyn.out">what each cast returns</h6>400 <div class="sim-out"></div>401 </div>402 <div class="sim-pane">403 <h6 data-i18n="dyn.how">why the code is shaped that way</h6>404 <ol class="steps"></ol>405 </div>406 </div>407 </div>408 <div class="cal"><span class="ic">i</span><p data-i18n="s5.null"><b>One thing an evaluator may push on.</b> <code>identify(Base *p)</code> prints nothing at all when <code>p</code> is <code>NULL</code>, because all three casts return <code>NULL</code>. That is defensible — the subject only ever passes a real object — but if you are asked to handle it during the modification step, the fix is two lines at the top of the function.</p></div>409</section>410 411<section id="build" class="rv">412 <div class="sec-tag"><b>06</b> <span data-i18n="s6.tag">Build & run</span></div>413 <h2 data-i18n="s6.h">The Makefile, and what to type</h2>414 <p data-i18n="s6.p1">All three Makefiles are the same file with three names swapped. The evaluator reads it before your code, because two of the “do not grade this exercise” conditions live in here: the wrong compiler, or missing flags.</p>415{{CODE ex00/Makefile|mk}}416 <p data-i18n="s6.p1b">The <code>ex01</code> and <code>ex02</code> Makefiles are this same file with three lines changed: <code>NAME</code>, <code>SRCS</code> and <code>HEADERS</code>. Nothing else differs.</p>417 <ol class="notes" data-for="mk">418 <li data-line="3-4" data-i18n="s6.n1"><b>These two lines are the ones being inspected.</b> <code>c++</code>, not <code>g++</code> or <code>clang++</code>, and all four of <code>-Wall -Wextra -Werror -std=c++98</code>. Anything missing here and the exercise is not graded at all.</li>419 <li data-line="8" data-i18n="s6.n2"><b><code>$(SRCS:.cpp=.o)</code> is a substitution reference</b>: it rewrites the source list into an object list, so a new file only ever has to be added in one place.</li>420 <li data-line="9,16" data-i18n="s6.n3"><b><code>HEADERS</code> as a prerequisite is what makes rebuilds correct.</b> Touch <code>ScalarConverter.hpp</code> and every object rebuilds. Leave it out and <code>make</code> happily links stale objects against a changed class — a bug that surfaces during a live modification and looks like sorcery.</li>421 <li data-line="16-17" data-i18n="s6.n4"><b>One pattern rule instead of a rule per file.</b> <code>$<</code> is the first prerequisite (the <code>.cpp</code>), <code>$@</code> is the target (the <code>.o</code>). The recipe line must start with a real tab.</li>422 <li data-line="22-25" data-i18n="s6.n5"><b><code>fclean</code> depends on <code>clean</code>, and <code>re</code> is <code>fclean</code> then <code>all</code></b> — the four rules 42 expects, in the order they are expected.</li>423 <li data-line="27" data-i18n="s6.n6"><b><code>.PHONY</code> keeps the rules working</b> even if a file called <code>clean</code> or <code>all</code> ever appears in the directory. Cheap insurance, and evaluators notice it.</li>424 </ol>425 <div class="term">426 <div class="term-head"><i></i><i></i><i></i><span>from the root of the project</span></div>427<pre><span class="p">$</span> <span class="c">cd ex00 && make</span>428c++ -Wall -Wextra -Werror -std=c++98 -c main.cpp -o main.o429c++ -Wall -Wextra -Werror -std=c++98 -c ScalarConverter.cpp -o ScalarConverter.o430c++ -Wall -Wextra -Werror -std=c++98 main.o ScalarConverter.o -o convert431<span class="o"># zero warnings, and the same for ex01 and ex02</span>432 433<span class="p">$</span> <span class="c">./convert 42.0f</span> <span class="o"># ex00: one literal per run</span>434<span class="p">$</span> <span class="c">cd ../ex01 && make && ./serialize</span>435<span class="p">$</span> <span class="c">cd ../ex02 && make && ./identify</span>436 437<span class="p">$</span> <span class="c">make re</span> <span class="o"># fclean + all, the rule evaluators try first</span>438<span class="p">$</span> <span class="c">make fclean</span> <span class="o"># leave the repo clean before you push</span></pre>439 </div>440 <div class="cal warn"><span class="ic">!</span><p data-i18n="s6.win">Building on Windows will not do. <code>char</code> signedness, <code>CHAR_MIN</code>, pointer width and the exact spelling of <code>nan</code> all follow the platform, and your defence happens on a 42 machine. Use the school computers, a Linux VM, or WSL — this build was verified with g++ 13.3.0 under WSL Ubuntu 24.04.</p></div>441</section>442 443<section id="leaks" class="rv">444 <div class="sec-tag"><b>07</b> <span data-i18n="s7.tag">Memory & sanitizers</span></div>445 <h2 data-i18n="s7.h">Proving there are no leaks before someone else looks</h2>446 <p data-i18n="s7.p1">The evaluation guidelines tell your evaluator, in writing, to verify the absence of memory leaks and to tick the <b>Leaks</b> flag if they find any. In this module there is exactly one allocation site — <code>new</code> inside <code>generate()</code> — and exactly one release, <code>delete p</code> in <code>main</code>. Check it yourself rather than hoping.</p>447 <div class="term">448 <div class="term-head"><i></i><i></i><i></i><span>the two checks worth running</span></div>449<pre><span class="p">$</span> <span class="c">c++ -Wall -Wextra -Werror -std=c++98 -g -fsanitize=address,undefined \450 main.cpp Base.cpp identify.cpp -o identify_san && ./identify_san</span>451<span class="o"># silence is the result you want: no leak report, no UB report</span>452 453<span class="p">$</span> <span class="c">valgrind --leak-check=full ./identify</span>454<span class="o"># All heap blocks were freed -- no leaks are possible</span></pre>455 </div>456 <div class="grid g3">457 <div class="card"><span class="k" data-i18n="s7.k1">verified</span><h4><span class="bd ok">warnings</span></h4><p data-i18n="s7.c1">All three exercises compile with <code>-Wall -Wextra -Werror -std=c++98</code> and emit nothing.</p></div>458 <div class="card"><span class="k" data-i18n="s7.k2">verified</span><h4><span class="bd ok">asan + ubsan</span></h4><p data-i18n="s7.c2">Rebuilt with <code>-fsanitize=address,undefined</code> and run: no leaks, no undefined behaviour, including on the overflow and NaN inputs.</p></div>459 <div class="card"><span class="k" data-i18n="s7.k3">verified</span><h4><span class="bd ok">forbidden symbols</span></h4><p data-i18n="s7.c3">A grep across all fourteen files for <code>printf</code>, <code>alloc</code>, <code>free</code>, <code>using namespace</code>, <code>friend</code>, <code>typeinfo</code> and <code>typeid</code> returns nothing.</p></div>460 </div>461 <div class="cal"><span class="ic">i</span><p data-i18n="s7.note">Sanitizers catch more than leaks, which is why they are worth the extra command here. The undefined-behaviour checker is what would flag a bad <code>isprint</code> call or an out-of-range <code>static_cast</code> — the two mistakes this module is designed to teach you to avoid.</p></div>462</section>463 464<section id="traps" class="rv">465 <div class="sec-tag"><b>08</b> <span data-i18n="s8.tag">Ten ways to lose points</span></div>466 <h2 data-i18n="s8.h">Ten ways this module is normally lost</h2>467 <div class="grid g2">468 <div class="card"><h4><span class="bd bad">01</span> <span data-i18n="s8.a1">Casting before checking</span></h4><p data-i18n="s8.a2">Writing <code>static_cast<int>(value)</code> and then asking whether the result looks sane. Out-of-range conversion is undefined behaviour, so the answer is already meaningless. Test the <code>double</code> first, always.</p></div>469 <div class="card"><h4><span class="bd bad">02</span> <span data-i18n="s8.b1">Using <code>std::isnan</code></span></h4><p data-i18n="s8.b2">It is C++11 and <code>-std=c++98</code> will reject it. The portable test is <code>value != value</code>.</p></div>470 <div class="card"><h4><span class="bd bad">03</span> <span data-i18n="s8.c1"><code>isprint</code> on a negative char</span></h4><p data-i18n="s8.c2">Undefined behaviour for any negative value other than <code>EOF</code>. Cast through <code>unsigned char</code> — that is why there are two casts on one line.</p></div>471 <div class="card"><h4><span class="bd bad">04</span> <span data-i18n="s8.d1">A body inside a header</span></h4><p data-i18n="s8.d2">One tiny getter written inline in the <code>.hpp</code> is a zero for that exercise. Templates are the only exception, and there are none here.</p></div>472 <div class="card"><h4><span class="bd bad">05</span> <span data-i18n="s8.e1">A public constructor in ex00 or ex01</span></h4><p data-i18n="s8.e2">Both subjects say the class must not be instantiable. Leaving the default constructor public — or simply not declaring it — hands the evaluator their first question and the wrong answer.</p></div>473 <div class="card"><h4><span class="bd bad">06</span> <span data-i18n="s8.f1"><code>reinterpret_cast<int></code> in ex01</span></h4><p data-i18n="s8.f2">Four bytes cannot hold an eight-byte address. <code>uintptr_t</code> is the type the standard defines for exactly this round trip.</p></div>474 <div class="card"><h4><span class="bd bad">07</span> <span data-i18n="s8.g1">A <code>Base</code> with no virtual function</span></h4><p data-i18n="s8.g2">Without a vtable there is no runtime type, so <code>dynamic_cast</code> will not even compile. The virtual destructor solves this and the delete-through-base problem together.</p></div>475 <div class="card"><h4><span class="bd bad">08</span> <span data-i18n="s8.h1">Touching <code><typeinfo></code> in ex02</span></h4><p data-i18n="s8.h2">Explicitly forbidden — which also rules out <code>catch (std::bad_cast &)</code>, since that class is declared there. Catch <code>std::exception &</code>.</p></div>476 <div class="card"><h4><span class="bd bad">09</span> <span data-i18n="s8.i1">A pointer inside <code>identify(Base &)</code></span></h4><p data-i18n="s8.i2">The subject bans it in that function, so <code>&p</code> or a <code>dynamic_cast<A *>(&p)</code> is an instant fail even though it works. The reference cast plus <code>try</code>/<code>catch</code> is the required shape.</p></div>477 <div class="card"><h4><span class="bd bad">10</span> <span data-i18n="s8.j1">Re-seeding <code>rand</code> on every call</span></h4><p data-i18n="s8.j2">Calling <code>srand(time(NULL))</code> inside <code>generate()</code> without a guard makes every object in the same second identical. It looks like a broken random and is really a broken seed.</p></div>478 </div>479</section>480 481<section id="defend" class="rv">482 <div class="sec-tag"><b>09</b> <span data-i18n="s9.tag">The defence</span></div>483 <h2 data-i18n="s9.h">The questions on the correction sheet, answered</h2>484 <p data-i18n="s9.p1">These are not invented. They are the checks printed on the CPP Module 06 evaluation form, plus the follow-ups that this particular implementation invites.</p>485 <details class="qa"><summary data-i18n="s9.q1">Did you create a class with a private constructor and static methods?</summary><div class="ans"><p data-i18n="s9.a1">Yes, in both ex00 and ex01. All four canonical members are declared in the <code>private</code> section of the header and defined in the <code>.cpp</code>, so the class satisfies Orthodox Canonical Form while <code>ScalarConverter c;</code> refuses to compile. The only public member is the <code>static</code> one the subject asks for.</p></div></details>486 <details class="qa"><summary data-i18n="s9.q2">Where is <code>static_cast</code> actually used, and why not a C cast?</summary><div class="ans"><p data-i18n="s9.a2">In the four printers of <code>ScalarConverter.cpp</code>: <code>static_cast<char></code>, <code>static_cast<unsigned char></code>, <code>static_cast<int></code>, <code>static_cast<float></code>, plus <code>static_cast<double></code> on the char branch of <code>convert</code>. A C cast would compile too, but it would also silently perform a <code>reinterpret_cast</code> if I ever pointed it at unrelated types. <code>static_cast</code> only permits conversions between related types, so the compiler keeps checking my work.</p></div></details>487 <details class="qa"><summary data-i18n="s9.q3">Is <code>std::strtod</code> allowed?</summary><div class="ans"><p data-i18n="s9.a3">Yes. The ex00 sheet explicitly authorises “any function to convert from a string to an int, a float, or a double”, and the module's forbidden list names only <code>*printf()</code>, <code>*alloc()</code> and <code>free()</code>. <code>strtod</code> comes from <code><cstdlib></code>, so it is used as <code>std::strtod</code>. If a C++-flavoured version is preferred, <code>std::istringstream</code> is a drop-in replacement.</p></div></details>488 <details class="qa"><summary data-i18n="s9.q4">Why <code>uintptr_t</code> rather than <code>long</code> or <code>int</code>?</summary><div class="ans"><p data-i18n="s9.a4">Because it is the only integer type the standard defines as being able to hold an object pointer and give it back unchanged. <code>int</code> is four bytes here and would truncate an address; <code>long</code> happens to work on 64-bit Linux and would break on Windows. The subject names <code>uintptr_t</code> for that reason.</p></div></details>489 <details class="qa"><summary data-i18n="s9.q5">Show me the two <code>reinterpret_cast</code>s and prove the round trip.</summary><div class="ans"><p data-i18n="s9.a5"><code>serialize</code> does <code>Data *</code> → <code>uintptr_t</code>, <code>deserialize</code> does <code>uintptr_t</code> → <code>Data *</code>, and <code>main</code> prints <code>same pointer : true</code> from <code>ptr == &data</code>. It then reads <code>ptr->name</code> and <code>ptr->id</code>, which is the stronger proof: the address is not merely equal, the object behind it is intact.</p></div></details>490 <details class="qa"><summary data-i18n="s9.q6">How does <code>dynamic_cast</code> know what the object really is?</summary><div class="ans"><p data-i18n="s9.a6">Every object of a polymorphic class carries a hidden pointer to its class's vtable, and the RTTI record hangs off that. <code>dynamic_cast</code> follows it and compares. This is why <code>Base</code> needs at least one virtual function: without one there is no vtable, no runtime type, and the cast does not compile.</p></div></details>491 <details class="qa"><summary data-i18n="s9.q7">Why does the pointer version use <code>if</code> and the reference version use <code>try</code>/<code>catch</code>?</summary><div class="ans"><p data-i18n="s9.a7">Because they report failure differently, and that asymmetry is the lesson of the exercise. A failed pointer cast returns <code>NULL</code>, which is a testable value, so the cast itself is the condition. A reference cannot be null, so the only channel left is an exception — <code>std::bad_cast</code>. Same operator, two failure protocols.</p></div></details>492 <details class="qa"><summary data-i18n="s9.q8">Where is <code><typeinfo></code>? It is forbidden.</summary><div class="ans"><p data-i18n="s9.a8">Nowhere — grep the directory. That also rules out writing <code>catch (std::bad_cast &)</code>, because <code>std::bad_cast</code> is declared in that header. I catch its base class, <code>std::exception</code>, from <code><exception></code>: same behaviour, no forbidden include.</p></div></details>493 <details class="qa"><summary data-i18n="s9.q9">Why does <code>./convert 2147483647</code> print <code>float: 2147483648.0f</code>?</summary><div class="ans"><p data-i18n="s9.a9">Because a <code>float</code> has 24 bits of mantissa and <code>INT_MAX</code> needs 31, so the value is not representable and the conversion rounds to the nearest one that is — which is one higher. The <code>int</code> line is exact, the <code>float</code> line is honest about the precision loss. This is the module's real subject matter, not a bug.</p></div></details>494 <details class="qa"><summary data-i18n="s9.q10">Why is <code>3.14159</code> printed as <code>3.1</code>?</summary><div class="ans"><p data-i18n="s9.a10">Because the printers use <code>std::fixed</code> with <code>setprecision(1)</code>, which is the format the subject's own examples show (<code>0.0f</code>, <code>42.0f</code>, <code>42.0</code>). Only the display is rounded; the stored <code>double</code> is untouched. Raising it to <code>setprecision(6)</code> is a one-character change if a grader prefers that.</p></div></details>495 <details class="qa"><summary data-i18n="s9.q11">What happens with no argument, or with an empty string?</summary><div class="ans"><p data-i18n="s9.a11">With no argument, <code>main</code> prints <code>Usage: ./convert <literal></code> and returns 1 — deliberately, because <code>argv[1]</code> would be <code>NULL</code> and constructing a <code>std::string</code> from a null pointer is undefined behaviour. With an explicit empty string, <code>detect</code> falls through to <code>INVALID_LIT</code> and the “is not a char, an int, a float nor a double” line is printed.</p></div></details>496 <details class="qa"><summary data-i18n="s9.q12">Are there leaks?</summary><div class="ans"><p data-i18n="s9.a12">One <code>new</code> in <code>generate()</code>, one <code>delete p</code> in the same loop iteration in <code>main</code>, and nothing else allocates. Rebuilt under <code>-fsanitize=address,undefined</code> the program is silent. <code>Base</code>'s destructor is virtual, so deleting through a <code>Base *</code> really does run <code>A</code>'s, <code>B</code>'s or <code>C</code>'s destructor.</p></div></details>497 498 <h3 data-i18n="s9.h2">Be ready to change the code in front of them</h3>499 <p data-i18n="s9.p2">The subject's own submission chapter warns that “a brief modification of the project may occasionally be requested” — a few minutes' work, decided on the spot. These five are the likely ones for module 06. Rehearse them; do not read them.</p>500 <div class="tw"><table>501 <thead><tr><th data-i18n="s9.t1">If they ask for…</th><th data-i18n="s9.t2">Where you go, and what you write</th></tr></thead>502 <tbody>503 <tr><td data-i18n="s9.d1">a fifth output line, <code>long:</code></td><td data-i18n="s9.d2">Copy <code>printInt</code> to <code>printLong</code>, swap the bounds for <code>LONG_MIN</code>/<code>LONG_MAX</code>, cast to <code>long</code>, and add one call at the bottom of <code>convert</code>. Four lines, no other file touched.</td></tr>504 <tr><td data-i18n="s9.d3"><code>identify(Base *)</code> to handle <code>NULL</code></td><td data-i18n="s9.d4">Add <code>if (!p) { std::cout << "unknown" << std::endl; return ; }</code> as the first statement. Mention that the reference version cannot receive a null at all.</td></tr>505 <tr><td data-i18n="s9.d5">a fourth class <code>D</code></td><td data-i18n="s9.d6">Copy <code>A.hpp</code> to <code>D.hpp</code>, include it in <code>identify.cpp</code>, widen <code>rand() % 3</code> to <code>% 4</code>, add <code>case 2: return (new C());</code> so <code>default</code> becomes <code>D</code>, and add one branch to each <code>identify</code>. Add nothing to the Makefile — there is no new <code>.cpp</code>.</td></tr>506 <tr><td data-i18n="s9.d7">two decimals instead of one</td><td data-i18n="s9.d8"><code>setprecision(1)</code> → <code>setprecision(2)</code> in <code>printFloat</code> and <code>printDouble</code>. Say out loud that this changes the display only.</td></tr>507 <tr><td data-i18n="s9.d9">a third field in <code>Data</code></td><td data-i18n="s9.d10">Add the member to the struct, set it in <code>main</code>, print it through the deserialised pointer. Nothing in <code>Serializer</code> changes, and being able to explain <i>why</i> nothing changes is the actual answer.</td></tr>508 </tbody>509 </table></div>510</section>511 512<section id="check" class="rv">513 <div class="sec-tag"><b>10</b> <span data-i18n="s10.tag">Checklist</span></div>514 <h2 data-i18n="s10.h">Before you push, and before you defend</h2>515 <p data-i18n="s10.p1">Ticks are stored in this browser, so you can close the page and come back to it.</p>516 <div id="checklist">517 <div class="chk-bar"><span class="count">0 / 0</span><span class="track"><span class="fill"></span></span><button class="btn reset" type="button" data-i18n="s10.reset">reset</button></div>518 <div class="chks">519 <label class="chk" data-k="a1"><input type="checkbox"><span data-i18n="s10.i1"><b>make re</b> in all three directories, zero warnings, with <code>-Wall -Wextra -Werror -std=c++98</code>.</span></label>520 <label class="chk" data-k="a2"><input type="checkbox"><span data-i18n="s10.i2"><b>make fclean</b> run last, so no <code>.o</code> file and no binary is in the repository.</span></label>521 <label class="chk" data-k="a3"><input type="checkbox"><span data-i18n="s10.i3">Directories named exactly <code>ex00</code>, <code>ex01</code>, <code>ex02</code>, and the class files named after their classes.</span></label>522 <label class="chk" data-k="a4"><input type="checkbox"><span data-i18n="s10.i4">Every header has an include guard, and compiles on its own.</span></label>523 <label class="chk" data-k="a5"><input type="checkbox"><span data-i18n="s10.i5">No function body in any header. No <code>using namespace</code>. No <code>friend</code>.</span></label>524 <label class="chk" data-k="a6"><input type="checkbox"><span data-i18n="s10.i6">No <code>*printf</code>, no <code>*alloc</code>, no <code>free</code> — grep for them, do not assume.</span></label>525 <label class="chk" data-k="a7"><input type="checkbox"><span data-i18n="s10.i7">No STL container and no <code><algorithm></code> anywhere in the module.</span></label>526 <label class="chk" data-k="a8"><input type="checkbox"><span data-i18n="s10.i8"><code><typeinfo></code> appears in no file of ex02, and no <code>typeid</code> either.</span></label>527 <label class="chk" data-k="a9"><input type="checkbox"><span data-i18n="s10.i9">ScalarConverter and Serializer both have four private canonical members and public static methods only.</span></label>528 <label class="chk" data-k="a10"><input type="checkbox"><span data-i18n="s10.i10">The three subject examples reproduced exactly: <code>0</code>, <code>nan</code>, <code>42.0f</code>.</span></label>529 <label class="chk" data-k="a11"><input type="checkbox"><span data-i18n="s10.i11">Overflow and pseudo-literals tried by hand: <code>2147483648</code>, <code>-inff</code>, <code>nanf</code>, <code>128</code>, <code>hello</code>.</span></label>530 <label class="chk" data-k="a12"><input type="checkbox"><span data-i18n="s10.i12">ex01 prints <code>same pointer : true</code> and reads both members back.</span></label>531 <label class="chk" data-k="a13"><input type="checkbox"><span data-i18n="s10.i13">ex02 run several times: the two lines of every pair always match.</span></label>532 <label class="chk" data-k="a14"><input type="checkbox"><span data-i18n="s10.i14">Leak-checked with sanitizers or valgrind, and clean.</span></label>533 <label class="chk" data-k="a15"><input type="checkbox"><span data-i18n="s10.i15">You can name each cast, say what checks it, and say what it costs — out loud, without the page open.</span></label>534 <label class="chk" data-k="a16"><input type="checkbox"><span data-i18n="s10.i16">You have practised one live modification from the table above, timed.</span></label>535 </div>536 </div>537</section>538 539<footer>540 <div class="row">541 <span data-i18n="foot.1">Written from the code in this repository — every terminal block on this page is a real run.</span>542 </div>543 <div class="row" style="margin-top:8px">544 <span data-i18n="foot.2">42 · 1337 · CPP Module 06 · C++98</span>545 <span data-i18n="foot.3">the guide is rebuilt from the sources, so it cannot drift from them</span>546 </div>547</footer>548 </main>549 </div>550</div>551 552<a class="up" href="#start" aria-label="Back to top">↑</a>553<script src="assets/ar.js"></script>554<script src="assets/app.js"></script>555</body>556</html>557 