<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Posts on 番茄🍅杂货铺</title>
    <link>https://blog.tomatostore.top/posts/</link>
    <description>Recent content in Posts on 番茄🍅杂货铺</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-cn</language>
    <lastBuildDate>Thu, 30 Oct 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.tomatostore.top/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>C&#43;&#43;：CRTP（奇异递归模板模式）实用入门</title>
      <link>https://blog.tomatostore.top/posts/2025/10/cpp-crtp-intruduction/</link>
      <pubDate>Thu, 30 Oct 2025 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2025/10/cpp-crtp-intruduction/</guid>
      <description>用清晰示例解释 CRTP 的概念、优势、典型用法与局限</description>
      <content:encoded><![CDATA[<p>在看代码时遇到这样一种写法，不是很理解，把模版类自己用作模版参数：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyStubService</span> <span class="o">:</span> <span class="k">public</span> <span class="n">DbusServer</span><span class="o">&lt;</span><span class="n">MyStubService</span><span class="o">&gt;</span> <span class="p">{};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>咨询了 DeepSeek 后得知这是 C++ 中的 <strong>CRTP（Curiously Recurring Template Pattern，奇异递归模板模式）</strong>。</p>
<p>CRTP 是一种借助模板实现<strong>编译时多态</strong>的技术，核心思想是：<strong>类模板以其派生类作为模板参数</strong>。</p>
<h2 id="crtp-的优势">CRTP 的优势</h2>
<ol>
<li><strong>性能优势</strong>：避免了虚函数调用的运行时开销</li>
<li><strong>编译时优化</strong>：编译器可以进行更好的内联和优化</li>
<li><strong>类型安全</strong>：在编译时捕获类型错误</li>
<li><strong>减少二进制大小</strong>：不需要虚函数表</li>
</ol>
<h2 id="crtp-基本语法结构">CRTP 基本语法结构</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">Derived</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Base</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 基类定义
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Derived</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Base</span><span class="o">&lt;</span><span class="n">Derived</span><span class="o">&gt;</span> <span class="p">{</span>  <span class="c1">// 关键：派生类将自己作为模板参数传递给基类
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 派生类定义
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="crtp-主要应用场景">CRTP 主要应用场景</h2>
<h3 id="1-静态多态编译时多态">1. 静态多态（编译时多态）</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">Derived</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Animal</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">speak</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">Derived</span><span class="o">*&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">speakImpl</span><span class="p">();</span>  <span class="c1">// 编译时绑定
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">Derived</span><span class="o">*&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">runImpl</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Dog</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Animal</span><span class="o">&lt;</span><span class="n">Dog</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">speakImpl</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Woof!&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">runImpl</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Running on four legs&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Cat</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Animal</span><span class="o">&lt;</span><span class="n">Cat</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">speakImpl</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Meow!&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">runImpl</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Running gracefully&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">makeAnimalSpeak</span><span class="p">(</span><span class="n">Animal</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&amp;</span> <span class="n">animal</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">animal</span><span class="p">.</span><span class="n">speak</span><span class="p">();</span>  <span class="c1">// 编译时确定调用哪个实现
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="2-对象计数">2. 对象计数</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Counter</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">int</span> <span class="n">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">protected</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Counter</span><span class="p">()</span> <span class="p">{</span> <span class="o">++</span><span class="n">count</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">Counter</span><span class="p">(</span><span class="k">const</span> <span class="n">Counter</span><span class="o">&amp;</span><span class="p">)</span> <span class="p">{</span> <span class="o">++</span><span class="n">count</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">~</span><span class="n">Counter</span><span class="p">()</span> <span class="p">{</span> <span class="o">--</span><span class="n">count</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">int</span> <span class="n">getCount</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="n">count</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 静态成员初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">Counter</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">class</span> <span class="nc">MyClass</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Counter</span><span class="o">&lt;</span><span class="n">MyClass</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 自动获得计数功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">AnotherClass</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Counter</span><span class="o">&lt;</span><span class="n">AnotherClass</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 独立的计数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="3-链式调用">3. 链式调用</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">Derived</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Chainable</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Derived</span><span class="o">&amp;</span> <span class="n">self</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">Derived</span><span class="o">&amp;&gt;</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">Derived</span><span class="o">&amp;</span> <span class="n">setX</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 具体实现由派生类提供
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">self</span><span class="p">().</span><span class="n">setXImpl</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nf">self</span><span class="p">();</span>  <span class="c1">// 返回派生类引用，支持链式调用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">Derived</span><span class="o">&amp;</span> <span class="n">setY</span><span class="p">(</span><span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">self</span><span class="p">().</span><span class="n">setYImpl</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nf">self</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Point</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Chainable</span><span class="o">&lt;</span><span class="n">Point</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Point</span><span class="o">&amp;</span> <span class="n">setXImpl</span><span class="p">(</span><span class="kt">int</span> <span class="n">x_</span><span class="p">)</span> <span class="p">{</span> <span class="n">x</span> <span class="o">=</span> <span class="n">x_</span><span class="p">;</span> <span class="k">return</span> <span class="o">*</span><span class="k">this</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">Point</span><span class="o">&amp;</span> <span class="n">setYImpl</span><span class="p">(</span><span class="kt">int</span> <span class="n">y_</span><span class="p">)</span> <span class="p">{</span> <span class="n">y</span> <span class="o">=</span> <span class="n">y_</span><span class="p">;</span> <span class="k">return</span> <span class="o">*</span><span class="k">this</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">print</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Point(&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;, &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">y</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;)&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用：链式调用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Point</span> <span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">p</span><span class="p">.</span><span class="n">setX</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="n">setY</span><span class="p">(</span><span class="mi">20</span><span class="p">).</span><span class="n">print</span><span class="p">();</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="4-实现运算符重载">4. 实现运算符重载</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">Derived</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Comparable</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="k">operator</span><span class="o">==</span><span class="p">(</span><span class="k">const</span> <span class="n">Derived</span><span class="o">&amp;</span> <span class="n">other</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">Derived</span><span class="o">*&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">equals</span><span class="p">(</span><span class="n">other</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="k">operator</span><span class="o">!=</span><span class="p">(</span><span class="k">const</span> <span class="n">Derived</span><span class="o">&amp;</span> <span class="n">other</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="o">!</span><span class="k">static_cast</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">Derived</span><span class="o">*&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">equals</span><span class="p">(</span><span class="n">other</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Person</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Comparable</span><span class="o">&lt;</span><span class="n">Person</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">age</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Person</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a</span><span class="p">)</span> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">age</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="nf">equals</span><span class="p">(</span><span class="k">const</span> <span class="n">Person</span><span class="o">&amp;</span> <span class="n">other</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">name</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">name</span> <span class="o">&amp;&amp;</span> <span class="n">age</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">age</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="5-静态接口检查约束派生类的必备成员">5. 静态接口检查（约束派生类的必备成员）</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">Derived</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MustHaveFoo</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">callFoo</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">static_assert</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">is_same_v</span><span class="o">&lt;</span><span class="k">decltype</span><span class="p">(</span><span class="o">&amp;</span><span class="n">Derived</span><span class="o">::</span><span class="n">foo</span><span class="p">),</span> <span class="kt">void</span><span class="p">(</span><span class="n">Derived</span><span class="o">::*</span><span class="p">)()</span><span class="o">&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="s">&#34;Derived must define void foo() member&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">Derived</span><span class="o">*&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">foo</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">OK</span> <span class="o">:</span> <span class="k">public</span> <span class="n">MustHaveFoo</span><span class="o">&lt;</span><span class="n">OK</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">foo</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>也可用 C++20 Concepts 直接约束：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">concept</span> <span class="n">HasFoo</span> <span class="o">=</span> <span class="k">requires</span><span class="p">(</span><span class="n">T</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span> <span class="n">t</span><span class="p">.</span><span class="n">foo</span><span class="p">();</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="n">HasFoo</span> <span class="n">Derived</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MustHaveFoo2</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="6-空基类优化ebo">6. 空基类优化（EBO）</h3>
<p>CRTP 基类常为空（仅编译期行为），作为空基类被继承时通常不占用额外空间，有助于零开销地叠加特性。</p>
<h3 id="7-bartonnackman-技巧基于-crtp-的友元运算符">7. Barton–Nackman 技巧（基于 CRTP 的友元运算符）</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">Derived</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Orderable</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">friend</span> <span class="kt">bool</span> <span class="k">operator</span><span class="o">&lt;</span><span class="p">(</span><span class="k">const</span> <span class="n">Derived</span><span class="o">&amp;</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="n">Derived</span><span class="o">&amp;</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">a</span><span class="p">.</span><span class="n">less</span><span class="p">(</span><span class="n">b</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Node</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Orderable</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">less</span><span class="p">(</span><span class="k">const</span> <span class="n">Node</span><span class="o">&amp;</span> <span class="n">other</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">key</span> <span class="o">&lt;</span> <span class="n">other</span><span class="p">.</span><span class="n">key</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">key</span><span class="p">{</span><span class="mi">0</span><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="crtp-的局限性">CRTP 的局限性</h2>
<ol>
<li><strong>编译时绑定</strong>：不能在运行时动态改变行为</li>
<li><strong>模板复杂性</strong>：错误信息可能难以理解</li>
<li><strong>代码膨胀</strong>：每个派生类都会实例化一个不同的基类模板</li>
</ol>
<h2 id="最佳实践与常见陷阱">最佳实践与常见陷阱</h2>
<ul>
<li><strong>只在需要零开销和编译期已知类型时使用</strong>，否则优先简单的虚函数或组合。</li>
<li>基类中用 <code>static_cast&lt;Derived*&gt;</code>/<code>static_cast&lt;const Derived*&gt;</code> 调用派生实现；不要用 <code>dynamic_cast</code>。</li>
<li>避免通过基类指针/引用“多态地”存放不同派生对象（CRTP 不是为此设计）。</li>
<li>多重继承下的 CRTP：注意菱形结构与方法可见性，必要时用 <code>using Derived::method</code> 暴露成员。</li>
<li>为 CRTP 基类提供 <code>protected</code> 构造函数，避免被直接实例化。</li>
<li>若需接口约束，结合 <code>static_assert</code> 或 Concepts，尽早在编译期报错。</li>
<li>链式 API 返回 <code>Derived&amp;</code>/<code>const Derived&amp;</code>，避免返回 <code>*this</code> 的基类引用。</li>
</ul>
<h2 id="何时使用--不使用-crtp">何时使用 / 不使用 CRTP</h2>
<ul>
<li>使用：
<ul>
<li><strong>静态多态</strong>、热点路径需要最大化内联与去虚拟化。</li>
<li>可复用的“mixin 特性”（日志、计数、对比、序列化接口）按需叠加。</li>
<li>需要在编译期强制派生类提供某些接口（静态约束）。</li>
</ul>
</li>
<li>不使用：
<ul>
<li>需要在运行时根据配置/输入切换具体实现。</li>
<li>需要通过基类容器存放异构对象并统一调度。</li>
<li>团队对模板元编程不熟，维护成本高于收益。</li>
</ul>
</li>
</ul>
<h2 id="与虚函数多态的对比">与虚函数多态的对比</h2>
<ul>
<li><strong>分发时机</strong>：
<ul>
<li>虚函数是运行时分发（需 vtable），可通过基类指针/引用统一处理异构对象。</li>
<li>CRTP 是编译时分发，要求在编译期就知道具体类型，换来零开销。</li>
</ul>
</li>
<li><strong>扩展方式</strong>：
<ul>
<li>虚函数通过覆盖虚方法扩展行为。</li>
<li>CRTP 通过“静态接口”约束派生类提供实现，基类用 <code>static_cast&lt;Derived*&gt;</code> 调用。</li>
</ul>
</li>
<li><strong>适用场景</strong>：
<ul>
<li>运行时需要真正的动态绑定 → 选虚函数。</li>
<li>性能敏感、类型在编译期已知、可模板化 → 选 CRTP。</li>
</ul>
</li>
</ul>
<h2 id="faq">FAQ</h2>
<ul>
<li>为什么叫“奇异递归”？
<ul>
<li>因为基类模板的实参“递归地”引用了派生类本身，看起来很“奇怪”，但这是合法且有用的模板技巧。</li>
</ul>
</li>
<li>可以多层套 CRTP 吗？
<ul>
<li>可以，但要控制复杂度，注意方法名冲突与可读性。</li>
</ul>
</li>
<li>和 Concepts 是什么关系？
<ul>
<li>Concepts 负责“约束”，CRTP 负责“复用 + 分发”。两者可结合使用，提升错误信息友好度与可维护性。</li>
</ul>
</li>
</ul>
<h2 id="小结">小结</h2>
<p>CRTP 是 C++ 模板元编程中的重要模式，在性能敏感场景（如游戏开发、高频交易等）尤为常见。它在不引入虚函数开销的前提下提供多态式的扩展能力，适合用于“编译期就能决定行为”的问题。但需要权衡模板复杂度与可维护性，避免过度抽象。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>利用AI高效搭建Hugo Landing Page实录</title>
      <link>https://blog.tomatostore.top/posts/2025/06/amclock-landing-page/</link>
      <pubDate>Sun, 22 Jun 2025 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2025/06/amclock-landing-page/</guid>
      <description>记录使用AI助手开发AMClock产品落地页的完整过程，包括技术选型、自动化部署和问题排查</description>
      <content:encoded><![CDATA[<h2 id="背景">背景</h2>
<p>为了推广我的App AMClock，我决定搭建一个产品落地页（Landing Page）。整个开发和部署过程中，AI助手（Cursor）是高效开发、排查问题的得力伙伴。</p>
<hr>
<h2 id="技术选型与准备">技术选型与准备</h2>
<ul>
<li><strong>静态站点生成器</strong>：Hugo，因之前搭建 blog 用的 hugo，相对熟悉，另外其构建速度快、支持多语言、支持屏幕适配</li>
<li><strong>主题</strong>：hugo-saasify-theme（fork到自己仓库，方便后续定制和维护）。</li>
<li><strong>前端工具</strong>：TailwindCSS + PostCSS，非单独引入，hugo-saasify-theme主题中使用。</li>
<li><strong>自动化部署</strong>：GitHub Actions 实现CI/CD，自动同步到腾讯云服务器。</li>
<li><strong>安全管理</strong>：敏感信息（如服务器IP、SSH密钥）通过 GitHub Secrets 管理。</li>
</ul>
<hr>
<h2 id="先来看看成品效果">先来看看成品效果</h2>
<ul>
<li>访问地址：
<ul>
<li>English: <a href="https://amclock.online">https://amclock.online</a></li>
<li>中文: <a href="https://amclock.online/zh">https://amclock.online/zh</a>
<img loading="lazy" src="/images/amclock-online-screenshot.png" alt="Screenshot of amclock online"  /></li>
</ul>
</li>
</ul>
<hr>
<h2 id="项目开发历程">项目开发历程</h2>
<h3 id="第一阶段基础搭建多语言支持6月15日">第一阶段：基础搭建、多语言支持（6月15日）</h3>
<ul>
<li><strong>中文版本实现</strong>：添加了完整的中文翻译文件（<code>i18n/zh.yaml</code>、<code>data/features/zh.yaml</code>）</li>
<li><strong>产品图片资源</strong>：上传了18个产品相关图片，包括功能展示图、Hero区域图片和Logo</li>
<li><strong>多语言配置</strong>：在 <code>hugo.toml</code> 中配置了中英文切换功能</li>
</ul>
<h3 id="第二阶段功能完善与优化图片设计6月16-21日">第二阶段：功能完善与优化、图片设计（6月16-21日）</h3>
<ul>
<li><strong>CTA按钮国际化</strong>：实现了Call-to-Action按钮的多语言支持</li>
<li><strong>功能特性优化</strong>：动态调整功能特性展示，提升用户体验</li>
<li><strong>主题定制</strong>：使用fork的主题分支，应用了多个修复和改进</li>
</ul>
<h3 id="第三阶段云端手动部署自动化发布6月22日">第三阶段：云端手动部署、自动化发布（6月22日）</h3>
<ul>
<li><strong>手动部署</strong>：上传 public 文件夹到服务器，修改 Nginx 配置文件</li>
<li><strong>GitHub Actions配置</strong>：创建了完整的 <code>.github/workflows/deploy.yml</code> 部署流程</li>
<li><strong>依赖安装优化</strong>：在Hugo构建前增加 <code>npm install</code> 步骤，解决PostCSS依赖缺失问题</li>
<li><strong>部署流程完善</strong>：最终实现了从代码提交到自动部署的完整CI/CD流程</li>
</ul>
<h3 id="总结---常见坑处理">总结 - 常见&quot;坑&quot;处理</h3>
<ul>
<li><strong>baseURL 问题</strong>：AI建议在部署命令中指定 <code>--baseURL</code>，避免生产环境链接指向本地地址。</li>
<li><strong>多语言菜单跳转</strong>：AI帮助我修正菜单配置，确保中英文切换正常。</li>
<li><strong>404 页面跳转</strong>：AI协助自定义 <code>404.html</code>，并用 Hugo 变量生成正确跳转链接，提升用户体验。</li>
</ul>
<h3 id="references">References</h3>
<h4 id="github-action-deployyml">Github action <code>deploy.yml</code></h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy to Tencent Cloud</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">public ] </span><span class="w"> </span><span class="c"># 当推送到主分支时触发</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Checkout code</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">submodules</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">  </span><span class="c"># 初始化submodules，包括你的主题</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">fetch-depth</span><span class="p">:</span><span class="w"> </span><span class="m">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Setup Hugo v0.135.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-hugo@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">hugo-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;0.135.0&#39;</span><span class="w">  </span><span class="c"># 指定与你本地相同的版本</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">extended</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Setup Node.js</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-node@v3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">node-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;16&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install dependencies</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm install</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build Hugo site</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        hugo --minify --baseURL https://amclock.online/
</span></span></span><span class="line"><span class="cl"><span class="sd">        </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy to server</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">appleboy/ssh-action@v0.1.5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HOST }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.USERNAME }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.KEY }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.PORT }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          # 备份当前版本
</span></span></span><span class="line"><span class="cl"><span class="sd">          if [ -d &#34;/var/www/myconfig/amclock.online.public&#34; ]; then
</span></span></span><span class="line"><span class="cl"><span class="sd">            sudo cp -r /var/www/myconfig/amclock.online.public /var/www/myconfig/amclock.online.public.backup.$(date +%Y%m%d_%H%M%S)
</span></span></span><span class="line"><span class="cl"><span class="sd">          fi
</span></span></span><span class="line"><span class="cl"><span class="sd">          
</span></span></span><span class="line"><span class="cl"><span class="sd">          # 清理目标目录
</span></span></span><span class="line"><span class="cl"><span class="sd">          sudo rm -rf /var/www/myconfig/amclock.online.public/*
</span></span></span><span class="line"><span class="cl"><span class="sd">          </span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload files</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">appleboy/scp-action@v0.1.4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HOST }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.USERNAME }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.KEY }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.PORT }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;public/*&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">target</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/var/www/myconfig/amclock.online.public&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">strip_components</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set permissions and reload nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">appleboy/ssh-action@v0.1.5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HOST }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.USERNAME }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.KEY }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.PORT }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          sudo chmod -R 755 /var/www/myconfig/amclock.online.public
</span></span></span><span class="line"><span class="cl"><span class="sd">          sudo systemctl reload nginx
</span></span></span><span class="line"><span class="cl"><span class="sd">          echo &#34;部署完成！网站已更新到 https://amclock.online&#34; </span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    
    <item>
      <title>Mac 上浏览器可访问github，但命令行 SSH 连接超时</title>
      <link>https://blog.tomatostore.top/posts/2025/04/github-cmd-access-fix/</link>
      <pubDate>Wed, 30 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2025/04/github-cmd-access-fix/</guid>
      <description>using SSH over 443 port to access Github</description>
      <content:encoded><![CDATA[<h2 id="问题表现">问题表现</h2>
<p>可以通过浏览器正常访问<code>github.com</code>。
命令行中执行 <code>git pull</code> 时出现以下错误：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh: connect to host github.com port 22: Operation timed out
</span></span><span class="line"><span class="cl">fatal: Could not <span class="nb">read</span> from remote repository.
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="核心解决步骤">核心解决步骤</h2>
<h3 id="第一步验证-ssh-端口封锁">第一步：验证 SSH 端口封锁</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nc -vz github.com <span class="m">22</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>预期正常响应：<code>Connected to github.com...</code></li>
<li>异常响应：<code>Operation timed out</code> 表示端口 22 被阻断: <code>nc: connectx to github.com port 22 (tcp) failed: Operation timed out</code></li>
</ul>
<h3 id="第二步配置-ssh-over-443-端口">第二步：配置 SSH over 443 端口</h3>
<ol>
<li>创建 SSH 配置文件</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vi ~/.ssh/config
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="2">
<li>写入配置内容</li>
</ol>
<pre tabindex="0"><code class="language-config" data-lang="config">Host github.com
  Hostname ssh.github.com
  Port 443
  User git
</code></pre><h3 id="第三步测试新配置">第三步：测试新配置</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh -T git@github.com
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>如果有询问是否修改<code>known_hosts</code>, 输入 <code>yes</code> ：
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">This host key is known by the following other names/addresses:
</span></span><span class="line"><span class="cl">  ~/.ssh/known_hosts:28: github.com
</span></span><span class="line"><span class="cl">Are you sure you want to <span class="k">continue</span> connecting <span class="o">(</span>yes/no/<span class="o">[</span>fingerprint<span class="o">])</span>? yes
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>成功标志：<code>Hi username! You've successfully authenticated...</code></li>
</ul>
<hr>
<h2 id="补充方案">补充方案</h2>
<h3 id="使用-https-协议代替-ssh">使用 HTTPS 协议代替 SSH</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git config --global url.<span class="s2">&#34;https://github.com/&#34;</span>.insteadOf git@github.com:
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    
    <item>
      <title>修复 RevenueCat 国内访问，App Store Connect 审核加速</title>
      <link>https://blog.tomatostore.top/posts/2024/12/revenuecat-connection-fix-with-proxy/</link>
      <pubDate>Sun, 15 Dec 2024 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2024/12/revenuecat-connection-fix-with-proxy/</guid>
      <description>Fix connection to RevenueCat from China and expedite App Store Connect review</description>
      <content:encoded><![CDATA[<p>2024年12月突发，国内无法访问 RevenueCat 导致很多 App 无法进行正常 IAP。</p>
<blockquote>
<p>In December 2024, RevenueCat became inaccessible in China, affecting IAP functionality in many apps.</p></blockquote>
<p>官方给出了紧急修复方案：https://www.revenuecat.com/docs/getting-started/configuring-sdk#configuration-for-users-in-mainland-china</p>
<blockquote>
<p>Official emergency fix: <a href="https://www.revenuecat.com/docs/getting-started/configuring-sdk#configuration-for-users-in-mainland-china">https://www.revenuecat.com/docs/getting-started/configuring-sdk#configuration-for-users-in-mainland-china</a></p></blockquote>
<p><img loading="lazy" src="/images/revenuecat-proxy.png" alt="Configuration for users in Mainland China"  /></p>
<p>你说巧不巧，就在一周前，我的一个 iOS app 刚通过审核发布成功，就正好使用了 RevenueCat。🤣
没办法，现改实现方案移除 RC 肯定不是一时半会能搞定的，再说这个状态还不确定是不是常态。所以紧急按照官方方案提交了修改。（虽然实际上我的 app 也没啥用户，也没有收到任何消息抱怨说无法购买）。</p>
<blockquote>
<p>Coincidentally, my iOS app using RevenueCat was just approved a week ago. 🤣 Instead of removing RC, I quickly applied the official fix, though my app has few users and no complaints yet.</p></blockquote>
<p>修改后测试效果是可以去到产品信息了（我没有 RC 的 paywall，只是定义了产品），然后按照之前学习过的 App Store Connect 加急审核的步骤走了一遍，没想到的是，加急申请之后，仅仅3分钟就审核通过了🫨。这里给水果审核点个赞，然后再分享加速审核流程。</p>
<blockquote>
<p>After the fix, product info was accessible again. I requested an expedited review, and surprisingly, it was approved in just 3 minutes! 🫨 Here&rsquo;s how to request expedited review:</p></blockquote>
<ol>
<li>登录 <a href="https://developer.apple.com/">https://developer.apple.com/</a> 后，找到页面下方的 Contact Us。</li>
</ol>
<blockquote>
<p>Go to <a href="https://developer.apple.com/">https://developer.apple.com/</a> and find &ldquo;Contact Us&rdquo; at the bottom.</p></blockquote>
<p><img loading="lazy" src="/images/apple-contact-us.png" alt="contact us"  /></p>
<ol start="2">
<li>选择 <strong>App 审核</strong> -&gt; <strong>App 审核加快请求</strong></li>
</ol>
<blockquote>
<p>Choose <strong>App Review</strong> -&gt; <strong>Request Expedited Review</strong></p></blockquote>
<p><img loading="lazy" src="/images/apple-app-review.png" alt="App review"  /></p>
<ol start="3">
<li>点击 <strong>联系 App Review</strong>，在 <strong>App Information</strong>中选择对应的 App 即可。</li>
</ol>
<blockquote>
<p>Click <strong>Contact App Review</strong> and select your app.</p></blockquote>
<p><img loading="lazy" src="/images/apple-app-info.png" alt="app info"  /></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>HowTo: make links between Apple notes</title>
      <link>https://blog.tomatostore.top/posts/2024/11/hyperlink-between-apple-notes/</link>
      <pubDate>Sun, 24 Nov 2024 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2024/11/hyperlink-between-apple-notes/</guid>
      <description>如何在苹果🍎的备忘录间链接跳转</description>
      <content:encoded><![CDATA[<h2 id="how-to-add-a-link-to-other-note">How to add a link to other note</h2>
<h3 id="1-typing-">1. Typing &ldquo;&raquo;&rdquo;</h3>
<p>Type &ldquo;&raquo;&rdquo; in the body of your note, enter the title of the note that you want to link to, and select it from the list. It works both on iOS and macOS.
<img loading="lazy" src="/images/typeing-add-link-in-notes.png" alt="typing &raquo;"  /></p>
<h3 id="2-menu">2. Menu</h3>
<p>On iOS, long press to open the options menu. Swipe left the menu to find <code>Add Link</code>.

  <img loading="lazy" src="https://cdsassets.apple.com/live/7WUAS350/images/ios/ios-18-iphone-16-pro-notes-formatting-add-link-callout.png" alt="Add link on iOS"  /></p>
<p>On macOS, right click in a note and choose <code>Add link</code> in the options menu.
<img loading="lazy" src="/images/notes-option-menu-macos.png" alt="options menu on macOS"  /></p>
<h2 id="how-to-navigate-back-to-previous-note">How to navigate back to previous note</h2>
<p>Now we have a link to another note, it will jump to the linked note when clicking the link.
Then, how can we go back to the previous note?</p>
<p>On iOS, click <code>...</code> to open menu, select <code>Recent Notes</code>.
<img loading="lazy" src="/images/navigation-back-in-notes-ios.jpeg" alt="recent notes on iOS"  /></p>
<p>On macOS, open <code>View</code> menu to select <code>Previous Note</code>.
<img loading="lazy" src="/images/navigate-back-in-notes-macos.png" alt="navigate between notes on macOS"  /></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Ask ChatGPT/Cursor: Preprocessor Macro Doesn&#39;t Work in Swift</title>
      <link>https://blog.tomatostore.top/posts/2024/10/preprocessor-macro-in-swift/</link>
      <pubDate>Tue, 08 Oct 2024 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2024/10/preprocessor-macro-in-swift/</guid>
      <description>Xcode 中如何定义 Swift 适用的预处理宏</description>
      <content:encoded><![CDATA[<h2 id="the-problem">The Problem</h2>
<p>I defined the preprocessor macro <code>USING_REVENUE_CAT=1</code> in my project target&rsquo;s Build Settings under <strong>Preprocessor Macros</strong>.</p>
<p>However, when I tried to use it in my <code>Swift</code> file with the following code, I received an error indicating that the module <code>SwiftyStoreKit</code> could not be found. This was puzzling because I expected the macro to work similarly to how it does in <code>Objective-C</code>.</p>
<p><img loading="lazy" src="/images/Preprocessor-Macros.png" alt="Preprocessor-Macros in Xcode"  /></p>
<h2 id="the-solution">The Solution</h2>
<p>Asked both <code>ChatGPT</code> and <code>Cursor</code> for help, and they gave me similar answers as following:</p>
<p><code>Swift</code> does not recognize preprocessor macros defined in the Build Settings the same way <code>Objective-C</code> does. Instead, you need to define custom flags specifically for <code>Swift</code>.</p>
<h3 id="steps-to-define-a-preprocessor-macro-for-swift">Steps to Define a Preprocessor Macro for Swift</h3>
<ol>
<li>
<p><strong>Navigate to Build Settings:</strong>
Open your project in Xcode and select your target. Then, go to the <strong>Build Settings</strong> tab.</p>
</li>
<li>
<p><strong>Find Swift Compiler - Custom Flags:</strong>
Scroll down to the <strong>Swift Compiler - Custom Flags</strong> section.</p>
</li>
<li>
<p><strong>Add the Custom Flag:</strong>
In the <strong>Other Swift Flags</strong> field, add the following:</p>
<pre tabindex="0"><code>USING_REVENUE_CAT
</code></pre></li>
</ol>
<p>This tells the <code>Swift</code> compiler to define the <code>USING_REVENUE_CAT</code> flag, allowing you to use it in your <code>Swift</code> code.</p>
<h3 id="updated-swift-code">Updated Swift Code</h3>
<p>After adding the custom flag, your <code>Swift</code> code should work as intended:</p>
<p><img loading="lazy" src="/images/Swift-Compiler_Customer-Flags.png" alt="Swift-Compiler_Customer-Flags"  /></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>解决 Xcode 15.4 在 macOS 14.5 上开发 Adobe Illustrator Plugin 时 Python 命令未找到问题</title>
      <link>https://blog.tomatostore.top/posts/2024/09/python/</link>
      <pubDate>Sun, 01 Sep 2024 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2024/09/python/</guid>
      <description>Solving the Python Command Not Found Issue When Developing Adobe Illustrator Plugin with Xcode 15.4 on macOS 14.5</description>
      <content:encoded><![CDATA[<h2 id="一问题描述">一、问题描述</h2>
<p>在使用 Xcode 15.4 在 macOS 14.5 上尝试开发 Adobe Illustrator Plugin 时，在编译 Adobe Illustrator 2023 SDK 的 sample 过程中出现错误：</p>
<pre tabindex="0"><code>/Users/aidy/Workspace/Adobe Illustrator 2023 SDK/samplecode/StrokeFilter/build/StrokeFilter.build/Default/StrokeFilter.build/Script-FDD9160721AC093D0018B958.sh: line 2: python: command not found
Command PhaseScriptExecution failed with a nonzero exit code
</code></pre><p>查看 <code>Script-FDD9160721AC093D0018B958.sh</code> 文件内容为：</p>
<pre tabindex="0"><code>#!/bin/sh
python ../../tools/pipl/create_pipl.py -input &#39;[{&#34;name&#34;:&#34;StrokeFilter&#34;,&#34;entry_point&#34; : &#34;PluginMain&#34;}]&#39;
</code></pre><h2 id="二尝试过程">二、尝试过程</h2>
<p>首先怀疑是没有安装 <code>xcode command line tool</code>，但确认已经安装过了。
接着考虑可能是没有安装 <code>Python</code>。通过 <code>brew install python</code> 安装了 <code>Python3</code>，但 <code>Xcode</code> 中仍然提示<code>python: command not found</code>。
从 <code>Adobe Community</code> 搜索得知这里需要 <code>Python2</code>，于是使用 <code>pyenv</code> 安装了 <code>Python2</code>，并设置 <code>pyenv global 2.7.18</code>。在 <code>Terminal</code> 中执行 <code>python -V</code> 输出是 <code>Python 2.7.18</code>，然而 <code>Xcode build</code> 依旧报错。
尝试了 <code>sudo ln /Users/&lt;user&gt;/.pyenv/shims/python /Library/Developer/CommandLineTools/usr/bin/python</code>，但没有作用。</p>
<h2 id="三最终解决办法">三、最终解决办法</h2>
<p>通过 <code>sudo ln /Users/&lt;user&gt;/.pyenv/shims/python /usr/local/bin/python</code> 设置 <code>/usr/local/bin</code> 的 <code>link</code>，解决了问题。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>[Linux] Share environment variables with root user</title>
      <link>https://blog.tomatostore.top/posts/2024/07/share-env-when-running-cmd-with-sudo/</link>
      <pubDate>Thu, 25 Jul 2024 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2024/07/share-env-when-running-cmd-with-sudo/</guid>
      <description>Keep current user&amp;#39;s env variables when running sudo command</description>
      <content:encoded><![CDATA[<h2 id="linux-share-environment-variables-with-root-user">[Linux] Share environment variables with root user</h2>
<h4 id="problem">Problem</h4>
<p>There are some environment variabels (e.g. <code>http_proxy</code>) set for current user on Ubuntu 20.4, but when running some commands with <code>sudo</code> (like <code>sudo apt update</code>), those environment variables are not available anymore.</p>
<h4 id="solution">Solution</h4>
<ol>
<li><code>sudo -E</code></li>
</ol>
<p>If you are running commands manually, it&rsquo;s possible to preserve evn setting by adding <code>-E</code> option.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo -E apt update
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="2">
<li><code>sudo visudo</code></li>
</ol>
<p>Using option <code>-E</code> may not work in the cases that the commands with <code>sudo</code> are run in script.
To make the specific environment settings shared with <code>sudo</code>, it can be configured with <code>visudo</code>.</p>
<p>Using following command:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo visudo
</span></span></code></pre></td></tr></table>
</div>
</div><p>In opened nano editor, find the line begins with <code>Defaults        env_keep</code>, modify it as following to make <code>http_proxy</code> and <code>https_proxy</code> settings be keeped when running commands using <code>sudo</code>:</p>
<pre tabindex="0"><code>Defaults        env_keep += &#34;https_proxy http_proxy&#34;
</code></pre><p>Then, when running <code>sudo apt update</code>, those proxy env variables will kept.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to enable AddressSanitizer(ASan) for C&#43;&#43; Project</title>
      <link>https://blog.tomatostore.top/posts/2024/05/how-to-enable-asan-in-cpp-project/</link>
      <pubDate>Fri, 24 May 2024 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2024/05/how-to-enable-asan-in-cpp-project/</guid>
      <description>The tool to detect runtime memory leak</description>
      <content:encoded><![CDATA[<p>To integrate AddressSanitizer(ASan) to an existing project, just add the compile option <code>-fsanitize=address</code> and generate debug infor with <code>-g</code> in <code>CMakeLists.txt</code>.</p>
<pre tabindex="0"><code>option(ENABLE_ASAN &#34;enable AddressSanitizer&#34; OFF)

if(ENABLE_ASAN)
    set(CMAKE_CXX_FLAGS &#34;${CMAKE_CXX_FLAGS} -fsanitize=address&#34;)
    set(CMAKE_CXX_FLAGS &#34;-O -g&#34;)
    set(CMAKE_C_FLAGS &#34;-O -g&#34;)
endif()
</code></pre><p>Compile the project:</p>
<pre tabindex="0"><code>mkdir build
cd build
cmake -DENABLE_ASAN=ON ..
cmake --build .
</code></pre><p>After the build, using following command to start the program:</p>
<pre tabindex="0"><code>ASAN_OPTIONS=detect_leaks=1 ./a.out
</code></pre><p>It will print detail info when memory leak occurs.</p>
<pre tabindex="0"><code>=================================================================
==12345==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 40 byte(s) in 1 object(s) allocated from:
    #0 0x7f3d8b4fbb90 in __interceptor_new (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xde90)
    #1 0x4006d6 in createLeak /path/to/your/project/main.cpp:4
    #2 0x4006f8 in main /path/to/your/project/main.cpp:9
    #3 0x7f3d8b0d283f in __libc_start_main (/lib64/libc.so.6+0x2383f)

SUMMARY: AddressSanitizer: 40 byte(s) leaked in 1 allocation(s).
</code></pre>]]></content:encoded>
    </item>
    
    <item>
      <title>Install docker in Ubuntu 20 and config network proxy</title>
      <link>https://blog.tomatostore.top/posts/2024/05/install-docker-on-ubuntu-20/</link>
      <pubDate>Tue, 21 May 2024 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2024/05/install-docker-on-ubuntu-20/</guid>
      <description>在Ubuntu 20中安装 Docker、配置网络代理</description>
      <content:encoded><![CDATA[<h2 id="steps-to-install-docker-on-ubuntu-20">steps to install docker on Ubuntu 20</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">sudo apt-get install apt-transport-https ca-certificates curl software-properties-common
</span></span><span class="line"><span class="cl">curl -fsSL https://download.docker.com/linux/ubuntu/gpg <span class="p">|</span> sudo apt-key add -
</span></span><span class="line"><span class="cl">sudo add-apt-repository <span class="s2">&#34;deb [arch=amd64] https://download.docker.com/linux/ubuntu </span><span class="k">$(</span>lsb_release -cs<span class="k">)</span><span class="s2"> stable&#34;</span>
</span></span><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">sudo apt-get install docker-ce
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="verify-the-installation">verify the installation</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo systemctl status docker
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="add-current-user-to-docker-group">add current user to docker group</h2>
<ul>
<li>in order to avoid adding <code>sudo</code> when running docker command</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo usermod -aG docker <span class="si">${</span><span class="nv">USER</span><span class="si">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="network-proxy-settings">Network proxy settings</h2>
<h3 id="dockerd-proxy">Dockerd Proxy</h3>
<p>To configure the proxy for <code>dockerd</code>, which is managed by <code>systemd</code>, create and edit a configuration file:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo mkdir -p /etc/systemd/system/docker.service.d
</span></span><span class="line"><span class="cl">sudo touch /etc/systemd/system/docker.service.d/proxy.conf
</span></span></code></pre></td></tr></table>
</div>
</div><p>Add the following to <code>proxy.conf</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Service]</span>
</span></span><span class="line"><span class="cl"><span class="na">Environment</span><span class="o">=</span><span class="s">&#34;HTTP_PROXY=http://proxy.example.com:8080/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">Environment</span><span class="o">=</span><span class="s">&#34;HTTPS_PROXY=http://proxy.example.com:8080/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">Environment</span><span class="o">=</span><span class="s">&#34;NO_PROXY=localhost,127.0.0.1,.example.com&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Replace <code>proxy.example.com:8080</code> with your proxy address.</p>
<h3 id="container-proxy">Container Proxy</h3>
<p>To configure proxy settings for containers, edit <code>~/.docker/config.json</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;proxies&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&#34;default&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="nt">&#34;httpProxy&#34;</span><span class="p">:</span> <span class="s2">&#34;http://proxy.example.com:8080&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nt">&#34;httpsProxy&#34;</span><span class="p">:</span> <span class="s2">&#34;http://proxy.example.com:8080&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nt">&#34;noProxy&#34;</span><span class="p">:</span> <span class="s2">&#34;localhost,127.0.0.1,.example.com&#34;</span>
</span></span><span class="line"><span class="cl">   <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>For container runtime proxies, use the <code>-e</code> option to set environment variables.</p>
<h3 id="docker-build-proxy">Docker Build Proxy</h3>
<p>For <code>docker build</code>, inject proxy settings using <code>--build-arg</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">docker build . <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --build-arg <span class="s2">&#34;HTTP_PROXY=http://proxy.example.com:8080/&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --build-arg <span class="s2">&#34;HTTPS_PROXY=http://proxy.example.com:8080/&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    --build-arg <span class="s2">&#34;NO_PROXY=localhost,127.0.0.1,.example.com&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -t your/image:tag
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="applying-changes">Applying Changes</h3>
<p>To apply the changes:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo systemctl daemon-reload
</span></span><span class="line"><span class="cl">sudo systemctl restart docker
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="trouble-shooting">Trouble shooting</h2>
<h3 id="when-running-command-sudo-apt-update-it-has-following-error">when running command <code>sudo apt update</code>, it has following error</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">Err:5 https://download.docker.com/linux/ubuntu focal Release                                            
</span></span><span class="line"><span class="cl">  Could not handshake: The TLS connection was non-properly terminated. <span class="o">[</span>IP: &lt;xx.xxx.xx.xxx&gt; 3128<span class="o">]</span>
</span></span><span class="line"><span class="cl">Hit:6 http://security.ubuntu.com/ubuntu focal-security InRelease                                        
</span></span><span class="line"><span class="cl">Hit:7 http://us.archive.ubuntu.com/ubuntu focal-security InRelease
</span></span><span class="line"><span class="cl">Reading package lists... Done
</span></span><span class="line"><span class="cl">E: The repository <span class="s1">&#39;https://download.docker.com/linux/ubuntu focal Release&#39;</span> does not have a Release file.
</span></span><span class="line"><span class="cl">N: Updating from such a repository can<span class="err">&#39;</span>t be <span class="k">done</span> securely, and is therefore disabled by default.
</span></span><span class="line"><span class="cl">N: See apt-secure<span class="o">(</span>8<span class="o">)</span> manpage <span class="k">for</span> repository creation and user configuration details.
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="solution">Solution</h4>
<p>According to <a href="https://askubuntu.com/a/1289675/1578724">this answer</a>, modify <code>/etc/apt/apt.config</code> to make sure the proxy settings are correct, especially the <code>https</code> settings.</p>
<ul>
<li><strong>Wrong config:</strong></li>
</ul>
<pre tabindex="0"><code>Acquire::http::proxy &#34;http://proxy.mydom.it:8080&#34;;
Acquire::https::proxy &#34;https://proxy.mydom.it:8080&#34;;
</code></pre><ul>
<li><strong>Good config:</strong></li>
</ul>
<pre tabindex="0"><code>Acquire::http::proxy &#34;http://proxy.mydom.it:8080&#34;;
Acquire::https::proxy &#34;http://proxy.mydom.it:8080&#34;;
</code></pre>]]></content:encoded>
    </item>
    
    <item>
      <title>VSCode: 正则搜索匹配多行多个关键字</title>
      <link>https://blog.tomatostore.top/posts/2024/05/vscode-regexp-match-multiple-words-in-diff-lines/</link>
      <pubDate>Tue, 07 May 2024 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2024/05/vscode-regexp-match-multiple-words-in-diff-lines/</guid>
      <description>Match two words in diff lines in VSCode</description>
      <content:encoded><![CDATA[<p>场景：<code>QML</code> 项目中搜索 <code>Button</code> 控件并使用了 <code>background</code> 属性的代码。</p>
<p>正则表达式写法： <code>Button.*(?:\n.*(?:\n.*)){0,10}.*background</code></p>
<p>匹配效果：</p>
<p><img loading="lazy" src="/images/vscode-regexp-match-multiple-words-in-diff-lines.png" alt="vscode-regexp-match-multiple-words-in-diff-lines"  /></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>DP接口无法连接显示器的解决办法</title>
      <link>https://blog.tomatostore.top/posts/2024/03/cannot-connect-display-via-dp-channel/</link>
      <pubDate>Fri, 08 Mar 2024 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2024/03/cannot-connect-display-via-dp-channel/</guid>
      <description>Cannot connect to display via DP channel</description>
      <content:encoded><![CDATA[<p>AOC的显示器之前一直可以用miniDP转DP的线连接MacBookPro，突然有一天就连不上了。
尝试了重现插拔、重启电脑、关闭/打开显示器，都不行。所以怀疑是线的问题，买了一根新的线，结果还是不行。尝试了HDMI线连接是没问题的。</p>
<p>在准备联系显示器维修之前发现了这个网页 <a href="https://zhuanlan.zhihu.com/p/466826180">https://zhuanlan.zhihu.com/p/466826180</a>，没想到的是在<strong>拔掉显示器电源线再插回去</strong>之后，竟然就好了。。。</p>
<p>My AOC monitor, which used to connect to my MacBook Pro with a miniDP-to-DP cable, suddenly stopped working one day.</p>
<p>I tried re-plugging the cable, restarting my computer, and turning the monitor off and on, but nothing worked. So I suspected it might be the cable&rsquo;s fault and bought a new one, but it still didn&rsquo;t work. I tried connecting with an HDMI cable and it worked fine.</p>
<p>Before I was about to contact the monitor repair service, I found this webpage: <a href="https://zhuanlan.zhihu.com/p/466826180">https://zhuanlan.zhihu.com/p/466826180</a>. Surprisingly, after <strong>unplugging the monitor&rsquo;s power cord and plugging it back in</strong>, it worked again&hellip;</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>iOS开发-加载其他语言的String资源</title>
      <link>https://blog.tomatostore.top/posts/2023/11/ios-localized-string-in-diff-languages/</link>
      <pubDate>Sat, 11 Nov 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/11/ios-localized-string-in-diff-languages/</guid>
      <description>Load Settings.bundle in diff languages</description>
      <content:encoded><![CDATA[<p>在开发iOS应用时，我们可能会遇到需要使用不同语言的字符串资源的情况。比如，当iOS的语言设置为A，而我们的应用需要使用语言B。
这里有一个语音计算器，用户可以在计算时选择不同的语音朗读语言，而无需改变整个iOS的语言设置。例如，如果我们的手机语言设置为英语，但我们希望计算器能用中文朗读计算结果。</p>
<a href="https://apps.apple.com/cn/app/id1501166219">
  <img src="/images/calculator-settings.jpg" align="center" style="width: 80%; height: 80%;"/>
</a>
<p>为了实现这个功能，我们可以使用iOS的本地化机制。有两种常见的解决方案：</p>
<ol>
<li><strong>加载相应语言的资源文件</strong></li>
</ol>
<p>我们可以为每种语言创建一个资源文件，并在其中添加我们的本地化字符串。然后，我们可以根据用户选择的语言，从相应的资源文件中加载字符串。</p>
<p>在Swift中，我们可以使用如下方式实现：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">extension</span> <span class="nc">String</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">localized</span><span class="p">:</span> <span class="nb">String</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// TalkingNumCalUserDefaults.voiceLang has value like &#39;en-US&#39;, &#39;fr-FR&#39;, &#39;de-DE&#39; etc.</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="kd">let</span> <span class="nv">lang</span> <span class="p">=</span> <span class="n">Locale</span><span class="p">(</span><span class="n">identifier</span><span class="p">:</span> <span class="n">TalkingNumCalUserDefaults</span><span class="p">.</span><span class="n">voiceLang</span><span class="p">).</span><span class="n">languageCode</span> <span class="p">{</span> <span class="c1">// e.g. en, jp, zh ...</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="kd">let</span> <span class="nv">path</span> <span class="p">=</span> <span class="n">Bundle</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">path</span><span class="p">(</span><span class="n">forResource</span><span class="p">:</span> <span class="n">lang</span><span class="p">,</span> <span class="n">ofType</span><span class="p">:</span> <span class="s">&#34;lproj&#34;</span><span class="p">),</span> <span class="kd">let</span> <span class="nv">bundle</span> <span class="p">=</span> <span class="n">Bundle</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">path</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="kd">let</span> <span class="nv">string</span> <span class="p">=</span> <span class="n">bundle</span><span class="p">.</span><span class="n">localizedString</span><span class="p">(</span><span class="n">forKey</span><span class="p">:</span> <span class="kc">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="n">table</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">string</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">NSLocalizedString</span><span class="p">(</span><span class="kc">self</span><span class="p">,</span> <span class="n">comment</span><span class="p">:</span> <span class="s">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>这个解决方案需要我们预先准备好所有可能用到的语言的资源文件。当用户选择了一种语言后，我们从对应的资源文件中加载字符串。如果没有找到对应的资源文件，我们就返回一个未本地化的字符串。</p>
<ol start="2">
<li><strong>设置Bundle的语言</strong></li>
</ol>
<p>我们可以先通过用户选择的语言设置Bundle的语言，然后直接从Bundle中获取本地化的字符串。</p>
<p>在Swift中，我们可以使用如下方式实现：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">extension</span> <span class="nc">String</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">localized</span><span class="p">:</span> <span class="nb">String</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// TalkingNumCalUserDefaults.voiceLang has value like &#39;en-US&#39;, &#39;fr-FR&#39;, &#39;de-DE&#39; etc.</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="kd">let</span> <span class="nv">lang</span> <span class="p">=</span> <span class="n">Locale</span><span class="p">(</span><span class="n">identifier</span><span class="p">:</span> <span class="n">TalkingNumCalUserDefaults</span><span class="p">.</span><span class="n">voiceLang</span><span class="p">).</span><span class="n">languageCode</span> <span class="p">{</span> <span class="c1">// e.g. en, jp, zh ...</span>
</span></span><span class="line"><span class="cl">            <span class="n">Bundle</span><span class="p">.</span><span class="n">setLanguage</span><span class="p">(</span><span class="n">lang</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">NSLocalizedString</span><span class="p">(</span><span class="kc">self</span><span class="p">,</span> <span class="n">tableName</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="n">bundle</span><span class="p">:</span> <span class="n">Bundle</span><span class="p">.</span><span class="n">main</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="s">&#34;&#34;</span><span class="p">,</span> <span class="n">comment</span><span class="p">:</span> <span class="s">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>这个解决方案需要我们在每次用户选择一种新的语言时，都重新设置一次Bundle的语言。然后，我们从Bundle中获取本地化的字符串。如果没有找到对应的字符串，我们就返回一个未本地化的字符串。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Variadic Template and Fold Expression in C&#43;&#43;</title>
      <link>https://blog.tomatostore.top/posts/2023/08/cpp-variadic-template-and-fold-expression/</link>
      <pubDate>Mon, 21 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/08/cpp-variadic-template-and-fold-expression/</guid>
      <description>C&#43;&#43; 可变模板和折叠表达式</description>
      <content:encoded><![CDATA[<p>遇到如下代码，对于 <code>func2</code> 没有看懂。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">DataStruct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DataStruct</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">T</span> <span class="n">n</span><span class="p">)</span><span class="o">:</span> <span class="n">index</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="n">value</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">index</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">T</span> <span class="n">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">func1</span><span class="p">(</span><span class="n">string</span> <span class="n">prefix</span><span class="p">,</span> <span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">T</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">__FUNCTION__</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;(&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">prefix</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;, &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;, &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">n</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;)&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span><span class="p">...</span> <span class="n">T</span><span class="o">&gt;</span> <span class="c1">// mark 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">auto</span> <span class="n">func2</span><span class="p">(</span><span class="n">string</span> <span class="n">p</span><span class="p">,</span> <span class="k">const</span> <span class="n">DataStruct</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&amp;&amp;</span><span class="p">...</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// mark 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">(</span><span class="n">func1</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">s</span><span class="p">.</span><span class="n">index</span><span class="p">,</span> <span class="n">s</span><span class="p">.</span><span class="n">value</span><span class="p">),</span> <span class="p">...);</span>  <span class="c1">// mark 3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">func2</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span><span class="p">(</span><span class="s">&#34;hi &#34;</span><span class="p">,</span> <span class="n">DataStruct</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="s">&#34;Tom&#34;</span><span class="p">),</span> <span class="n">DataStruct</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">29</span><span class="p">,</span> <span class="s">&#34;Mason&#34;</span><span class="p">),</span> <span class="n">DataStruct</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">999</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;done</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// execute result
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">func1(hi , 12, Tom)
</span></span></span><span class="line"><span class="cl"><span class="cm">func1(hi , 29, Mason)
</span></span></span><span class="line"><span class="cl"><span class="cm">func1(hi , 100, 999)
</span></span></span><span class="line"><span class="cl"><span class="cm">done
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>查询后得知是 <code>C++</code> 的可变模板(<code>variadic template</code>)和折叠表达式(<code>fold expression</code>)。</p>
<h2 id="可变模板variadic-template">可变模板（Variadic Template）</h2>
<p>可变模板是 C++11 引入的功能，允许模板接受<em>不定数量的模板参数</em>。这对于需要处理多个参数类型或数量的情况非常有用。例如：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">coutX</span><span class="p">()</span> <span class="p">{}</span> <span class="c1">// 终止条件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">Types</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">coutX</span><span class="p">(</span><span class="k">const</span> <span class="n">T</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">,</span> <span class="k">const</span> <span class="n">Types</span><span class="o">&amp;</span><span class="p">...</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">t</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="c1">// cout 1st argument
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">coutX</span><span class="p">(</span><span class="n">args</span><span class="p">...);</span> <span class="c1">// 对可变参数递归调用 coutX
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>其中非模板重载函数 <code>contX()</code> 是递归调用的终止条件。</p>
<h2 id="折叠表达式fold-expression">折叠表达式（Fold Expression）</h2>
<p>折叠表达式是 <code>C++17</code> 为配合 <code>Variadic Template</code> 使用引入的功能，它允许在模板展开过程中进行一元或二元操作。折叠表达式有两种形式：左折叠和右折叠。</p>
<p>对应的展开关系如下：</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Fold Expression</th>
          <th style="text-align: right">Evaluation</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><code>(E op ...)</code></td>
          <td style="text-align: right"><code>(E1 op (... op (EN-1 op EN)))</code></td>
      </tr>
      <tr>
          <td style="text-align: left"><code>(... op E)</code></td>
          <td style="text-align: right"><code>(((E1 op E2) op ...) op EN)</code></td>
      </tr>
      <tr>
          <td style="text-align: left"><code>(E op ... op I)</code></td>
          <td style="text-align: right"><code>(E1 op (... op (EN−1 op (EN op I))))</code></td>
      </tr>
      <tr>
          <td style="text-align: left"><code>(I op ... op E)</code></td>
          <td style="text-align: right"><code>((((I op E1) op E2) op ...) op EN)</code></td>
      </tr>
  </tbody>
</table>
<p>其中</p>
<ul>
<li>左折叠形式：<code>(expr op ...)</code>，其中 <code>expr</code> 是一个表达式，<code>op</code> 是二元操作符。</li>
<li>右折叠形式：<code>(... op expr)</code>，同样，<code>expr</code> 是一个表达式，<code>op</code> 是二元操作符。</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span><span class="p">...</span> <span class="n">T</span><span class="o">&gt;</span> <span class="c1">// variadic template
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">auto</span> <span class="n">func2</span><span class="p">(</span><span class="n">string</span> <span class="n">p</span><span class="p">,</span> <span class="k">const</span> <span class="n">DataStruct</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&amp;&amp;</span><span class="p">...</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// variadic template
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">(</span><span class="n">func1</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">s</span><span class="p">.</span><span class="n">index</span><span class="p">,</span> <span class="n">s</span><span class="p">.</span><span class="n">value</span><span class="p">),</span> <span class="p">...);</span> <span class="c1">// fold expression
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在上面的例子中，<code>func2</code> 函数的实现使用了右折叠表达式<code>(expr op ...)</code>。其对应关系如下：</p>
<ul>
<li><code>expr</code> : <code>func1(p, s.index, s.value)</code></li>
<li><code>op</code> : <code>, </code></li>
<li><code>...</code> : <code>...</code></li>
</ul>
<p>另一个 <code>(I op ... op E)</code> 折叠表达式的例子：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span><span class="p">...</span> <span class="n">Values</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">sum</span><span class="p">(</span><span class="n">Values</span> <span class="k">const</span><span class="o">&amp;</span><span class="p">...</span> <span class="n">values</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="mi">0</span> <span class="o">+</span> <span class="p">...</span> <span class="o">+</span> <span class="n">values</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">sum</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> <span class="c1">// same as (0 + 1 + 2 + 3 + 4)
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>其中</p>
<ul>
<li><code>I</code> : <code>0</code></li>
<li><code>op</code> : <code>+</code></li>
<li><code>...</code> : <code>...</code></li>
<li><code>op</code> : <code>+</code></li>
<li><code>E</code> : <code>values</code></li>
</ul>
<p>对于前面 <code>contX</code> 的例子，在使用 <code>C++17</code> 的 <code>Fold Expression</code> 后就不需要再手动实现递归和终止条件，可简化如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">coutX</span><span class="p">(</span><span class="k">const</span> <span class="n">Types</span><span class="o">&amp;</span><span class="p">...</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="p">...</span> <span class="o">&lt;&lt;</span> <span class="n">args</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    
    <item>
      <title>How to run fsck to check and fix filesystem of Ubuntu 20</title>
      <link>https://blog.tomatostore.top/posts/2023/08/how-to-run-fsck-in-ubuntu20-to-fix-filesystem/</link>
      <pubDate>Wed, 02 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/08/how-to-run-fsck-in-ubuntu20-to-fix-filesystem/</guid>
      <description>/dev/sda1: UNEXPECTED INCONSISTENCY: RUN fsck MANUALLY. fsck exited with status code 4</description>
      <content:encoded><![CDATA[<p>当在 <code>VMware</code> 虚拟机中运行 <code>Ubuntu 20</code> 时，有时可能会遇到启动时出现 &ldquo;需要 fsck&rdquo; 的问题。</p>
<pre tabindex="0"><code>/dev/sda1 contains a file system with errors, check forced.
Inodes that were part of a corrupted orphan linked list found.

/dev/sda1: UNEXPECTED INCONSISTENCY: RUN fsck MANUALLY.
         (i.e., without -a or -p options)
fsck exited with status code 4
The root filesystem on /dev/sda1 requires a manual fsck

BusyBox v1.22.1 (Ubuntu 1:1.22.0-19ubuntuu2) built-in shell (ash)
Enter &#39;help&#39; for a list of built-in commands.

(initramfs)_
</code></pre><p>这是由于文件系统 <code>/dev/sda1</code> 出现了严重错误或不一致性，需要运行 <code>fsck</code>（文件系统检查）来解决。</p>
<p>以下是解决方法的步骤：</p>
<h2 id="1进入grub">1：进入GRUB</h2>
<p>启动虚拟机，并在 <code>GRUB</code> 启动加载器菜单中选择 <code>Advanced options for Ubuntu</code>。
<img loading="lazy" src="/images/grub-ubuntu.png" alt="GRUB"  /></p>
<h2 id="2进入恢复模式">2：进入恢复模式</h2>
<p>然后，选择当前内核版本的恢复模式 <code>Ubuntu, with Linux x.x.x-generic (recovery mode)</code>。
虚拟机将进入恢复模式，并显示一个带有多个选项的菜单。</p>
<h2 id="3选择-root-shell-如果有">3：选择 <code>Root Shell</code> (如果有)</h2>
<p>选择恢复模式菜单中的 <code>Drop to root shell prompt</code>。若无此选项可跳过此步。</p>
<h2 id="4将根文件系统重新挂载为读写">4：将根文件系统重新挂载为读写</h2>
<p>恢复模式下，根文件系统通常以只读模式挂载。为了能够运行 fsck 并修复文件系统，您需要将根文件系统重新挂载为读写模式：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mount -o remount,rw /
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="5手动运行-fsck">5：手动运行 <code>fsck</code></h2>
<p>在根文件系统上运行 fsck 来检查和修复错误：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">fsck -f /dev/sda1
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li><code>-f</code> 选项告诉 <code>fsck</code> 要强制进行检查。</li>
</ul>
<p>运行后会有多个提示需要手动输入，一般全部默认回车即可。</p>
<h2 id="6重新启动">6：重新启动</h2>
<p>在 <code>fsck</code> 完成检查和修复后，输入 <code>reboot</code> 或通过 VMWare 菜单重新启动虚拟机。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>ChatGPT AI 协助 iOS 开发: customized sheet</title>
      <link>https://blog.tomatostore.top/posts/2023/07/swift-shared-customize-sheet/</link>
      <pubDate>Wed, 12 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/07/swift-shared-customize-sheet/</guid>
      <description>程序员走在被 OpenAI 取代的路上</description>
      <content:encoded><![CDATA[<h2 id="需求">需求</h2>
<p>App 中想加入一个订阅页面，期望 iOS 上按 bottom sheet 展示，iPad 上居中显示，同时支持横屏和竖屏，效果如下：</p>
<p><img loading="lazy" src="/images/iphone-subscription.png" alt="subscription in iPhone"  />
<img loading="lazy" src="/images/ipad-subsciption.png" alt="subscription in iPad"  /></p>
<p>iOS 15 提供了 <code>UISheetPresentationController</code> 可以令 Bottom Sheet 更方便，具体可参考<a href="https://sarunw.com/posts/bottom-sheet-in-ios-15-with-uisheetpresentationcontroller/">How to present a Bottom Sheet in iOS 15</a>还有<a href="https://www.avanderlee.com/swift/presenting-sheets-uikit-uisheetpresentationcontroller/">Presenting sheets with UIKit using a UISheetPresentationController</a>。</p>
<p><code>UISheetPresentationController</code> 不满足需求因为它只有 <code>medium</code> 和 <code>large</code> 两种高度，并且宽度是满屏，不符合需求。
通过 <code>OpenAI</code> 问答，找到了可以满足需求的方法来在一个页面中弹出订阅页面。</p>
<p>然后不出意外的，新问题又出现了：想要在另外一个页面中也弹出同样的订阅页面，当时这两个页面的父类是不同 <code>UIViewController</code>，也就是说没办法给他们定义一个共同的定制化的 <code>UIViewController</code>  子类，而 <code>Swift</code> 又不支持多继承，尝试了带默认实现的 <code>Protocol</code>，无法实现。最终还是通过询问 <code>AI</code> 找到了实现方法：</p>
<p><img loading="lazy" src="/images/chatgpt-swift-question.png" alt="dns analysis"  /></p>
<h2 id="最终实现">最终实现：</h2>
<ul>
<li><code>SubscriptionSheetPresenter.swift</code></li>
</ul>
<p>共享的 presenter</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">SubscriptionSheetPresenter</span><span class="p">:</span> <span class="n">NSObject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kd">let</span> <span class="nv">instance</span> <span class="p">=</span> <span class="n">SubscriptionSheetPresenter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">func</span> <span class="nf">presentBottomSheetForSubscription</span><span class="p">(</span><span class="n">from</span> <span class="n">vc</span><span class="p">:</span> <span class="n">UIViewController</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// storyboard 中定义了定制页面的布局</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nv">dashboard</span> <span class="p">=</span> <span class="n">UIStoryboard</span><span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="s">&#34;Main&#34;</span><span class="p">,</span> <span class="n">bundle</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">guard</span> <span class="kd">let</span> <span class="nv">subscriptionVC</span> <span class="p">=</span> <span class="n">dashboard</span><span class="p">.</span><span class="n">instantiateViewController</span><span class="p">(</span><span class="n">withIdentifier</span><span class="p">:</span> <span class="s">&#34;subscriptionViewController&#34;</span><span class="p">)</span> <span class="k">as</span><span class="p">?</span> <span class="n">SubscriptionViewController</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="n">subscriptionVC</span><span class="p">.</span><span class="n">modalPresentationStyle</span> <span class="p">=</span> <span class="p">.</span><span class="n">custom</span>
</span></span><span class="line"><span class="cl">        <span class="n">subscriptionVC</span><span class="p">.</span><span class="n">transitioningDelegate</span> <span class="p">=</span> <span class="kc">self</span>
</span></span><span class="line"><span class="cl">        <span class="n">vc</span><span class="p">.</span><span class="n">present</span><span class="p">(</span><span class="n">subscriptionVC</span><span class="p">,</span> <span class="n">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="n">completion</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// </span><span class="cs">MARK:</span><span class="c1"> - Bottom Sheet for Subscription</span>
</span></span><span class="line"><span class="cl"><span class="kd">extension</span> <span class="nc">SubscriptionSheetPresenter</span><span class="p">:</span> <span class="n">UIViewControllerTransitioningDelegate</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">func</span> <span class="nf">presentationController</span><span class="p">(</span><span class="n">forPresented</span> <span class="n">presented</span><span class="p">:</span> <span class="n">UIViewController</span><span class="p">,</span> <span class="n">presenting</span><span class="p">:</span> <span class="n">UIViewController</span><span class="p">?,</span> <span class="n">source</span><span class="p">:</span> <span class="n">UIViewController</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="n">UIPresentationController</span><span class="p">?</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">SubscriptionSheetPresentationController</span><span class="p">(</span><span class="n">presentedViewController</span><span class="p">:</span> <span class="n">presented</span><span class="p">,</span> <span class="n">presenting</span><span class="p">:</span> <span class="n">presenting</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li><code>SubscriptionViewController.swift</code></li>
</ul>
<p>Storyboard 中订阅页面的 UIViewController class:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">SubscriptionViewController</span><span class="p">:</span> <span class="n">UIViewController</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">@IBOutlet</span> <span class="kr">weak</span> <span class="kd">var</span> <span class="nv">monthlyBuy</span><span class="p">:</span> <span class="n">UIButton</span><span class="p">!</span>
</span></span><span class="line"><span class="cl">    <span class="kr">@IBOutlet</span> <span class="kr">weak</span> <span class="kd">var</span> <span class="nv">yearlyBuy</span><span class="p">:</span> <span class="n">UIButton</span><span class="p">!</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">func</span> <span class="nf">viewWillLayoutSubviews</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="n">viewWillLayoutSubviews</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">updateUI</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">func</span> <span class="nf">viewWillTransition</span><span class="p">(</span><span class="n">to</span> <span class="n">size</span><span class="p">:</span> <span class="n">CGSize</span><span class="p">,</span> <span class="n">with</span> <span class="n">coordinator</span><span class="p">:</span> <span class="n">UIViewControllerTransitionCoordinator</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="n">viewWillTransition</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="n">size</span><span class="p">,</span> <span class="n">with</span><span class="p">:</span> <span class="n">coordinator</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">updateUI</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">updateUI</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// update layour for diff device (iPhone/iPad) and view (Landscape/Protraint)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ...</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li><code>SubscriptionSheetPresentationController.swift</code></li>
</ul>
<p>定制订阅页面的宽度和高度</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">SubscriptionSheetPresentationController</span><span class="p">:</span> <span class="n">UIPresentationController</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">var</span> <span class="nv">frameOfPresentedViewInContainerView</span><span class="p">:</span> <span class="n">CGRect</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nv">screenWidth</span> <span class="p">=</span> <span class="n">UIScreen</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">bounds</span><span class="p">.</span><span class="n">width</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nv">screenHeight</span> <span class="p">=</span> <span class="n">UIScreen</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">bounds</span><span class="p">.</span><span class="n">height</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">         <span class="c1">// default is iPhone+portrait for non-SE</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nv">width</span><span class="p">:</span> <span class="n">CGFloat</span> <span class="p">=</span> <span class="n">screenWidth</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nv">height</span><span class="p">:</span> <span class="n">CGFloat</span> <span class="p">=</span> <span class="n">screenHeight</span> <span class="o">*</span> <span class="mi">2</span><span class="o">/</span><span class="mi">3</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nv">x</span><span class="p">:</span> <span class="n">CGFloat</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nv">yOffset</span> <span class="p">=</span> <span class="mf">1.0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">UIDevice</span><span class="p">.</span><span class="n">current</span><span class="p">.</span><span class="n">userInterfaceIdiom</span> <span class="p">==</span> <span class="p">.</span><span class="n">phone</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">UIDevice</span><span class="p">.</span><span class="n">current</span><span class="p">.</span><span class="n">orientation</span><span class="p">.</span><span class="n">isLandscape</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">width</span> <span class="p">=</span> <span class="n">screenWidth</span> <span class="o">/</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">                <span class="n">height</span> <span class="p">=</span> <span class="n">screenHeight</span> <span class="o">*</span> <span class="mf">0.95</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="n">screenHeight</span> <span class="o">&lt;</span> <span class="mi">700</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">height</span> <span class="p">=</span> <span class="n">screenHeight</span> <span class="o">*</span> <span class="mf">2.5</span><span class="o">/</span><span class="mi">3</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// iPad or Mac</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">UIDevice</span><span class="p">.</span><span class="n">current</span><span class="p">.</span><span class="n">orientation</span><span class="p">.</span><span class="n">isLandscape</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">width</span> <span class="p">=</span> <span class="n">screenWidth</span> <span class="o">/</span> <span class="mf">2.5</span>
</span></span><span class="line"><span class="cl">                <span class="n">height</span> <span class="p">=</span> <span class="n">screenHeight</span> <span class="o">*</span> <span class="mf">0.65</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">width</span> <span class="p">=</span> <span class="n">screenWidth</span> <span class="o">/</span> <span class="mf">1.8</span>
</span></span><span class="line"><span class="cl">                <span class="n">height</span> <span class="p">=</span> <span class="n">screenHeight</span> <span class="o">*</span> <span class="mf">0.5</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="n">yOffset</span> <span class="p">=</span> <span class="mf">0.5</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">x</span> <span class="p">=</span> <span class="p">(</span><span class="n">screenWidth</span> <span class="o">-</span> <span class="n">width</span><span class="p">)</span><span class="o">/</span><span class="mi">2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">CGRect</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">containerView</span><span class="p">.</span><span class="n">bounds</span><span class="p">.</span><span class="n">height</span> <span class="o">-</span> <span class="n">height</span><span class="p">)</span> <span class="o">*</span> <span class="n">yOffset</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">width</span><span class="p">:</span> <span class="n">width</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">height</span><span class="p">:</span> <span class="n">height</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">override</span> <span class="kd">func</span> <span class="nf">containerViewWillLayoutSubviews</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kc">super</span><span class="p">.</span><span class="n">containerViewWillLayoutSubviews</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="kd">let</span> <span class="nv">presentedView</span> <span class="p">=</span> <span class="n">presentedView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">presentedView</span><span class="p">.</span><span class="n">frame</span> <span class="p">=</span> <span class="n">frameOfPresentedViewInContainerView</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在需要弹出订阅页面的 <code>UIViewController</code> 中直接调用即可：</p>
<ul>
<li><code>&lt;Any&gt;ViewController.swift</code></li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="p">&lt;</span><span class="nb">Any</span><span class="p">&gt;</span><span class="n">ViewController</span><span class="p">:</span> <span class="p">&lt;</span><span class="n">XXX</span><span class="p">&gt;</span><span class="n">ViewController</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">func</span> <span class="nf">showSubscription</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">SubscriptionSheetPresenter</span><span class="p">.</span><span class="n">instance</span><span class="p">.</span><span class="n">presentBottomSheetForSubscription</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="kc">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li><code>&lt;Another&gt;ViewController.swift</code></li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">class</span> <span class="p">&lt;</span><span class="n">Anyother</span><span class="p">&gt;</span><span class="n">ViewController</span><span class="p">:</span> <span class="p">&lt;</span><span class="n">YYY</span><span class="p">&gt;</span><span class="n">ViewController</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">func</span> <span class="nf">goPremium</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">SubscriptionSheetPresenter</span><span class="p">.</span><span class="n">instance</span><span class="p">.</span><span class="n">presentBottomSheetForSubscription</span><span class="p">(</span><span class="n">from</span><span class="p">:</span> <span class="kc">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="usefull-links">Usefull links</h2>
<ul>
<li><a href="https://blog.tomatostore.top/posts/2023/04/swift-block-based-animation/">Swift: beginAnimations(_:context:) was deprecated in iOS 13.0: Use the block-based animation API instead
</a></li>
<li><a href="https://blog.tomatostore.top/posts/2023/04/swift-substring/">Swift String Substring
</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>github: REMOTE HOST IDENTIFICATION HAS CHANGED</title>
      <link>https://blog.tomatostore.top/posts/2023/07/github-remote-host-identif-change/</link>
      <pubDate>Sun, 09 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/07/github-remote-host-identif-change/</guid>
      <description>修改hosts访问github</description>
      <content:encoded><![CDATA[<p><code>git pull</code> 出现了如下开头的错误提示：</p>
<pre tabindex="0"><code>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
</code></pre><h2 id="解决方法-update-know_hosts">解决方法: update know_hosts</h2>
<ol>
<li>执行如下命令删除 <code>~/.ssh/know_hosts</code> 中的 <code>github.com</code> 记录：</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-keygen -R github.com
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>可以使用 <code>ssh-keygen -l -f ~/.ssh/known_hosts</code> 命令查看 <code>~/.ssh/know_hosts</code></li>
</ul>
<ol start="2">
<li>再次执行 <code>git pull</code> 命令，会有如下提示，输入 <code>yes</code> 后继续即可。</li>
</ol>
<pre tabindex="0"><code>The authenticity of host &#39;github.com (ipaddress)&#39; can&#39;t be established.
ED25519 key fingerprint is SHA256:+XDFLKJWEOIRXXX.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?
</code></pre><ol start="3">
<li>如果还是无法访问，或者出现 <a href="mailto:git@github.com">git@github.com</a> 输入密码的提示，先参考 <a href="https://blog.tomatostore.top/posts/2023/07/github-ping-return-127-or-update-hosts/">这篇文章更新hosts</a>后，再更新 <code>~/.ssh/know_hosts</code>。</li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>Ping github.com returns 127.0.0.1</title>
      <link>https://blog.tomatostore.top/posts/2023/07/github-ping-return-127-or-update-hosts/</link>
      <pubDate>Sun, 09 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/07/github-ping-return-127-or-update-hosts/</guid>
      <description>修改hosts访问github</description>
      <content:encoded><![CDATA[<p>浏览器中无法访问 <code>github.com</code>，出现 <strong>无法访问此网站</strong> 的提示。</p>
<p>命令行中执行 <code>ping github.com</code> 出现如下结果：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ ping github.com
</span></span><span class="line"><span class="cl">PIING github.com<span class="o">(</span>127.0.0.1<span class="o">)</span>: <span class="m">56</span> data bytes
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="解决方法-update-hostst">解决方法: update hostst</h2>
<p>通过IP 查询网站（如：<code>www.ipaddress.com</code>）查到 <code>github.com</code> 的 IP (如：140.82.113.3)，在系统 <code>hosts</code> 文件中添加如下一行：</p>
<pre tabindex="0"><code>140.82.113.3 github.com 
</code></pre><p>如果在 <code>git pull</code> 时出现 <code>REMOTE HOST IDENTIFICATION HAS CHANGED</code> 的提示，可参考 <a href="https://blog.tomatostore.top/posts/2023/07/github-remote-host-identif-change">github: REMOTE HOST IDENTIFICATION HAS CHANGED</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>C&#43;&#43; template&lt;class&gt; v.s. template&lt;typename&gt;</title>
      <link>https://blog.tomatostore.top/posts/2023/06/cpp-template-parameter-class-vs-typename/</link>
      <pubDate>Wed, 21 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/06/cpp-template-parameter-class-vs-typename/</guid>
      <description>C&#43;&#43;模板用typename和class有啥区别</description>
      <content:encoded><![CDATA[<p><strong>Q:</strong> <code>C++</code> 模板用 <code>typename</code> 和 <code>class</code> 有啥区别?
<strong>A:</strong> 无论使用 <code>class</code> 还是 <code>typename</code> 声明类型参数，效果都是相同的，选择使用哪个关键字主要取决于个人或团队的偏好。一般来说，较为常见的做法是在声明类模板时使用 class，而在嵌套类型的声明或模板内部使用时则使用 typename。</p>
<hr>
<p>早期的 <code>C++</code> 标准将 <code>class</code> 用作声明类型参数的关键字。为了支持<em>模板内嵌套类型</em>，<code>C++98</code> 标准引入了 <code>typename</code>， 同时为了兼容性，<code>class</code> 作为模板类型声明的关键字继续保留。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// template&lt;class&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">template</span><span class="o">&lt;</span><span class="k">class</span> <span class="nc">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyClass1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// template&lt;typename&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyContainer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// internal usage
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">using</span> <span class="n">value_type</span> <span class="o">=</span> <span class="n">T</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// nested type
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">typename</span> <span class="n">MyContainer</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">value_type</span> <span class="n">getValue</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    
    <item>
      <title>Godot 2D Sprite Animation</title>
      <link>https://blog.tomatostore.top/posts/2023/06/godot-2d-sprite-animation/</link>
      <pubDate>Wed, 21 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/06/godot-2d-sprite-animation/</guid>
      <description>如何在 Godot 中创建 2D Sprite 动画</description>
      <content:encoded><![CDATA[<p>官方文档 <a href="ttps://docs.godotengine.org/en/stable/tutorials/2d/2d_sprite_animation.html">2D Sprite Animation</a> 介绍了 3 种实现 2D Sprite 动画的方法。</p>
<p>第一种方法适用于<strong>每一帧的动画是单独图片</strong>的情况。后两种方式适用于<strong>多个动画帧在一张图片内</strong>的情况。</p>
<h2 id="animatedsprite2d单独的图像帧"><code>AnimatedSprite2D</code>+单独的图像帧</h2>
<p>这种方式适用于将动画拆分为单独的图像帧（frame）的情况。
需要将每个动画帧作为单独的图像导入到 Godot 中，并使用 <code>AnimatedSprite2D</code> 节点来创建动画。通过在 <code>SpriteFrames</code> 面板中设置帧速率和添加动画帧，可以控制动画的播放。</p>
<ol>
<li>点击 <code>AnimatedSprite2D</code> 的 <code>SpriteFrames</code> 属性，选择 <code>New SpriteFrames</code>.

  <img loading="lazy" src="https://docs.godotengine.org/en/stable/_images/2d_animation_new_spriteframes.webp" alt="AnimatedSprite2D - SpriteFrames - New SpriteFrames"  /></li>
<li>拖拽单个帧的图片到 <code>SpriteFrames</code>

  <img loading="lazy" src="https://docs.godotengine.org/en/stable/_images/2d_animation_spriteframes_done.webp" alt="add-individual-frames-to-spriteFrames"  /></li>
</ol>
<h2 id="animatedsprite2dspritesheet"><code>AnimatedSprite2D</code>+<code>SpriteSheet</code></h2>
<p>这种方式适用于使用精灵表单（sprite sheet）的情况，精灵表单是指将所有动画帧合并到一个图像中。</p>
<ol>
<li>点击 <code>AnimatedSprite2D</code> 的 <code>SpriteFrames</code> 属性，选择 <code>New SpriteFrames</code>.</li>
<li>在 <code>SpriteFrames</code> 面板中选择 <code>Add frames from a Sprite Sheet</code>

  <img loading="lazy" src="https://docs.godotengine.org/en/stable/_images/2d_animation_add_from_spritesheet.webp" alt="add-frames-from-a-sprite-sheet"  /></li>
<li>选择想要导入的精灵表单，在弹出的窗口中输入行与列的数量将图片分成多个帧（frame），用鼠标选择相应的帧导入。

  <img loading="lazy" src="https://docs.godotengine.org/en/stable/_images/2d_animation_spritesheet_select_rows.webp" alt="select-frames"  /></li>
</ol>
<p>到此为止的前两种用 <code>AnimatedSprete2D</code> 实现的动画都可以在代码中使用 <code>play()</code> 和 <code>stop()</code> 来控制。以 <code>GDScropt</code> 为例：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">extends</span> <span class="nx">CharacterBody2D</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="nx">onready</span> <span class="kd">var</span> <span class="nx">_animated_sprite</span> <span class="o">=</span> <span class="nx">$AnimatedSprite2D</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">func</span> <span class="nx">_process</span><span class="p">(</span><span class="nx">_delta</span><span class="p">)</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">Input</span><span class="p">.</span><span class="nx">is_action_pressed</span><span class="p">(</span><span class="s2">&#34;ui_right&#34;</span><span class="p">)</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">_animated_sprite</span><span class="p">.</span><span class="nx">play</span><span class="p">(</span><span class="s2">&#34;run&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">_animated_sprite</span><span class="p">.</span><span class="nx">stop</span><span class="p">()</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="sprite2danimationplayerspritesheet"><code>Sprite2D</code>+<code>AnimationPlayer</code>+<code>SpriteSheet</code></h2>
<ol>
<li>选中 <code>Sprite2D</code> 节点，拖拽精灵表单到 <code>Texture</code> ，设置相应的 <code>Hframes</code> 和 <code>Vframes</code> 来切分成单个帧。

  <img loading="lazy" src="https://docs.godotengine.org/en/stable/_images/2d_animation_setframes.webp" alt="sprite2d-texture"  /></li>
<li>选中 <code>AnimationPlayer</code> 节点，点击 <code>Animation</code> 按钮，接着点 <code>New</code>，输入名字，设置动画时长和循环。

  <img loading="lazy" src="https://docs.godotengine.org/en/stable/_images/2d_animation_new_animation.webp" alt="animation-setting"  /></li>
<li>在 <code>Animation</code> 的时间轴上选择插入点，在 <code>Sprite2D</code> 的 <code>Frame</code> 属性设置中选择对应的帧，点 <code>key</code> （钥匙）图标，将相应的帧插入时间轴。对其余帧重复操作。

  <img loading="lazy" src="https://docs.godotengine.org/en/stable/_images/2d_animation_new_track.webp" alt="click-key-to-add-frame"  />

  <img loading="lazy" src="https://docs.godotengine.org/en/stable/_images/2d_animation_full_animation.webp" alt="multi-frames-in-animation"  /></li>
</ol>
<p>对 <code>AnimationPlayer</code> 也可用通过代码控制动画。比如：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">extends</span> <span class="nx">CharacterBody2D</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="nx">onready</span> <span class="kd">var</span> <span class="nx">_animation_player</span> <span class="o">=</span> <span class="nx">$AnimationPlayer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">func</span> <span class="nx">_process</span><span class="p">(</span><span class="nx">_delta</span><span class="p">)</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">Input</span><span class="p">.</span><span class="nx">is_action_pressed</span><span class="p">(</span><span class="s2">&#34;ui_right&#34;</span><span class="p">)</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">_animation_player</span><span class="p">.</span><span class="nx">play</span><span class="p">(</span><span class="s2">&#34;walk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="nx">_animation_player</span><span class="p">.</span><span class="nx">stop</span><span class="p">()</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>本文只是概述，详情参看<a href="https://docs.godotengine.org/en/stable/tutorials/2d/2d_sprite_animation.html">原文</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何选择正确的日志级别（log level）</title>
      <link>https://blog.tomatostore.top/posts/2023/06/loglevel-introduction/</link>
      <pubDate>Tue, 20 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/06/loglevel-introduction/</guid>
      <description>Log Level Introduction</description>
      <content:encoded><![CDATA[<h2 id="日志级别的历史">日志级别的历史：</h2>
<p><code>日志级别（log level）</code>起源于 20 世纪 80 年代的由 <code>Eric Allman</code> 开发的项目 <code>Sendmail</code> 。该项目需要一种日志记录解决方案，这最终导致了 <code>System Logging Protocol（Syslog）</code> 的创建以及不同日志级别的概念。后来 <code>Syslog</code> 被广泛应用于各种应用程序，并成为系统日志和事件消息的标准协议。</p>
<h2 id="日志级别">日志级别</h2>
<p>按重要性递减排列：</p>
<ul>
<li>OFF：不记录任何日志信息。</li>
<li>FATAL：表示应用程序即将发生严重问题或数据损坏，意味着应用程序处于灾难性状态，重要功能无法正常工作。</li>
<li>ERROR：表示无法访问服务或文件等重要故障，应用程序可能继续运行，但需要及时处理。</li>
<li>WARN：表示检测到意外问题，可能会影响特定进程的运行，但不会对应用程序造成损害。</li>
<li>INFO：记录应用程序的正常行为，如服务启动或停止、数据库添加等，通常不需要跟进处理。</li>
<li>DEBUG：提供详细的诊断信息，用于故障诊断、排查或测试应用程序。</li>
<li>TRACE：捕获应用程序行为的详细信息，比DEBUG级别更细粒度，用于查看代码中的参数或解释算法的步骤。</li>
<li>ALL：记录所有定义的日志级别，包括自定义日志级别，相当于综合所有级别的日志。</li>
</ul>
<h2 id="常用日志库">常用日志库</h2>
<table>
  <thead>
      <tr>
          <th>编程语言</th>
          <th>日志库</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>C++</td>
          <td><a href="https://github.com/gabime/spdlog">spdlog</a>, <a href="https://www.boost.org/doc/libs/1_75_0/libs/log/doc/html/index.html">Boost.Log</a></td>
      </tr>
      <tr>
          <td>Java</td>
          <td><a href="https://logging.apache.org/log4j/2.x/">Log4j</a>, <a href="http://www.slf4j.org/">Slf4j</a></td>
      </tr>
      <tr>
          <td>Python</td>
          <td><a href="https://docs.python.org/3/library/logging.html">Python logging</a>, <a href="https://github.com/Delgan/loguru">Loguru</a></td>
      </tr>
      <tr>
          <td>JavaScript</td>
          <td><a href="https://github.com/winstonjs/winston">Winston</a>, <a href="https://github.com/trentm/node-bunyan">Bunyan</a></td>
      </tr>
      <tr>
          <td>C#</td>
          <td><a href="https://nlog-project.org/">NLog</a>, <a href="https://serilog.net/">Serilog</a></td>
      </tr>
      <tr>
          <td>Go</td>
          <td><a href="https://github.com/sirupsen/logrus">logrus</a>, <a href="https://github.com/uber-go/zap">zap</a></td>
      </tr>
      <tr>
          <td>Ruby</td>
          <td><a href="https://github.com/colbygk/log4r">Log4r</a>, <a href="https://ruby-doc.org/stdlib-3.0.2/libdoc/logger/rdoc/Logger.html">Logger</a></td>
      </tr>
      <tr>
          <td>PHP</td>
          <td><a href="https://github.com/Seldaek/monolog">Monolog</a>, <a href="https://laravel.com/docs/8.x/logging">Laravel Log</a></td>
      </tr>
  </tbody>
</table>
]]></content:encoded>
    </item>
    
    <item>
      <title>VSCode: Markdown 预览支持 PlantUML</title>
      <link>https://blog.tomatostore.top/posts/2023/06/vscode-preview-markdown-with-plantuml/</link>
      <pubDate>Fri, 16 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/06/vscode-preview-markdown-with-plantuml/</guid>
      <description>Dot Executable: /opt/local/bin/dot</description>
      <content:encoded><![CDATA[<p>想在 VSCode 中预览 PlantUML 格式的文件，可以通过安装 <a href="https://marketplace.visualstudio.com/items?itemName=jebbs.plantuml">PlantUML Extension</a> 来实现。</p>
<p>如果想在 Markdown 文件中嵌入一段 PlantUML 脚本并在 VSCode 中预览，可以安装 <a href="https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced">Markdown Preview Enahnced</a>.</p>
<p><img loading="lazy" src="/images/vscode-preview-plantuml-in-markdown.png" alt="Preview PlantUML in Markdown"  /></p>
<p>在 Linux 的 VSCode 中，如果在 Preview 的时候 PlantUML 图出不来并有如下错误：</p>
<pre tabindex="0"><code>Dot Executable: /opt/local/bin/dot
File does not exist
Cannot find Graphviz. You should try
 
@startuml
testdot
@enduml
 
or
 
java -jar plantuml.jar -testdot
</code></pre><p>可以执行 <code>sudo apt-get install graphviz</code> 安装 <code>graphviz</code> 来解决。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何用VLC Media Player截取部分视频</title>
      <link>https://blog.tomatostore.top/posts/2023/06/how-to-cut-videos-using-vlc/</link>
      <pubDate>Tue, 13 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/06/how-to-cut-videos-using-vlc/</guid>
      <description>比如删除视频后半部分、只保留中间想要的部分</description>
      <content:encoded><![CDATA[<p>VLC Media Player是一款流行的免费媒体播放器，除了播放功能之外，它还可以用于简单的视频编辑。
在本文中，我们将介绍如何使用VLC Media Player截取视频。</p>
<h2 id="步骤">步骤</h2>
<ol>
<li>用 VLC 打开你要编辑的视频，播放到想要截取的开始位置。</li>
<li>并点击菜单栏中的“视图”选项。然后选择“高级控制”以显示高级控制面板。
<img loading="lazy" src="/images/vlc-views-menu.png" alt="VLC View menu"  /></li>
<li>在底部的控制栏上，你会看到一个红色圆圈的“记录”按钮。</li>
<li>点击红色按钮以开始录制。然后点击播放按钮开始播放你的视频。
<img loading="lazy" src="/images/vlc-record-and-play.png" alt="VLC Record and Play"  /></li>
<li>当视频播放到你想要删除的位置时，点击红色录制按钮停止录制，也可以先停止播放，再停止录制。</li>
<li>此时 VLC 已经将录制的视频自动保存，但不会有提示。可以从如下路径找到录制好的视频：</li>
</ol>
<ul>
<li>Windows: <code>C:\Users\&lt;Username&gt;\Videos</code></li>
<li>macOS: <code>/Users/&lt;Username&gt;/Movies</code></li>
<li>Linux: <code>/home/&lt;Username&gt;/Videos</code></li>
</ul>
<p>这种方法实际上是通过录制一个新的视频文件来达到截取视频(cut video)的目的，而不是直接编辑原始视频文件。</p>
<p>如果你需要进行更复杂的视频编辑操作，如剪切、裁剪、合并等，你可能需要考虑使用专业的视频编辑软件，如Adobe Premiere Pro、Final Cut Pro等。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>阿里云域名解析 Github.io</title>
      <link>https://blog.tomatostore.top/posts/2023/06/github-pages-with-customized-domain-alicloud/</link>
      <pubDate>Sun, 11 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/06/github-pages-with-customized-domain-alicloud/</guid>
      <description>Github pages 自定义域名</description>
      <content:encoded><![CDATA[<p>Github Page 提供了免费的域名来搭建静态网站，但国内访问速度不是很理想，经过搜索后学习到可以通过自定义域名+国内域名解析来实现国内提速。</p>
<p>本网站是 <code>Github page + Hugo</code>搭建的，默认地址 <code>aidysun.github.io</code>。</p>
<ol>
<li>域名注册</li>
</ol>
<p>在<a href="https://wanwang.aliyun.com/domain/?spm=5176.100251.111252.17.13bb4f15ZkUpEQ">阿里云</a>中注册自己的域名，如果对名字和后缀没有特殊要求的话，价格还是很美丽的。
我选择的 <code>tomatostore.top</code> 3年只需要一百多块钱。</p>
<ol start="2">
<li>域名解析</li>
</ol>
<p>登陆【阿里云】-【域名控制台】-【域名解析】，填写内容参考下图：</p>
<p><img loading="lazy" src="/images/dns-analysis-ali-cloud.png" alt="dns analysis"  /></p>
<ol start="3">
<li>github设置</li>
</ol>
<p>在 github.io 的 repo 设置，选择 pages, 在 Custom Domain 中输入自定义的域名，保存即可。</p>
<p><img loading="lazy" src="/images/github-io-customize-domain-setting.png" alt="github io customize domain setting"  /></p>
<p>**注意⚠**️：这一步保存之后会在 github.io 的根目录下自动添加一个 <code>CNAME</code> 的文件。<code>CNAME</code>的内容就是在 setting 中设置的 Custom domain 的内容。</p>
<p>如果你的网站并不是直接在 github.io 下管理的，比如是通过其他 repo 的action更新到 github.io 的 repo 的。那么需要确保在后面自动更新github.io后也要有这个CNAME文件。</p>
<p>比如我用的是 hugo 在另一个repo中，那我需要在 hugo 的 static 文件夹下新建一个 CNAME 文件，内容为我的custom域名：blog.tomatostore.top。这样在hugo build之后push到github.io的根目录下就会有 CNAME。</p>
<ul>
<li>我的网站：<a href="https://blog.tomatostore.top">https://blog.tomatostore.top</a></li>
<li>github: <a href="https://github.com/AidySun/aidysun.github.io">https://github.com/AidySun/aidysun.github.io</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Use customized class in std::map, std::set</title>
      <link>https://blog.tomatostore.top/posts/2023/05/custom-class-with-std-associative-container/</link>
      <pubDate>Fri, 19 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/05/custom-class-with-std-associative-container/</guid>
      <description>Make self-defined class comparable</description>
      <content:encoded><![CDATA[<p>有一个自定义的 <code>class A</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">A</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">  <span class="o">~</span><span class="n">A</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="nf">getVal</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">val</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="kt">void</span> <span class="nf">setVal</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="n">val</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>当尝试把 class A 的对象存到 std::set 或作为key用在 std:map 中时，会有编译错误。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;map&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;set&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">A</span> <span class="n">a1</span><span class="p">,</span> <span class="n">a2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">a1</span><span class="p">.</span><span class="n">setVal</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">a2</span><span class="p">.</span><span class="n">setVal</span><span class="p">(</span><span class="o">-</span><span class="mi">200</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">map</span><span class="o">&lt;</span><span class="n">A</span><span class="p">,</span> <span class="n">string</span><span class="o">&gt;</span> <span class="n">mapOfA</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">mapOfA</span><span class="p">[</span><span class="n">a1</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;hello&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">mapOfA</span><span class="p">[</span><span class="n">a2</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;world&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">set</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span> <span class="n">setOfA</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">setOfA</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">a1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">setOfA</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">a2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>compile error:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/usr/include/c++/9/bits/stl_function.h: In instantiation of ‘constexpr bool std::less&lt;_Tp&gt;::operator<span class="o">()(</span>const _Tp<span class="p">&amp;</span>, const _Tp<span class="p">&amp;</span><span class="o">)</span> const <span class="o">[</span>with <span class="nv">_Tp</span> <span class="o">=</span> A<span class="o">]</span>’:
</span></span><span class="line"><span class="cl">/usr/include/c++/9/bits/stl_map.h:497:32:   required from ‘std::map&lt;_Key, _Tp, _Compare, _Alloc&gt;::mapped_type<span class="p">&amp;</span> std::map&lt;_Key, _Tp, _Compare, _Alloc&gt;::operator<span class="o">[](</span>const key_type<span class="p">&amp;</span><span class="o">)</span> <span class="o">[</span>with <span class="nv">_Key</span> <span class="o">=</span> A<span class="p">;</span> <span class="nv">_Tp</span> <span class="o">=</span> std::__cxx11::basic_string&lt;char&gt;<span class="p">;</span> <span class="nv">_Compare</span> <span class="o">=</span> std::less&lt;A&gt;<span class="p">;</span> <span class="nv">_Alloc</span> <span class="o">=</span> std::allocator&lt;std::pair&lt;const A, std::__cxx11::basic_string&lt;char&gt; &gt; &gt;<span class="p">;</span> std::map&lt;_Key, _Tp, _Compare, _Alloc&gt;::mapped_type <span class="o">=</span> std::__cxx11::basic_string&lt;char&gt;<span class="p">;</span> std::map&lt;_Key, _Tp, _Compare, _Alloc&gt;::key_type <span class="o">=</span> A<span class="o">]</span>’
</span></span><span class="line"><span class="cl">/home/rduis/Workspace/github/notesnotes/swift/src/custom-class-in-std-map.cpp:25:12:   required from here
</span></span><span class="line"><span class="cl">/usr/include/c++/9/bits/stl_function.h:386:20: error: no match <span class="k">for</span> ‘operator&lt;’ <span class="o">(</span>operand types are ‘const A’ and ‘const A’<span class="o">)</span>
</span></span><span class="line"><span class="cl">  <span class="m">386</span> <span class="p">|</span>       <span class="o">{</span> <span class="k">return</span> __x &lt; __y<span class="p">;</span> <span class="o">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">|</span>                ~~~~^~~~~
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="solution-1">solution 1:</h2>
<p>declare <code>operator &lt; </code> in class A:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">A</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">  <span class="o">~</span><span class="n">A</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="nf">getVal</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">val</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="kt">void</span> <span class="nf">setVal</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="n">val</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// solution 1: operator &lt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kt">bool</span> <span class="k">operator</span><span class="o">&lt;</span><span class="p">(</span><span class="k">const</span> <span class="n">A</span> <span class="o">&amp;</span><span class="n">rhs</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">val</span> <span class="o">&lt;</span> <span class="n">rhs</span><span class="p">.</span><span class="n">getVal</span><span class="p">();</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="solution-2">solution 2:</h2>
<ol>
<li>create a new class as comparator</li>
<li>modify map declaration:</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">A</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">  <span class="o">~</span><span class="n">A</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="nf">getVal</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">val</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="kt">void</span> <span class="nf">setVal</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="n">val</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// solution 2: delcare comparator
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">class</span> <span class="nc">comparator</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="k">operator</span><span class="p">()(</span><span class="k">const</span> <span class="n">A</span><span class="o">&amp;</span> <span class="n">lhs</span><span class="p">,</span> <span class="k">const</span> <span class="n">A</span><span class="o">&amp;</span> <span class="n">rhs</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">lhs</span><span class="p">.</span><span class="n">getVal</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">rhs</span><span class="p">.</span><span class="n">getVal</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="why">Why</h2>
<ul>
<li>
<p><code>std::map</code> and <code>std::set</code> are <strong>sorted</strong> associative containers.</p>
</li>
<li>
<p>Sorting is done using the <strong>comparison function Compare</strong>.</p>
</li>
<li>
<p>Declaration of <code>std::map</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">Key</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">T</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">Compare</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">less</span><span class="o">&lt;</span><span class="n">Key</span><span class="o">&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">Allocator</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">allocator</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">Key</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="k">class</span> <span class="nc">map</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>Declaration of <code>std::map</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Key</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Compare</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">less</span><span class="o">&lt;</span><span class="n">Key</span><span class="o">&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Allocator</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">allocator</span><span class="o">&lt;</span><span class="n">Key</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="k">class</span> <span class="nc">set</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
</ul>
<h2 id="ref">Ref</h2>
<ul>
<li><a href="https://en.cppreference.com/w/cpp/container/map">https://en.cppreference.com/w/cpp/container/map</a></li>
<li><a href="https://stackoverflow.com/questions/1102392/how-can-i-use-stdmaps-with-user-defined-types-as-key">https://stackoverflow.com/questions/1102392/how-can-i-use-stdmaps-with-user-defined-types-as-key</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Xcode自动删除空行的白字符</title>
      <link>https://blog.tomatostore.top/posts/2023/05/xcode-remove-whitespaces-of-empty-lines/</link>
      <pubDate>Fri, 19 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/05/xcode-remove-whitespaces-of-empty-lines/</guid>
      <description>Auto remove spaces of empty lines</description>
      <content:encoded><![CDATA[<p>在 Xcode 编辑代码后用 git 提交时，如果空行含有白字符（空格或tab），会有如下提示：</p>
<p><img loading="lazy" src="/images/whitespace-in-empty-line.png" alt="space-in-empty-line"  /></p>
<p><strong>那么如何能自动删除空白行开头的空格呢？</strong></p>
<ol>
<li>编辑时自动删除</li>
</ol>
<p>在 <code>Xcode</code> 中, 依次打开 <code>[Preferences... -&gt; Text Editing -&gt; Editing -&gt; While Editing]</code>，勾选两个选项：</p>
<p><img loading="lazy" src="/images/xcode-while-editing.png" alt="xcode-while-editing"  /></p>
<p>然而以上设置只对在编辑过程中出现的空白行中的空格起作用，对于已经存在的文件不会起作用。</p>
<ol start="2">
<li>命令行删除</li>
</ol>
<p>那对已经存在的文件，可以通过在 Terminal 中执行命令来修改，比如修改所有 Swift/Objective-C 的代码文件：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">cd</span> &lt;folder of your project&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">find . -type f <span class="se">\(</span> -iname <span class="s2">&#34;*.swift&#34;</span> -o -iname <span class="s2">&#34;*.h&#34;</span> -o -iname <span class="s2">&#34;*.m&#34;</span> <span class="se">\)</span> -exec sed -i -e <span class="s2">&#34;s/^\( \{1,99\}\)*</span>$<span class="s2">//g&#34;</span> <span class="o">{}</span> <span class="se">\;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="3">
<li>code format 工具</li>
</ol>
<p>可以在每次项目编译时统一处理代码格式。比如通过 <code>swiftlint</code>:</p>
<p><img loading="lazy" src="/images/whitespace-swiftlint-setting-in-xcode.png" alt="xcode-with-switlint"  /></p>
<p>关于具体配置参考<code>swiftlint</code>的文档。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>[C&#43;&#43;11] Scoped Enumeration (enum class)</title>
      <link>https://blog.tomatostore.top/posts/2023/05/cpp-scoped-enum-class/</link>
      <pubDate>Thu, 18 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/05/cpp-scoped-enum-class/</guid>
      <description>Strong typed enums in C&#43;&#43;11</description>
      <content:encoded><![CDATA[<h2 id="traditional-enum-in-c">Traditional enum in C++</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Week</span>        <span class="p">{</span><span class="n">Mon</span>   <span class="p">,</span><span class="n">Tue</span>   <span class="p">,</span><span class="n">Wed</span>  <span class="p">,</span><span class="n">Thu</span>  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Season</span>      <span class="p">{</span><span class="n">Sprint</span><span class="p">,</span><span class="n">Summer</span><span class="p">,</span><span class="n">Fall</span> <span class="p">,</span><span class="n">Winter</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Week</span> <span class="n">w</span> <span class="o">=</span> <span class="n">Tue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">Season</span> <span class="n">s</span> <span class="o">=</span> <span class="n">Summer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">isEqual</span> <span class="o">=</span> <span class="p">(</span><span class="n">w</span> <span class="o">==</span> <span class="n">s</span><span class="p">);</span> <span class="c1">// isEqual is true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="problem">Problem</h3>
<p>非类型安全，比如对<code>Week w</code>和<code>Season s</code>两个逻辑意义上不同类型的变量作比较，会产生不合逻辑的结果：<code>w == s</code> 是 <code>true</code>。</p>
<p>It&rsquo;s not safe, the result of <code>w == s</code> is <code>true</code> is not reasonalbe.</p>
<p>不同类型的 enum 比较会有编译警告<code>warning</code>，但只是警告：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">warning: comparison between ‘enum Week’ and ‘enum Season’ <span class="o">[</span>-Wenum-compare<span class="o">]</span>
</span></span><span class="line"><span class="cl">      <span class="p">|</span>   bool <span class="nv">isEqual</span> <span class="o">=</span> <span class="o">(</span><span class="nv">w</span> <span class="o">==</span> s<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">|</span>                        ^
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="scoped-enum">Scoped enum</h2>
<ul>
<li><code>enum class</code> and <code>enum struct</code> are the same</li>
<li><code>enum class</code> is not a class</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="n">EnumOnly</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Week</span> <span class="p">{</span> <span class="n">Mon</span><span class="p">,</span> <span class="n">Tue</span><span class="p">,</span> <span class="n">Wed</span><span class="p">,</span> <span class="n">Thu</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Season</span> <span class="p">{</span> <span class="n">Sprint</span><span class="p">,</span> <span class="n">Summer</span><span class="p">,</span> <span class="n">Fall</span><span class="p">,</span> <span class="n">Winter</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="c1">// namespace EnumOnly
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">namespace</span> <span class="n">EnumClass</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">Month</span> <span class="p">{</span> <span class="n">Jan</span><span class="p">,</span> <span class="n">Feb</span><span class="p">,</span> <span class="n">Mar</span><span class="p">,</span> <span class="n">Apr</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">Num</span> <span class="p">{</span> <span class="n">One</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">Two</span><span class="p">,</span> <span class="n">Three</span><span class="p">,</span> <span class="n">Four</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="c1">// namespace EnumClass
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;EnumOnly::Week::Mon = &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">EnumOnly</span><span class="o">::</span><span class="n">Week</span><span class="o">::</span><span class="n">Mon</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;EnumClass::Month::Jan = &#34;</span> <span class="o">&lt;&lt;</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">EnumClass</span><span class="o">::</span><span class="n">Month</span><span class="o">::</span><span class="n">Jan</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Is EnumOnly::Week a class : &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">is_class</span><span class="o">&lt;</span><span class="n">EnumOnly</span><span class="o">::</span><span class="n">Week</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Is EnumOnly::Week a enum : &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">is_enum</span><span class="o">&lt;</span><span class="n">EnumOnly</span><span class="o">::</span><span class="n">Week</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Is EnumClass::Month a class : &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">is_class</span><span class="o">&lt;</span><span class="n">EnumClass</span><span class="o">::</span><span class="n">Month</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Is EnumClass::Month a enum : &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">is_enum</span><span class="o">&lt;</span><span class="n">EnumClass</span><span class="o">::</span><span class="n">Month</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">EnumOnly</span><span class="o">::</span><span class="n">Week</span> <span class="n">w</span> <span class="o">=</span> <span class="n">EnumOnly</span><span class="o">::</span><span class="n">Tue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">EnumOnly</span><span class="o">::</span><span class="n">Season</span> <span class="n">s</span> <span class="o">=</span> <span class="n">EnumOnly</span><span class="o">::</span><span class="n">Summer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">EnumClass</span><span class="o">::</span><span class="n">Month</span> <span class="n">m</span> <span class="o">=</span> <span class="n">EnumClass</span><span class="o">::</span><span class="n">Month</span><span class="o">::</span><span class="n">Feb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">EnumClass</span><span class="o">::</span><span class="n">Num</span> <span class="n">n</span> <span class="o">=</span> <span class="n">EnumClass</span><span class="o">::</span><span class="n">Num</span><span class="o">::</span><span class="n">Two</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">EnumClass</span><span class="o">::</span><span class="n">Num</span> <span class="n">n2</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">EnumClass</span><span class="o">::</span><span class="n">Num</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">EnumClass</span><span class="o">::</span><span class="n">Num</span> <span class="n">n3</span> <span class="o">=</span> <span class="n">EnumClass</span><span class="o">::</span><span class="n">Num</span><span class="o">::</span><span class="n">Three</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">w</span> <span class="o">==</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;w == s, ohh!&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>  <span class="c1">// unmeaningful
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;w != s&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">==</span> <span class="n">n2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;n == n2, yes&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;n != n2, why&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">==</span> <span class="n">n3</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;n == n3&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;n != n3, yes&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">  if (w == m) {
</span></span></span><span class="line"><span class="cl"><span class="cm">    cout &lt;&lt; &#34;w == m&#34;;
</span></span></span><span class="line"><span class="cl"><span class="cm">  } else {
</span></span></span><span class="line"><span class="cl"><span class="cm">    cout &lt;&lt; &#34;w != m&#34;;
</span></span></span><span class="line"><span class="cl"><span class="cm">  }
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>输出如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">EnumOnly::Week::Mon <span class="o">=</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">EnumClass::Month::Jan <span class="o">=</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">Is EnumOnly::Week a class : <span class="m">0</span>
</span></span><span class="line"><span class="cl">Is EnumOnly::Week a enum : <span class="m">1</span>
</span></span><span class="line"><span class="cl">Is EnumClass::Month a class : <span class="m">0</span>
</span></span><span class="line"><span class="cl">Is EnumClass::Month a enum : <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nv">w</span> <span class="o">==</span> s, ohh!
</span></span><span class="line"><span class="cl"><span class="nv">n</span> <span class="o">==</span> n2, yes
</span></span><span class="line"><span class="cl">n !<span class="o">=</span> n3, yes
</span></span></code></pre></td></tr></table>
</div>
</div><p>如果用 <code>==</code> 比较两个不同类型的 <code>enum class</code> 对象会产生编译错误。
比如用<code>if (w == m)</code>比较 <code>enum Week</code> 类型的 <code>w</code> 与 <code>enum class Month</code> 类型的 <code>m</code> 会产生如下错误 <code>error</code> ：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">error: no match <span class="k">for</span> ‘operator<span class="o">==</span>’ <span class="o">(</span>operand types are ‘EnumOnly::Week’ and ‘EnumClass::Month’<span class="o">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">|</span>   <span class="k">if</span> <span class="o">(</span><span class="nv">w</span> <span class="o">==</span> m<span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">|</span>       ~ ^~ ~
</span></span><span class="line"><span class="cl">      <span class="p">|</span>       <span class="p">|</span>    <span class="p">|</span>
</span></span><span class="line"><span class="cl">      <span class="p">|</span>       <span class="p">|</span>    EnumClass::Month
</span></span><span class="line"><span class="cl">      <span class="p">|</span>       EnumOnly::Week
</span></span></code></pre></td></tr></table>
</div>
</div><p>用 <code>==</code> 比较两个不同的 <code>enum class</code> 对象也会产生相同的编译错误。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>git ssh REMOTE HOST IDENTIFICATION HAS CHANGED</title>
      <link>https://blog.tomatostore.top/posts/2023/05/git-remote-host-identification-has-changed/</link>
      <pubDate>Tue, 09 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/05/git-remote-host-identification-has-changed/</guid>
      <description>Could not read from remote repository</description>
      <content:encoded><![CDATA[<p>使用 <code>git</code> 命令或 <code>ssh</code> 命令时出现如下提示：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
</span></span><span class="line"><span class="cl">@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
</span></span><span class="line"><span class="cl">@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
</span></span><span class="line"><span class="cl">IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
</span></span><span class="line"><span class="cl">Someone could be eavesdropping on you right now <span class="o">(</span>man-in-the-middle attack<span class="o">)</span>!
</span></span><span class="line"><span class="cl">It is also possible that a host key has just been changed.
</span></span><span class="line"><span class="cl">The fingerprint <span class="k">for</span> the RSA key sent by the remote host is
</span></span><span class="line"><span class="cl">SHA256:xxxxxxx.
</span></span><span class="line"><span class="cl">Please contact your system administrator.
</span></span><span class="line"><span class="cl">Add correct host key in /Users/xxx/.ssh/known_hosts to get rid of this message.
</span></span><span class="line"><span class="cl">Offending RSA key in /Users/xxx/.ssh/known_hosts:1
</span></span><span class="line"><span class="cl">Host key <span class="k">for</span> github.com has changed and you have requested strict checking.
</span></span><span class="line"><span class="cl">Host key verification failed.
</span></span><span class="line"><span class="cl">fatal: Could not <span class="nb">read</span> from remote repository.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Please make sure you have the correct access rights
</span></span><span class="line"><span class="cl">and the repository exists.
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="solution">Solution</h2>
<p>执行如下命令，找到输出中 <code>github.com</code> 对应的 IP 地址。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-keygen -lf ~/.ssh/known_hosts
</span></span></code></pre></td></tr></table>
</div>
</div><p>比如：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="m">256</span> SHA256:xxxxxxxxxxxxxxxx github.com,13.250.177.223
</span></span></code></pre></td></tr></table>
</div>
</div><p>对找到的 IP 地址（e.g. <code>13.250.177.223</code>），执行：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">&gt;&gt;&gt;&gt; $ ssh-keygen -R 13.250.177.223
</span></span></code></pre></td></tr></table>
</div>
</div><p>完成！
执行成功后会有如下输出：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Host 13.250.177.223 found: line 1</span>
</span></span><span class="line"><span class="cl">/Users/xxx/.ssh/known_hosts updated.
</span></span><span class="line"><span class="cl">Original contents retained as /Users/xxx/.ssh/known_hosts.old
</span></span></code></pre></td></tr></table>
</div>
</div><p>再执行 <code>git</code> 或 <code>ssh</code> 命令时会有如下提示，输入 <code>yes</code> 即可。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">Are you sure you want to <span class="k">continue</span> connecting <span class="o">(</span>yes/no<span class="o">)</span>? 
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    
    <item>
      <title>如何删除Karabiner</title>
      <link>https://blog.tomatostore.top/posts/2023/05/macos-cannot-perform-action-because-locked/</link>
      <pubDate>Tue, 02 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/05/macos-cannot-perform-action-because-locked/</guid>
      <description>不能完成此操作，因为项目“Karabiner-Elements.app”已被锁定</description>
      <content:encoded><![CDATA[<ol>
<li><a href="#%E6%96%B9%E6%B3%95%E4%B8%80">方法一</a></li>
<li><a href="#%E6%96%B9%E6%B3%95%E4%BA%8C">方法二</a></li>
<li><a href="#%E6%96%B9%E6%B3%95%E4%B8%89">方法三</a></li>
</ol>
<p>macOS 12，从 应用程序 中手动删除 Karabiner 时出现如下错误：</p>
<pre tabindex="0"><code>不能完成此操作，因为项目“Karabiner-Elements.app”已被锁定。
不能完成此操作，因为项目“Karabiner-EventViewer.app”已被锁定。
</code></pre><h2 id="方法一">方法一</h2>
<p><a href="https://karabiner-elements.pqrs.org/docs/manual/operation/uninstall/">参照官方 uninstall 步骤</a>，如果 Karabiner-Elements 可以运行，可以通过界面操作卸载(Uninstall -&gt; Launch uninstaller)，但我的 App 已无法执行。</p>
<h2 id="方法二">方法二</h2>
<p>执行如下命令：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bash <span class="s1">&#39;/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/scripts/uninstall/deactivate_driver.sh&#39;</span>
</span></span><span class="line"><span class="cl">sudo <span class="s1">&#39;/Library/Application Support/org.pqrs/Karabiner-Elements/uninstall.sh&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>但我的环境中已经没有相关 sh，所以第一条指令已经失败：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ bash <span class="s1">&#39;/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/scripts/uninstall/deactivate_driver.sh&#39;</span>
</span></span><span class="line"><span class="cl">bash: /Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/scripts/uninstall/deactivate_driver.sh: No such file or directory
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="方法三">方法三</h2>
<p>终极方案——重装。从 <a href="https://karabiner-elements.pqrs.org/">https://karabiner-elements.pqrs.org/</a> 下载安装，然后通过<a href="#%E6%96%B9%E6%B3%95%E4%B8%80">方法一</a>删除：</p>
<p>
  <img loading="lazy" src="https://karabiner-elements.pqrs.org/docs/manual/operation/uninstall/images/uninstall-button@2x.png" alt="built-in uninstaller"  /></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>error: git object file .git/objects/xxx is empty 和 fatal: cannot read existing object info</title>
      <link>https://blog.tomatostore.top/posts/2023/04/git-object-file-is-empty/</link>
      <pubDate>Thu, 20 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/04/git-object-file-is-empty/</guid>
      <description>index-pack failed，unable to unlink null bad address</description>
      <content:encoded><![CDATA[<h2 id="object-file-相关错误"><code>object file</code> 相关错误</h2>
<p>本地的<code>repo</code>在执行<code>git log</code>和<code>git pull</code>命令时出现了如下关于<code>object file</code>的错误：</p>
<h3 id="git-log-错误"><code>git log</code> 错误</h3>
<pre tabindex="0"><code>&gt;&gt;&gt;&gt; $ git log
error: object file .git/objects/7b/40exxx is empty
error: object file .git/objects/7b/40exxx is empty
fatal: loose object 7b40exxx (stored in .git/objects/7b/40exxx) is corrupt
</code></pre><h3 id="git-pull-错误"><code>git pull</code> 错误</h3>
<pre tabindex="0"><code>&gt;&gt;&gt;&gt; $ git pull
error: object file .git/objects/7b/40exxx is empty
error: object file .git/objects/7b/40exxx is empty
remote: Counting objects: 479, done
remote: Finding sources: 100% (1388/1388)
error: object file .git/objects/7b/40exxx is empty
fatal: cannot read existing object info 40exxx
fatal: index-pack failed
warning: unable to unlink &#39;(null)&#39;: Bad address
</code></pre><h2 id="解决方法-solution">解决方法 Solution</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">cd</span> &lt;your_repo&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 备份 .git</span>
</span></span><span class="line"><span class="cl">cp -r .git .git-bak
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> .git
</span></span><span class="line"><span class="cl">find . -type f -empty -delete -print
</span></span><span class="line"><span class="cl">git fsck --full
</span></span></code></pre></td></tr></table>
</div>
</div><p>再次尝试<code>git log</code> 和 <code>git pull</code>命令。一切正常后删除备份 <code>rm -r .git-bak</code>。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何从 App Store Connect 中删除一个未发布版本</title>
      <link>https://blog.tomatostore.top/posts/2023/04/app-store-connect-rm-a-version/</link>
      <pubDate>Thu, 20 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/04/app-store-connect-rm-a-version/</guid>
      <description>苹果商店Connect无法删除App版本</description>
      <content:encoded><![CDATA[<p>一个 iOS App 最新已发布版本: <code>4.0</code>，在 <code>App Store Connect</code> 中添加了一个版本 <code>4.1</code>，状态为 <code>准备提交（Prepare for Submission）</code> (或其他未发布状态)。</p>
<p>后来改动有点大，打算做个大版本升级——把<code>4.1</code> 改为 <code>5.0</code>。</p>
<p>没有找到删除 <code>4.1</code> 的方法，但是可以通过如下途径更改版本为 <code>5.0</code>:</p>
<p>在 <code>version 4.1</code> 构建页面的 版本信息（Version Information） 模块下将版本 <code>4.1</code> 直接改为 <code>5.0</code>:</p>
<p><img loading="lazy" src="/images/v4.1.png" alt="v5.0"  /></p>
<p>点击 保存（Save）按钮，版本 <code>4.1</code> 就变成 <code>5.0</code> 啦：</p>
<p><img loading="lazy" src="/images/v5.0.png" alt="v5.0"  /></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何在 Markdown 中插入图片，并设置大小</title>
      <link>https://blog.tomatostore.top/posts/2023/04/markdown-insert-picture-with-size/</link>
      <pubDate>Thu, 20 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/04/markdown-insert-picture-with-size/</guid>
      <description>Add pictures to Markdown, with hyperlink and size setting</description>
      <content:encoded><![CDATA[<p>本文介绍在 <code>Markdown</code> 文件中插入图片的两种方式：<a href="#markdown_insert_img">1.Markdown语法</a>，<a href="#html_insert_img">2.HTML语法</a>；
以及<a href="#image_size">如何设置图片尺寸</a>，<a href="#image_link">如何给图片加链接</a>。</p>
<p><a name="markdown_insert_img"></a></p>
<h2 id="1-使用-markdown-语法">1. 使用 <code>Markdown</code> 语法</h2>
<ul>
<li>以叹号<code>!</code>开头</li>
<li>后面方括号<code>[]</code>中是替换文本（图片加载失败时显示给读者的文本）</li>
<li>再后面圆括号<code>()</code>中是图片的路径（本地路径、网络路径）</li>
<li>如果需要图片标题，可以在路径后面用双引号<code>&quot;&quot;</code>扩起来。
<em>⚠️：所有的<strong>符号必须是半角/英文符号</strong>，不可以使用全角/中文符号</em></li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">![<span class="nt">替换文本</span>](<span class="na">图片路径 &#34;图片标题&#34;</span>)
</span></span><span class="line"><span class="cl">![<span class="nt">alt text</span>](<span class="na">image_path &#34;image title&#34;</span>)
</span></span></code></pre></td></tr></table>
</div>
</div><p>比如</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">![<span class="nt">substring is deprecated</span>](<span class="na">/images/xcode-while-editing.png &#34;XCode屏幕截图&#34;</span>)
</span></span><span class="line"><span class="cl"><span class="ge">*Tips: 可以在图片下面用一行斜体的说明文本。图片标题不保证一定会显示*</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>显示效果如下：
<img loading="lazy" src="/images/xcode-while-editing.png" alt="substring is deprecated"  title="XCode屏幕截图"  />
<em>Tips: 可以在图片下面用一行斜体的说明文本。图片标题不保证一定会显示</em></p>
<p><a name="html_insert_img"></a></p>
<h2 id="2-使用-html-语法">2. 使用 <code>HTML</code> 语法</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">image</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://picsum.photos/400/200&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;网络图片400*200&#34;</span><span class="p">&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>效果如下：
<image src="https://picsum.photos/400/200" alt="网络图片400*200" title="HTML图片"></p>
<p><a name="image_size"></a></p>
<h3 id="如何设置图片尺寸">如何设置图片尺寸</h3>
<p>推荐使用<code>HTML</code>的方式设置图片大小：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">image</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://picsum.photos/400/200&#34;</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;网络图片400*200&#34;</span> <span class="na">width</span><span class="o">=</span><span class="s">&#34;100&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;200&#34;</span><span class="p">&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><image src="https://picsum.photos/400/200" alt="网络图片400*200" width="100" height="200">
<p>原生<code>Markdown</code>语法不支持设置图片尺寸，<code>Extended Markdown</code>支持如下语法。不是所有引擎都支持，个人不推荐。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">![<span class="nt">alt text</span>](<span class="na">image path &#34;image title&#34;</span>){ width=80%,height=120px }
</span></span></code></pre></td></tr></table>
</div>
</div><a name="image_link" />
<h3 id="如何给图片加链接">如何给图片加链接</h3>
<h4 id="markdown语法"><code>Markdown</code>语法</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">[<span class="nt">![图片替换文本</span>](<span class="na">图片路径</span>)](链接地址)
</span></span></code></pre></td></tr></table>
</div>
</div><p>比如</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">[<span class="nt">![点击图片跳转AppStore</span>](<span class="na">https://mzstatic.com/image/AppIcon.png</span>)](https://apps.apple.com)
</span></span></code></pre></td></tr></table>
</div>
</div><p><a href="https://apps.apple.com/cn/app/id1528477391">
  <img loading="lazy" src="https://is4-ssl.mzstatic.com/image/thumb/Purple122/v4/72/62/8a/72628ae2-9c11-e676-acb2-5e207f07aa65/AppIcon-1x_U007emarketing-0-6-0-85-220.png/460x0w.webp" alt="点击图片跳转AppStore"  /></a></p>
<h4 id="html语法"><code>HTML</code>语法</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://apps.apple.com/cn/app/id1528477391&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://mzstatic.com/image/AppIcon.png&#34;</span><span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><a href="https://apps.apple.com/cn/app/id1528477391">
    <img src="https://is4-ssl.mzstatic.com/image/thumb/Purple122/v4/72/62/8a/72628ae2-9c11-e676-acb2-5e207f07aa65/AppIcon-1x_U007emarketing-0-6-0-85-220.png/460x0w.webp" />
</a>
]]></content:encoded>
    </item>
    
    <item>
      <title>Bad owner or permissions on ssh config/git命令.ssh文件权限错误</title>
      <link>https://blog.tomatostore.top/posts/2023/04/ssh-file-permission/</link>
      <pubDate>Mon, 17 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/04/ssh-file-permission/</guid>
      <description>ssh Permissions are too open</description>
      <content:encoded><![CDATA[<p>在 macOS 的终端中执行 <code>git pull</code> 命令时出现如下两个错误，可以通过手动修改权限来修复。同样适用于 Linux 系统及其他 ssh 相关应用出现的此类错误。</p>
<ul>
<li>错误一：</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Bad owner or permissions on &lt;user&gt;/.ssh/config
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>错误二：</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
</span></span><span class="line"><span class="cl">@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
</span></span><span class="line"><span class="cl">@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
</span></span><span class="line"><span class="cl">Permissions 0640 for &#39;&lt;user&gt;/.ssh/id_rsa&#39; are too open.
</span></span><span class="line"><span class="cl">It is required that your private key files are NOT accessible by others.
</span></span><span class="line"><span class="cl">This private key will be ignored.
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="solution">Solution</h2>
<p>通过如下方式手动修复 <code>.ssh</code> 目录及文件权限：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">cd</span> ~/.ssh
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">700</span> .      <span class="c1"># .ssh dir</span>
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">700</span> *.pub  <span class="c1"># PUBLIC key file</span>
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">600</span> id_rsa <span class="c1"># PRIVATE key file</span>
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">600</span> config <span class="c1"># config file</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="reference">Reference</h2>
<p>其他类型文件的权限及详细描述参见 <code>man ssh</code>：</p>
<p><img loading="lazy" src="/images/man-ssh.png" alt="man ssh"  /></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Swift String Substring</title>
      <link>https://blog.tomatostore.top/posts/2023/04/swift-substring/</link>
      <pubDate>Sun, 16 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/04/swift-substring/</guid>
      <description>&lt;p&gt;Swift 中对 &lt;code&gt;String&lt;/code&gt; 类型的 &lt;code&gt;substring()&lt;/code&gt; 方法都被标记为&lt;code&gt;不推荐(deprecated)&lt;/code&gt;了，那该如何实现 substring 的功能呢？&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://blog.tomatostore.top/images/swift-string-substring-deprecated.jpg&#34; alt=&#34;substring is deprecated&#34;  title=&#34;XCode 中对String.substring的提示&#34;  /&gt;&lt;/p&gt;
&lt;p&gt;根据Xcode的提示&lt;strong&gt;可以使用&lt;code&gt;slicing（切片）&lt;/code&gt;的方式来实现&lt;code&gt;substring&lt;/code&gt;的功能&lt;/strong&gt;：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Swift 中对 <code>String</code> 类型的 <code>substring()</code> 方法都被标记为<code>不推荐(deprecated)</code>了，那该如何实现 substring 的功能呢？</p>
<p><img loading="lazy" src="/images/swift-string-substring-deprecated.jpg" alt="substring is deprecated"  title="XCode 中对String.substring的提示"  /></p>
<p>根据Xcode的提示<strong>可以使用<code>slicing（切片）</code>的方式来实现<code>substring</code>的功能</strong>：</p>
<p>以字符串 <code>let str = &quot;0123456789&quot;</code> 为例:</p>
<h3 id="从开始到第一个等于5的字符之前的子串">从开始到第一个等于<code>5</code>的字符之前的子串：</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="k">if</span> <span class="kd">let</span> <span class="nv">firstFive</span> <span class="p">=</span> <span class="n">str</span><span class="p">.</span><span class="n">firstIndex</span><span class="p">(</span><span class="n">of</span><span class="p">:</span> <span class="s">&#34;5&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">substr</span> <span class="p">=</span> <span class="n">str</span><span class="p">[</span><span class="n">str</span><span class="p">.</span><span class="n">startIndex</span><span class="p">..&lt;</span><span class="n">firstFive</span><span class="p">]</span> <span class="c1">// substr = 01234</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 或者可以省略开始的index</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">substr2</span> <span class="p">=</span> <span class="n">str</span><span class="p">[..&lt;</span><span class="n">firstFive</span><span class="p">]</span> <span class="c1">// substr2 = 01234</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="从开头到第一个等于5的字符">从开头到第一个等于<code>5</code>的字符</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">substr</span> <span class="p">=</span> <span class="n">str</span><span class="p">[...</span><span class="n">firstFive</span><span class="p">]</span> <span class="c1">// substr = 012345</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="从第一个5开始到字符串末尾">从第一个<code>5</code>开始到字符串末尾</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">substr</span> <span class="p">=</span> <span class="n">str</span><span class="p">[</span><span class="n">firstFive</span><span class="p">...]</span> <span class="c1">// 56789</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="前7个字符">前7个字符</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">strstr</span> <span class="p">=</span> <span class="n">str</span><span class="p">[</span><span class="n">str</span><span class="p">.</span><span class="n">startIndex</span><span class="p">..&lt;</span><span class="n">str</span><span class="p">.</span><span class="n">index</span><span class="p">(</span><span class="n">str</span><span class="p">.</span><span class="n">startIndex</span><span class="p">,</span> <span class="n">offsetBy</span><span class="p">:</span> <span class="mi">7</span><span class="p">)]</span> <span class="c1">// 0123456</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>String</code>切片后返回的结果类型是 <code>Substring</code>。</p>
<h2 id="struct-substring">struct Substring</h2>
<p><code>Substring</code> 不会为字符串额外分配空间，而是与原始字符串共享存储。</p>
<blockquote>
<p><strong>划重点：</strong> 因为<code>Substring</code>会持有对原始字符存储的引用计数，所以要确保子串的生命周期不长于源字符串的生命周期，以免内存泄漏。</p></blockquote>
<hr>
<blockquote>
<p>Ref: <a href="https://swiftunboxed.com/stdlib/substrings">https://swiftunboxed.com/stdlib/substrings</a></p></blockquote>
]]></content:encoded>
    </item>
    
    <item>
      <title>关于ASCII、DBCS、Unicode、UCS-16、UTF-8和Encoding</title>
      <link>https://blog.tomatostore.top/posts/2023/04/unicode-and-character-sets/</link>
      <pubDate>Wed, 12 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/04/unicode-and-character-sets/</guid>
      <description>每个程序员都应该知道的字符集发展简史</description>
      <content:encoded><![CDATA[<p>写于二十年前(<code>2003年</code>)的文章，现在依然受用。</p>
<p>你是否曾探究过<code>HTTP</code>请求中神秘的<code>Content-Type</code>？你是否曾遇到过满是问号 <code>???? ???? ??</code> 的电子邮件（或网页）？</p>
<p>作者发现多数程序开发人员并不是完全明白字符集<code>character set</code>、编码<code>encoding</code>、<code>Unicode</code>、<code>UTF-8</code>等相关话题。</p>
<p>这篇文章回顾了字符集的发展历史。</p>
<p><img loading="lazy" src="/images/ascii.png" alt="ASCII"  /></p>
<h2 id="asciiamerican-standard-code-for-information-interchange美国信息交换标准代码"><code>ASCII</code>（<code>American Standard Code for Information Interchange</code>，美国信息交换标准代码）</h2>
<p>回到<code>Unix</code>正在被发明的上古时代，一个英文字符的<code>ASCII</code>值可以用 <code>32-127</code> 范围内的数字来表示，包括英文字母、数字和符号。
比如空格是<code>32</code>，<code>A</code>是<code>65</code>，<code>a</code>是<code>97</code>。 <code>0-31</code> 被用作操作符。如<code>10</code>为换行，<code>13</code>为回车。</p>
<p>计算机的最小寻址单位是一字节（<code>8</code>个比特），<code>127</code>只需要<code>7</code>个比特位就能表示（<code>2的7次方=128</code>），所以还有一比特位（值<code>128-255</code>）空闲。</p>
<p>这些空闲的值在不同的语言地区被赋予不同的用法。比如美洲地区用<code>130</code>来表示<code>é</code>，但是以色列的电脑用<code>130</code>来表示希伯来字符<code>ג</code>。那么在美洲电脑上显示的单词<code>résumés</code>，在以色列的电脑上就成了 <code>rגsumגs</code>。这导致了同一文档内容在不同的电脑上可能有不同的显示。</p>
<h2 id="code-page代码页">Code Page（代码页）</h2>
<p><code>ANSI</code>（<code>American National Standards Institute</code>，美国国家标准学会）在制定标准的时候，引入了代码页。</p>
<p>这些代码页<code>128</code>以下是一样的，只有<code>128</code>以上不同。如，以色列使用<code>code page 862</code>，希腊使用 <code>code page 737</code>。</p>
<p>这样在同一电脑上可以有多个不同的代码页可供选择。</p>
<h2 id="dbcsdouble-byte-character-set双字节字符集"><code>DBCS</code>（<code>Double Byte Character Set</code>，双字节字符集）</h2>
<p>但是，由于亚洲语言有成千上万的字符，没办法用一字节（<code>8</code>比特）完全表示。</p>
<p><code>DBCS</code> 是对英文字符使用单字节，其他字符（如中日韩文）使用双字节。访问文本时，只能从前向后处理。因为需要一直判断当前字节是不是前导字节，对于既有可能是前导也有可能是后续的字节，只能通过上下文做判断。</p>
<p>对计算机程序及程序员来说，操作起来比较麻烦、容易出错。</p>
<h2 id="unicode统一码万国码"><code>Unicode</code>（统一码、万国码）</h2>
<p>一套可以适用于全世界所有国家的字符集。</p>
<p>对 <code>Unicode</code> 最常见的误解是：每个字符占用<code>16</code>比特，因此能表示的字符个数是 <code>2的16次方=65536</code>个。实际上，这是不对的。</p>
<p><code>Unicode</code> 中对所有字母表中的每个字符分配一个数字——<code>code point</code>，如 <code>A</code> 的<code>code point</code> 是 <code>U+0041</code>；<code>迪</code>的 <code>code point</code> 是 <code>U+8FEA</code>。</p>
<p><code>Unicode</code> 中的字符，是一个概念定义。比如字符<code>A</code>，很明显<code>A</code>不同于<code>B</code>，也不同于<code>a</code>，但是无论是斜体的<code>A</code>、粗体的<code>A</code>、还是正常的<code>A</code>，它们应该是一样的，都是字符<code>A</code>。对于字体<code>Times New Roman</code>中的<code>A</code>，应该和字体<code>Helvetica</code>中的<code>A</code>一样，都是字符<code>A</code>。(对于字符<code>A</code>，<code>Unicode</code>只定义<code>U+0041</code>是字符<code>A</code>，而具体的这个字符显示出来时什么样子，是定义在字体文件中的)</p>
<h2 id="ucs-2--utf-16"><code>UCS-2</code> / <code>UTF-16</code></h2>
<p>那么 <code>He</code> 用 <code>Unicode</code> 表示就是 <code>00 48 00 65</code>。</p>
<p>对吗？憋着急。那它为啥不能是 <code>48 00 65 00</code> 呢？技术上来说没有问题，而实际上也是两种方式都存在（因为特定的 <code>CPU</code> 用特定的顺序会更快）——大端序（<code>big-edian</code>）和 小端序（<code>little-endian</code>）。</p>
<p>为了区分，在文本开头加上字节顺序标记位，大端序用 <code>FE FF</code>，小端序用 <code>FF FE</code>。</p>
<p>这种方式对于新生成的文档没有任何问题，但是无法兼容已有的<code>ASCII</code>和<code>DBCS</code>文档。</p>
<h2 id="utf-8unicode-transformation-format-8-bit">UTF-8（Unicode Transformation Format 8-bit）</h2>
<p>所以，出现了另一种实现 <code>Unicode</code> 字符集的系统——<code>UTF-8</code>。它采用变长的编码，用一个字节存储 <code>code point 0-127</code>，对 <code>128</code> 以上的使用 <code>2字节、3字节，最多到 6字节</code>。</p>
<p>比如，表示字符 <code>₤</code> 的 <code>Unicode</code> 十六进制值为 <code>U+20A4</code>，其二进制为 <code>0010 0000 1010 0100</code>。用<code>UTF-8</code> 来表示字符<code>₤</code>的话，需要<code>3</code>个字节：</p>
<ul>
<li>第一个字节的前四位，用<code>3</code>比特<code>1</code>（代表<code>3</code>个字节）加一个<code>0</code>：<code>1110</code>。后四位接<code>20A4</code>的前四子节：<code>0010</code>。得到<code>1110 0010</code>。</li>
<li>第二子节以<code>10</code>开头，后六字节接<code>20A4</code>的六字节 <code>0000 10</code>，得到<code>1000 0010</code>。</li>
<li>第三子节还是以<code>10</code>开头，后六字节接<code>20A4</code>最后六字节<code>10 0100</code>，得到<code>1010 0100</code>。</li>
</ul>
<p>最终得到字符<code>₤</code>的<code>UTF-8</code>表示为<code>1110-0010 1000-0010 1010-0100</code>。</p>
<blockquote>
<p>对于英文文档来说，<code>UTF-8</code> 和 <code>ASCII</code>是一样的，不需要任何转换。</p></blockquote>
<h3 id="utf-8-比-utf-16-省空间吗">UTF-8 比 UTF-16 省空间吗？</h3>
<p><strong>不一定</strong>。</p>
<table>
  <thead>
      <tr>
          <th style="text-align: center">字符串</th>
          <th>UTF-8</th>
          <th>UTF-16</th>
          <th>空间比较</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: center"><code>AI</code></td>
          <td><code>4149</code></td>
          <td><code>0041 0049</code></td>
          <td><code>UTF-8</code>&lt;<code>UTF-16</code></td>
      </tr>
      <tr>
          <td style="text-align: center"><code>射雕</code></td>
          <td><code>E5B0 84E9 9B95</code></td>
          <td><code>5C04 96D5</code></td>
          <td><code>UTF-8</code>&gt;<code>UTF-16</code></td>
      </tr>
  </tbody>
</table>
<blockquote>
<p>如果存储数据英文字符较多，<code>UTF-8</code>肯定会比<code>UTF-16</code>节省空间。</p></blockquote>
<h2 id="encodings编码">Encodings（编码）</h2>
<p><img loading="lazy" src="/images/character-encoding.jpeg" alt="encoding"  /></p>
<p><code>Unicode</code>是字符集，上面说的 <code>UCS-2</code>、<code>UTF-8</code>等是实现<code>Unicode</code>的方法——<code>Encoding</code>。<code>Encoding</code>有多种实现，如<code>UTF-7</code>、<code>UTF-32</code>、<code>ISO8859-1</code>。</p>
<p>所以，必须确定所用的<code>Encoding</code>才能正确处理一个字符串。</p>
<p>在浏览器中看到这每个网页会有<code>Encoding</code>的信息：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">&#34;Content-Type&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;text/html; charset=utf-8&#34;</span><span class="p">&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>可是，在不知道<code>encoding</code>的情况下怎么去读这个<code>HTML</code>文件来取到<code>encoding</code>的信息呢？</p>
<p>幸运的是，绝大多数<code>encoding</code>对 <code>32-127</code> 的<code>code point</code>的处理方式是一样的，可以把文件开头的内容当作<code>ASCII</code>来处理。所以<code>encoding</code>信息越早出现越好，避免出现在任何<code>code point</code>大于<code>127</code>的字符之后。</p>
<p>另外，<code>HTTP</code>请求头中也可以包含<code>encoding</code>信息（<code>Content-Type</code>）。</p>
<p>早期的浏览器都有设置<code>encoding</code>的菜单，以应对找不到<code>encoding</code>定义而无法正确处理的情况。大部分现代浏览器（如<code>Chrome</code>）已经去掉该菜单，可以根据上下文判断出正确<code>encoding</code>了。</p>
<h2 id="原文链接">原文链接</h2>
<ul>
<li><a href="https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/">The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)</a></li>
<li><a href="https://mp.weixin.qq.com/s?__biz=Mzg5NDE1MTU2NQ==&amp;mid=2247483816&amp;idx=1&amp;sn=2be8183562976b65cbdc37c711ff79fb&amp;chksm=c022bf4af755365cf1440172cb044f204758796097fb42dfc028ff31c58505b5648e2478f10a&amp;token=258030414&amp;lang=zh_CN#rd">每个程序员都应该知道的字符集发展简史</a></li>
</ul>
<blockquote>
</blockquote>
]]></content:encoded>
    </item>
    
    <item>
      <title>设计模式：观察者模式、订阅-发布模式、事件监听模式</title>
      <link>https://blog.tomatostore.top/posts/2023/04/design-pattern-observer/</link>
      <pubDate>Tue, 11 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/04/design-pattern-observer/</guid>
      <description>Design Pattern: Observer Pattern</description>
      <content:encoded><![CDATA[<p>观察者模式（<code>Observer Pattern</code>）解决的对象间的一对多的关系，即一个被观察者与多个观察者。最直接的例子就是公众号（<code>Subject</code>）及订阅者（<code>Observer</code>）。</p>
<p>对于观察者模式、发布-订阅模式/事件监听模式，为只是相同技术实现基础上的不同演化，属于同一类设计模式。</p>
<hr>
<h2 id="观察者模式10">观察者模式1.0</h2>
<p>涉及两个角色：</p>
<ul>
<li>被观察者: Subject/Observable/Publisher, 会有状态/数据变化的对象。</li>
<li>观察者: Observer/Subscriber，关心<code>被观测对象</code>的变化。</li>
</ul>
<p>最基础的实现需要定义 2 个基类 <code>Subject</code> 和 <code>Observer</code>。</p>
<blockquote>
<p><code>C++</code>实现</p></blockquote>
<h3 id="观察者">观察者</h3>
<p>先定义接口类 <code>class Observer</code>，该类没有默认实现，子类必须实现 <code>update()</code> 接口。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// Observer.h 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Observer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">virtual</span> <span class="kt">void</span> <span class="n">update</span><span class="p">(</span><span class="n">string</span> <span class="n">msg</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// abstract virtual
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="被观察对象">被观察对象</h3>
<p>基类 <code>Subject</code>，它需要知道有那些观察者，在需要通知的时候遍历观察者列表发送通知。
有自己的实现，定义子类时不需要复写基类的方法，直接使用即可。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// Subject.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Subject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">virtual</span> <span class="kt">void</span> <span class="n">addObserver</span><span class="p">(</span><span class="n">Observer</span> <span class="o">*</span> <span class="n">observer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">removeObserver</span><span class="p">(</span><span class="n">Observer</span> <span class="o">*</span> <span class="n">observer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">notify</span><span class="p">(</span><span class="n">string</span> <span class="n">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">Observer</span><span class="o">*&gt;</span> <span class="n">observers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// Subjet.cpp
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">Subject</span><span class="o">::</span><span class="n">addObserver</span><span class="p">(</span><span class="n">Observer</span><span class="o">*</span> <span class="n">observer</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">observers</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">observer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">Subject</span><span class="o">::</span><span class="n">removeObserver</span><span class="p">(</span><span class="n">Observer</span><span class="o">*</span> <span class="n">observer</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">observers</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">observer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">Subject</span><span class="o">::</span><span class="n">notify</span><span class="p">(</span><span class="n">string</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">obs</span><span class="p">:</span> <span class="n">observers</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">obs</span><span class="o">-&gt;</span><span class="n">update</span><span class="p">(</span><span class="n">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="应用">应用</h3>
<p>以公众号和订阅者为例。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 公众号
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">class</span> <span class="nc">Publisher</span><span class="o">:</span> <span class="k">public</span> <span class="n">Subject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">addArticle</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span><span class="o">&amp;</span> <span class="n">article</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">articles</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">article</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">notify</span><span class="p">(</span><span class="n">article</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">articles</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 订阅者
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">class</span> <span class="nc">Subscriber</span><span class="o">:</span> <span class="k">public</span> <span class="n">Observer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Subscriber</span><span class="p">(</span><span class="n">string</span> <span class="n">name</span><span class="p">)</span><span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">update</span><span class="p">(</span><span class="n">string</span> <span class="n">article</span><span class="p">)</span> <span class="k">override</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">readArticle</span><span class="p">(</span><span class="n">article</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">readArticle</span><span class="p">(</span><span class="n">string</span> <span class="n">article</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">name</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; is reading article : &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">article</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">string</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Publisher</span> <span class="n">publisher</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Subscriber</span> <span class="n">sub1</span><span class="p">(</span><span class="s">&#34;Tom&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addObserver</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sub1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addArticle</span><span class="p">(</span><span class="s">&#34;Article 1&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Subscriber</span> <span class="n">sub2</span><span class="p">(</span><span class="s">&#34;Jerry&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addObserver</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sub2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addArticle</span><span class="p">(</span><span class="s">&#34;Article 2&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>执行结果：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">Tom</span> <span class="n">is</span> <span class="n">reading</span> <span class="nl">article</span> <span class="p">:</span> <span class="n">Article</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Tom</span> <span class="n">is</span> <span class="n">reading</span> <span class="nl">article</span> <span class="p">:</span> <span class="n">Article</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="n">Jerry</span> <span class="n">is</span> <span class="n">reading</span> <span class="nl">article</span> <span class="p">:</span> <span class="n">Article</span> <span class="mi">2</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="观察者模式20">观察者模式2.0</h2>
<p>上面的<code>Observer</code>提供了一个带有一个参数的<code>update</code>接口： <code>virtual void update(string msg)</code>。
虽然在应用中可以通过传递不同参数来区分不同的通知，比如：<code>update(&quot;全部订阅者可见更新&quot;)</code>，<code>update(&quot;VIP订阅者可见更新&quot;)</code>，但对于复杂一点的需要多参数传递的场景就不适用了。</p>
<p>那么可以通过引入<code>Event</code>类型，以更灵活的定义不同的、复杂的通知类型。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// Event 接口定义
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">EventType</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ARTICLE_UPDATED</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">VIP_ARTICLE_UPDATED</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Event</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">virtual</span> <span class="n">EventType</span> <span class="n">type</span><span class="p">()</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">virtual</span> <span class="o">~</span><span class="n">Event</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 具体的 Event 类型
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">class</span> <span class="nc">ArticleUpdatedEvent</span><span class="o">:</span> <span class="k">public</span> <span class="n">Event</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">virtual</span> <span class="n">EventType</span> <span class="n">type</span><span class="p">()</span> <span class="k">override</span> <span class="p">{</span> <span class="k">return</span> <span class="n">ARTICLE_UPDATED</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">string</span> <span class="nf">getArticle</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">article</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">setArticle</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span><span class="o">&amp;</span> <span class="n">article</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">article</span> <span class="o">=</span> <span class="n">article</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">string</span> <span class="n">article</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">VipArticleUpdatedEvent</span><span class="o">:</span> <span class="k">public</span> <span class="n">Event</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">virtual</span> <span class="n">EventType</span> <span class="n">type</span><span class="p">()</span> <span class="k">override</span> <span class="p">{</span> <span class="k">return</span> <span class="n">VIP_ARTICLE_UPDATED</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">string</span> <span class="nf">getArticle</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">article</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">setArticle</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span><span class="o">&amp;</span> <span class="n">article</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">article</span> <span class="o">=</span> <span class="n">article</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="nf">getReward</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">reward</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">setReward</span><span class="p">(</span><span class="kt">int</span> <span class="n">reward</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">reward</span> <span class="o">=</span> <span class="n">reward</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">string</span> <span class="n">article</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">reward</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>将<code>Observer</code>更新为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// Observer.h 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Observer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// virtual void update(string msg) = 0; // abstract virtual
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">virtual</span> <span class="kt">void</span> <span class="n">update</span><span class="p">(</span><span class="n">shared_pointer</span><span class="o">&lt;</span><span class="n">Event</span><span class="o">&gt;</span> <span class="n">event</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// abstract virtual
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>Subject</code>更新为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// Subject.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Subject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="p">...</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// virtual void notify(string msg);
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">virtual</span> <span class="kt">void</span> <span class="n">notify</span><span class="p">(</span><span class="n">shared_pointer</span><span class="o">&lt;</span><span class="n">Event</span><span class="o">&gt;</span> <span class="n">event</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>应用到<code>公众号——订阅者</code>上：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span><span class="lnt">74
</span><span class="lnt">75
</span><span class="lnt">76
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="c1">// 公众号
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Publisher</span><span class="o">:</span> <span class="k">public</span> <span class="n">Subject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="n">addArticle</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span><span class="o">&amp;</span> <span class="n">article</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">auto</span> <span class="n">e</span> <span class="o">=</span> <span class="n">make_shared</span><span class="o">&lt;</span><span class="n">ArticleUpdatedEvent</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">setArticle</span><span class="p">(</span><span class="n">article</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">notify</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">addVipArticleWithReward</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span><span class="o">&amp;</span> <span class="n">article</span><span class="p">,</span> <span class="kt">int</span> <span class="n">reward</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">auto</span> <span class="n">e</span> <span class="o">=</span> <span class="n">make_shared</span><span class="o">&lt;</span><span class="n">VipArticleUpdatedEvent</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">setArticle</span><span class="p">(</span><span class="n">article</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="n">setReward</span><span class="p">(</span><span class="n">reward</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">notify</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 订阅者
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">class</span> <span class="nc">Subscriber</span><span class="o">:</span> <span class="k">public</span> <span class="n">Observer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Subscriber</span><span class="p">(</span><span class="n">string</span> <span class="n">name</span><span class="p">)</span><span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">update</span><span class="p">(</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">Event</span><span class="o">&gt;</span> <span class="n">event</span><span class="p">)</span> <span class="k">override</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">switch</span> <span class="p">(</span><span class="n">event</span><span class="o">-&gt;</span><span class="n">type</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="n">EventType</span><span class="o">::</span><span class="nl">ARTICLE_UPDATED</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">auto</span> <span class="n">e</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">ArticleUpdatedEvent</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">get</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">e</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">string</span> <span class="n">article</span> <span class="o">=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">getArticle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">readArticle</span><span class="p">(</span><span class="n">article</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="n">EventType</span><span class="o">::</span><span class="nl">VIP_ARTICLE_UPDATED</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">auto</span> <span class="n">e</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">VipArticleUpdatedEvent</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">get</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">e</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">string</span> <span class="n">vipArticle</span> <span class="o">=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">getArticle</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">readArticle</span><span class="p">(</span><span class="n">vipArticle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">int</span> <span class="n">reward</span> <span class="o">=</span> <span class="n">e</span><span class="o">-&gt;</span><span class="n">getReward</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                    <span class="n">earnReward</span><span class="p">(</span><span class="n">reward</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">readArticle</span><span class="p">(</span><span class="n">string</span> <span class="n">article</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">name</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; is reading article : &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">article</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="nf">earnReward</span><span class="p">(</span><span class="kt">int</span> <span class="n">reward</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">name</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; earned reward : &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">reward</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">string</span> <span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Publisher</span> <span class="n">publisher</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Subscriber</span> <span class="n">sub1</span><span class="p">(</span><span class="s">&#34;Tom&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addObserver</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sub1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addArticle</span><span class="p">(</span><span class="s">&#34;Article 1&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Subscriber</span> <span class="n">sub2</span><span class="p">(</span><span class="s">&#34;Jerry&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addObserver</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sub2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addVipArticleWithReward</span><span class="p">(</span><span class="s">&#34;Article 2&#34;</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>执行结果：</p>
<pre tabindex="0"><code>Tom is reading article : Article 1

Tom is reading article : Article 2
Tom earned reward : 5
Jerry is reading article : Article 2
Jerry earned reward : 5
</code></pre><p>通过继承<code>Event</code>，这个版本支持灵活的扩展通知类型，对传递的参数个数和类型没有任何限制。</p>
<p>目前为止，被订阅对象发送 <code>notify</code> 时对所有的订阅者都是一视同仁的，也就是说每个订阅者接收到的消息是一样的。</p>
<p>对于上面公众号的例子，如果想实现对 VIP 订阅者和普通订阅者推送不同的通知，那就可以通过如下的方式。</p>
<hr>
<h2 id="观察者模式30">观察者模式3.0</h2>
<p>更改 <code>Subject</code> 的订阅和通知方法：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Subject.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Subject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="k">virtual</span> <span class="kt">void</span> <span class="n">addObserver</span><span class="p">(</span><span class="n">EventType</span> <span class="n">type</span><span class="p">,</span> <span class="n">Observer</span> <span class="o">*</span> <span class="n">observer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">removeObserver</span><span class="p">(</span><span class="n">EventType</span> <span class="n">type</span><span class="p">,</span> <span class="n">Observer</span> <span class="o">*</span> <span class="n">observer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">notify</span><span class="p">(</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">Event</span><span class="o">&gt;</span> <span class="n">event</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">EventType</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">Observer</span><span class="o">*&gt;</span> <span class="o">&gt;</span> <span class="n">observers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Subjet.cpp
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">Subject</span><span class="o">::</span><span class="n">addObserver</span><span class="p">(</span><span class="n">EventType</span> <span class="n">type</span><span class="p">,</span> <span class="n">Observer</span><span class="o">*</span> <span class="n">observer</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">observers</span><span class="p">[</span><span class="n">type</span><span class="p">].</span><span class="n">insert</span><span class="p">(</span><span class="n">observer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">Subject</span><span class="o">::</span><span class="n">removeObserver</span><span class="p">(</span><span class="n">EventType</span> <span class="n">type</span><span class="p">,</span> <span class="n">Observer</span><span class="o">*</span> <span class="n">observer</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">observers</span><span class="p">[</span><span class="n">type</span><span class="p">].</span><span class="n">erase</span><span class="p">(</span><span class="n">observer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">Subject</span><span class="o">::</span><span class="n">notify</span><span class="p">(</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">Event</span><span class="o">&gt;</span> <span class="n">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">obs</span><span class="p">:</span> <span class="n">observers</span><span class="p">[</span><span class="n">event</span><span class="o">-&gt;</span><span class="n">type</span><span class="p">()])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">obs</span><span class="o">-&gt;</span><span class="n">update</span><span class="p">(</span><span class="n">event</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>修改<code>main</code>：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Publisher</span> <span class="n">publisher</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Subscriber</span> <span class="n">sub1</span><span class="p">(</span><span class="s">&#34;Tom&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addObserver</span><span class="p">(</span><span class="n">EventType</span><span class="o">::</span><span class="n">ARTICLE_UPDATED</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">sub1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addArticle</span><span class="p">(</span><span class="s">&#34;Article 1&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">Subscriber</span> <span class="n">sub2</span><span class="p">(</span><span class="s">&#34;Jerry&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addObserver</span><span class="p">(</span><span class="n">EventType</span><span class="o">::</span><span class="n">VIP_ARTICLE_UPDATED</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">sub2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">publisher</span><span class="p">.</span><span class="n">addVipArticleWithReward</span><span class="p">(</span><span class="s">&#34;VIP Article 2&#34;</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>执行结果：</p>
<pre tabindex="0"><code>Tom is reading article : Article 1

Jerry is reading article : VIP Article 2
Jerry earned reward : 5
</code></pre><p>此版本进化到了一个更为特殊的观察者模式———发布订阅者模式，或者叫事件订阅模式。</p>
<h3 id="observer-pattern-vs-publish-subscribe-pattern">Observer Pattern v.s. Publish-Subscribe Pattern</h3>
<p>其主要区别是观察者模式涉及两种角色 <code>Subject</code> 和 <code>Observer</code>，而发布订阅模式中间多了一个<code>Event</code>处理，有的称之为<code>Broker</code>或<code>Channel</code>:</p>
<p><img loading="lazy" src="/images/observer-pattern-v.s.-publish-subscript-pattern.jpeg" alt="Observer Pattern v.s. Publish-Subscribe Pattern"  /></p>
<hr>
<h2 id="完整代码">完整代码</h2>
<ul>
<li><a href="https://github.com/AidySun/aidysun.github.io/tree/main/src/design-pattern-observer.v1.0.cpp">design-pattern-observer v1.0.cpp</a></li>
<li><a href="https://github.com/AidySun/aidysun.github.io/tree/main/src/design-pattern-observer.v2.0.cpp">design-pattern-observer v2.0.cpp</a></li>
<li><a href="https://github.com/AidySun/aidysun.github.io/tree/main/src/design-pattern-observer.v3.0.cpp">design-pattern-observer v3.0.cpp</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Swift: beginAnimations(_:context:) was deprecated in iOS 13.0: Use the block-based animation API instead</title>
      <link>https://blog.tomatostore.top/posts/2023/04/swift-block-based-animation/</link>
      <pubDate>Sun, 02 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/04/swift-block-based-animation/</guid>
      <description>Swift / Objective-C block-based animation</description>
      <content:encoded><![CDATA[<p>用 <code>Xcode 14.2</code> 升级一个 <code>Swift</code> 项目时有编译警告️⚠提示 <code>beginAnimations()</code> 在 <code>iOS 13.0</code> 后已经不推荐使用，应该用 <code>block-based animation</code> 代替。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="hl"><span class="lnt">1
</span></span><span class="hl"><span class="lnt">2
</span></span><span class="hl"><span class="lnt">3
</span></span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line hl"><span class="cl"><span class="n">UIView</span><span class="p">.</span><span class="n">beginAnimations</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span> <span class="c1">// &#39;beginAnimations(_:context:)&#39; was deprecated in iOS 13.0: Use the block-based animation API instead</span>
</span></span><span class="line hl"><span class="cl"><span class="n">UIView</span><span class="p">.</span><span class="n">setAnimationDuration</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span> <span class="c1">// &#39;setAnimationDuration&#39; was deprecated in iOS 13.0: Use the block-based animation API instead</span>
</span></span><span class="line hl"><span class="cl"><span class="n">UIView</span><span class="p">.</span><span class="n">commitAnimations</span><span class="p">()</span> <span class="c1">// &#39;commitAnimations()&#39; was deprecated in iOS 13.0: Use the block-based animation API instead </span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">currentOverlay</span> <span class="p">=</span> <span class="n">overlay</span>
</span></span><span class="line"><span class="cl"><span class="n">currentOverlayTarget</span> <span class="p">=</span> <span class="n">overlayTarget</span>
</span></span><span class="line"><span class="cl"><span class="n">currentLoadingText</span> <span class="p">=</span> <span class="n">loadingText</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>修改为使用 <code>Block-based animation</code> 的代码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">UIView</span><span class="p">.</span><span class="n">animate</span><span class="p">(</span><span class="n">withDuration</span><span class="p">:</span> <span class="mf">0.5</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">currentOverlay</span> <span class="p">=</span> <span class="n">overlay</span>
</span></span><span class="line"><span class="cl">    <span class="n">currentOverlayTarget</span> <span class="p">=</span> <span class="n">overlayTarget</span>
</span></span><span class="line"><span class="cl">    <span class="n">currentLoadingText</span> <span class="p">=</span> <span class="n">loadingText</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>如果用 <code>Objective-C</code> 则修改如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="p">[</span><span class="n">UIView</span> <span class="nl">animateWithDuration</span><span class="p">:</span> <span class="mf">0.5</span>
</span></span><span class="line"><span class="cl">                 <span class="nl">animations</span><span class="p">:</span> <span class="o">^</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">currentOverlay</span> <span class="o">=</span> <span class="n">overlay</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">currentOverlayTarget</span> <span class="o">=</span> <span class="n">overlayTarget</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">currentLoadingText</span> <span class="o">=</span> <span class="n">loadingText</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}];</span>
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    
    <item>
      <title>Markdown 路径中有空格的链接(link)</title>
      <link>https://blog.tomatostore.top/posts/2023/03/markdown-link-to-url-with-space/</link>
      <pubDate>Thu, 30 Mar 2023 21:17:22 +0800</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/03/markdown-link-to-url-with-space/</guid>
      <description>Markdown 中加入超链接指向文件，但文件名中包含空格，怎么办？</description>
      <content:encoded><![CDATA[<h2 id="方法一-将空格替换为20">方法一： 将空格替换为<code>%20</code></h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="k">-</span> [<span class="nt">百度搜索 markdown link space</span>](<span class="na">https://www.baidu.com/s?wd=markdown%20link%20space</span>)
</span></span><span class="line"><span class="cl"><span class="k">-</span> [<span class="nt">父目录中的READ ME.md</span>](<span class="na">../READ%20ME.md</span>) 
</span></span></code></pre></td></tr></table>
</div>
</div><p>效果：</p>
<ul>
<li><a href="https://www.baidu.com/s?wd=markdown%20link%20space">百度搜索 markdown link space</a></li>
<li><a href="../READ%20ME.md">父目录中的READ ME.md</a> <code>:spider:404预警</code></li>
</ul>
<h2 id="方法二-用--把带空格的路径url括起来">方法二： 用 <code>&lt;&gt;</code> 把带空格的路径/URL括起来</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="k">-</span> [<span class="nt">Bing搜索 markdown link with space</span>](<span class="na">&lt;https://cn.bing.com/search?q=markdown link with space&gt;</span>)
</span></span><span class="line"><span class="cl"><span class="k">-</span> [<span class="nt">父目录中的READ ME.md</span>](<span class="na">&lt;../READ ME.md&gt;</span>)
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li><a href="https://cn.bing.com/search?q=markdown%20link%20with%20space">Bing搜索 markdown link with space</a></li>
<li><a href="../READ%20ME.md">父目录中的READ ME.md</a> <code>:lady_beetle:404预警</code></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>hugo 主题本地没问题，github.io中不起作用</title>
      <link>https://blog.tomatostore.top/posts/2023/03/hugo-theme-not-work-online/</link>
      <pubDate>Tue, 28 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/03/hugo-theme-not-work-online/</guid>
      <description>Failed to find a valid digest in the &amp;#39;integrity&amp;#39; attribute for resource &amp;#39;https://.....css&amp;#39; with computed SHA-256 integrity &amp;#39;.....&amp;#39;. The resource has been blocked.</description>
      <content:encoded><![CDATA[<p><strong>根本原因：<code>git crlf</code> 设置导致推送到 <code>github</code> 的文件变动。</strong></p>
<p><code>Hugo</code> 静态博客，使用 <code>PaperMode</code> 主题，本地渲染没问题，将 <code>publish</code> 文件夹 <code>push</code> 到 <code>&lt;username&gt;.github.io</code> 后，通过 Github Page 访问会出现 <code>CSS block</code> 的情况：</p>
<p><img loading="lazy" src="/images/hugo-local-vs-online.png" alt="hugo local v.s. online"  /></p>
<h2 id="尝试过但不起作用的方法">尝试过但不起作用的方法</h2>
<ol>
<li>修改 <code>config.toml</code> 中的 <code>baseURL</code>，用 <code>http</code>、用 <code>https</code>。</li>
<li>根据 <a href="https://github.com/kylethedeveloper/hugo-PaperMod/commit/1b9352398fe91fab207337352befee77152cc544">head, footer: add option to disable fingerprinting adityatelange#89</a>, 在 <code>config.toml</code> 中加入如下内容:</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">assets</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">disableFingerprinting</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="我的解决方法">我的解决方法</h2>
<p>根据 <a href="https://github.com/lxndrblz/anatole/issues/114">Failed to find a valid digest in the integrity attribute</a>，问题可能是 <code>git</code> <code>line breaking</code> 的问题，我使用的是 <code>macOS</code>，当时的 <code>git config -l</code> 中:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">core.autocrlf<span class="o">=</span><span class="nb">true</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在 <code>hugo project repo</code> 和 我的 <code>github.io</code> repo 中 执行</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git config core.autocrlf <span class="nb">false</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>然后生成博客：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">cd</span> blog-hugo
</span></span><span class="line"><span class="cl">hugo -t PaperMod
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># publish is &lt;username&gt;.github.io repo as a submodule</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> publish
</span></span><span class="line"><span class="cl">git add .
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;fix online theme&#34;</span>
</span></span><span class="line"><span class="cl">git push
</span></span></code></pre></td></tr></table>
</div>
</div><p>完美解决：</p>
<p><img loading="lazy" src="/images/hugo-online-fixed.png" alt="fixed online"  /></p>
<h2 id="其它未尝试的方法">其它未尝试的方法</h2>
<ul>
<li>修改模版中的 <code>integrity=&quot;{{ xxxx }}&quot;</code> 为 <code>integrity=&quot;&quot;</code></li>
<li><code>git config --global core.autocrlf input</code></li>
<li><a href="https://swopnil.com/blog/valid-digest-integrity-error-hugo-styling/">CloudFlare Cache</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>能否在 C&#43;&#43; 的构造函数中使用 this 指针</title>
      <link>https://blog.tomatostore.top/posts/2023/03/this-pointer-in-cpp-ctor/</link>
      <pubDate>Mon, 27 Mar 2023 22:07:21 +0800</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/03/this-pointer-in-cpp-ctor/</guid>
      <description>构造函数没执行完，this不可以用？继承多态中父类能否用this指针触发多态调用？</description>
      <content:encoded><![CDATA[<p>直接说结论：<strong>可以在构造函数中使用 this，但是，多态无法生效。</strong></p>
<p>有人认为在构造函数中，对象的构造还没有完成，不能使用 this 指针。
根据<a href="https://isocpp.org/wiki/faq/ctors#using-this-in-ctors">Should you use the this pointer in the constructor?</a>的描述，在构造函数的函数体中使用this指针可以放心的访问父类和自己类中定义的数据成员（data members）。因为那些数据成员在构造函数开始执行时已经构造完成了。</p>
<blockquote>
<p>Here is something that always works: the {body} of a constructor (or a function called from the constructor) can reliably access the data members declared in a base class and/or the data members declared in the constructor’s own class. This is because all those data members are guaranteed to have been fully constructed by the time the constructor’s {body} starts executing.</p></blockquote>
<p>但要<strong>注意</strong>在有继承的情况下，即使是正在构造一个子类实例，在父类的构造阶段，当前的实例还不是子类型。</p>
<blockquote>
<p>The bottom line is this: even if the caller is constructing an object of a derived class, during the constructor of the base class, your object is not yet of that derived class. You have been warned.</p></blockquote>
<p>这也就导致了在父类的构造函数中，this指针是无法触发多态，也就是无法调用到正确的虚函数（virtual functions）。</p>
<blockquote>
<p>Here is something that never works: the {body} of a constructor (or a function called from the constructor) cannot get down to a derived class by calling a virtual member function that is overridden in the derived class. If your goal was to get to the overridden function in the derived class, you won’t get what you want. Note that you won’t get to the override in the derived class independent of how you call the virtual member function: explicitly using the this pointer (e.g., this-&gt;method()), implicitly using the this pointer (e.g., method()), or even calling some other function that calls the virtual member function on your this object.</p></blockquote>
<p>可以用如下的例子类验证一下。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="hl"><span class="lnt">10
</span></span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="hl"><span class="lnt">25
</span></span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="hl"><span class="lnt">57
</span></span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">A</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;A() { </span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line hl"><span class="cl">    <span class="k">this</span><span class="o">-&gt;</span><span class="n">vf</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="o">-&gt;</span><span class="n">f1</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;} </span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">~</span><span class="n">A</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;~A()</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">vf</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;  A&#39;s vf()</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kt">void</span> <span class="nf">f1</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;  A&#39;s f1()</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line hl"><span class="cl">    <span class="n">vf</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">B</span> <span class="o">:</span> <span class="k">public</span> <span class="n">A</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">B</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;B() { </span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="o">-&gt;</span><span class="n">vf</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="o">-&gt;</span><span class="n">f1</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;} </span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="o">~</span><span class="n">B</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;~B()</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">vf</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;  B&#39;s vf()</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kt">void</span> <span class="nf">f1</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;  B&#39;s f1()</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">vf</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;creat A a</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">A</span> <span class="n">a</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;creat B b</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line hl"><span class="cl">  <span class="n">B</span> <span class="n">b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;======</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>执行结果如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="hl"><span class="lnt"> 9
</span></span><span class="lnt">10
</span><span class="hl"><span class="lnt">11
</span></span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">creat A a
</span></span><span class="line"><span class="cl">A<span class="o">()</span> <span class="o">{</span> 
</span></span><span class="line"><span class="cl">  A<span class="s1">&#39;s vf()
</span></span></span><span class="line"><span class="cl"><span class="s1">  A&#39;</span>s f1<span class="o">()</span>
</span></span><span class="line"><span class="cl">  A<span class="s1">&#39;s vf()
</span></span></span><span class="line"><span class="cl"><span class="s1">} 
</span></span></span><span class="line"><span class="cl"><span class="s1">creat B b  // 开始构造 B b;
</span></span></span><span class="line"><span class="cl"><span class="s1">A() { 
</span></span></span><span class="line hl"><span class="cl"><span class="s1">  A&#39;</span>s vf<span class="o">()</span> // 在B的构造过程中，父类A中的this调用的是A的virtual <span class="k">function</span> 
</span></span><span class="line"><span class="cl">  A<span class="s1">&#39;s f1()
</span></span></span><span class="line hl"><span class="cl"><span class="s1">  A&#39;</span>s vf<span class="o">()</span> // 同样，在父类构造函数中间接调用的虚函数，也不会调用子类的虚函数
</span></span><span class="line"><span class="cl"><span class="o">}</span> 
</span></span><span class="line"><span class="cl">B<span class="o">()</span> <span class="o">{</span> 
</span></span><span class="line"><span class="cl">  B<span class="s1">&#39;s vf()
</span></span></span><span class="line"><span class="cl"><span class="s1">  B&#39;</span>s f1<span class="o">()</span>
</span></span><span class="line"><span class="cl">  B<span class="err">&#39;</span>s vf<span class="o">()</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span> 
</span></span><span class="line"><span class="cl"><span class="o">======</span>
</span></span><span class="line"><span class="cl">~B<span class="o">()</span>
</span></span><span class="line"><span class="cl">~A<span class="o">()</span>
</span></span><span class="line"><span class="cl">~A<span class="o">()</span></span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何删除本地 git 中未追踪的文件/文件夹</title>
      <link>https://blog.tomatostore.top/posts/2023/03/git-rm-untracked-file-dir/</link>
      <pubDate>Fri, 24 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.tomatostore.top/posts/2023/03/git-rm-untracked-file-dir/</guid>
      <description>git clean/delete untracked files, folders</description>
      <content:encoded><![CDATA[<p>对于一个 git 项目，执行 <code>git status -u</code> 有如下状态：</p>
<p><img loading="lazy" src="/images/git-status--u.png" alt="git status -u result"  title="hi"  /></p>
<p>对于本地新增的文件，可以通过 <code>clean</code> 命令直接删除：</p>
<p><img loading="lazy" src="/images/git-clean--f.png" alt="git clean -f"  /></p>
<p><img loading="lazy" src="/images/git-status--u-2.png" alt="after clean"  /></p>
<p>但是本地新增的文件夹却没有被删除掉。要删除文件夹需要加上 <code>-d</code> 参数：</p>
<p><img loading="lazy" src="/images/git-clean-fd.png" alt="git clean -f -d"  /></p>
<p>还有一种情况就是已经在 <code>.gitignore</code> 中的文件或文件夹是不会被删除的。</p>
<p><img loading="lazy" src="/images/cat-.gitignore.png" alt="cat .gitignore"  /></p>
<p><img loading="lazy" src="/images/git-status---ignored.png" alt="git status &ndash;ignored"  /></p>
<p>需要通过 <code>-x</code> 选项来删除被 <code>.gitignore</code> 包含的文件或文件夹：</p>
<p><img loading="lazy" src="/images/git-clean--fdx.png" alt="git clean -f -d -x"  /></p>
<h2 id="git-clean-options"><code>git clean</code> options</h2>
<table>
  <thead>
      <tr>
          <th style="text-align: center">Option</th>
          <th>Comments</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: center"><code>-f</code></td>
          <td><em>Delete files or diretories</em></td>
      </tr>
      <tr>
          <td style="text-align: center"><code>-d</code></td>
          <td><em>Recurse into untracked directories</em></td>
      </tr>
      <tr>
          <td style="text-align: center"><code>-x</code></td>
          <td><em>Don’t use the standard ignore rules</em></td>
      </tr>
  </tbody>
</table>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
