<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>JZX 轻语</title>
    <description>挖掘时光的细节</description>
    <link>https://www.jeza.net/</link>
    <atom:link href="https://www.jeza.net/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Fri, 26 Jun 2026 10:59:11 +0000</pubDate>
    <lastBuildDate>Fri, 26 Jun 2026 10:59:11 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>记录一次使用 WinDbg 排查程序退出时 GDI+ 对象内存二次释放问题</title>
        <description>&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;一个单例持有一个 GDI+ 对象的指针。在其生命周期末期（应用程序退出阶段、单例析构过程中）对&lt;strong&gt;该指针执行 delete 时发生 crash&lt;/strong&gt;，报错 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Access violation - code c0000005&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;怀疑是二次释放导致的 crash，但排查后发现该指针&lt;strong&gt;此前并未被显式 delete&lt;/strong&gt;，问题显得很诡异。&lt;/p&gt;

&lt;h2 id=&quot;排查过程&quot;&gt;排查过程&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;首先在VS/WinDbg中对crash现场进行分析：delete 这个指针的时候，&lt;strong&gt;该内存区域确实已经失效&lt;/strong&gt;（全部为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt;）。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/debug-gdi-plus-object-double-free-crash/memory.png&quot; alt=&quot;指针对应内存区域的截图&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;第一次尝试：在 Visual Studio 中对该指针对应的内存地址&lt;strong&gt;设置数据断点&lt;/strong&gt;，但&lt;strong&gt;未能命中&lt;/strong&gt;。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;第二次尝试：使用 WinDbg 调试，输入命令 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ba w4 &amp;lt;指针地址&amp;gt;&lt;/code&gt; 试图对该内存的写操作设置断点，但&lt;strong&gt;仍未命中&lt;/strong&gt;，最终还是触发 crash。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;经过两次尝试，怀疑并非是&lt;strong&gt;自己的代码写坏了内存&lt;/strong&gt;，更可能是&lt;strong&gt;堆内存已被回收&lt;/strong&gt;。（很多堆分配器会在用户可见内存前后放置元数据，并不会真正改写被监控的内存区域，因此数据断点无法命中；但后续执行 delete 时，堆管理器会发现堆的元数据已被破坏，从而触发 crash）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在 VS 中查看 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete&lt;/code&gt; operator 的定义后发现，它并不是 CRT 自带的 operator，而是&lt;strong&gt;由 GDI+ 自行定义的，最终会调用其内部的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GdipFree(void* ptr)&lt;/code&gt; 函数&lt;/strong&gt;。&lt;/p&gt;

    &lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GdiplusBase&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
     &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_pVoid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DllExports&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GdipFree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_pVoid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DllExports&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GdipAlloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_pVoid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;DllExports&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GdipFree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_pVoid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DllExports&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GdipAlloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;第三次尝试：在 WinDbg 中用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x Gdiplus!*&lt;/code&gt; 找到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GdipFree&lt;/code&gt; 的符号，并用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bp&lt;/code&gt; 为其设置断点，但在 crash 之前还是未命中。由此猜测：程序退出前，GDI+ 的某个阶段可能会&lt;strong&gt;统一释放其申请的所有堆内存&lt;/strong&gt;。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;第四次尝试：使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!address &amp;lt;指针值&amp;gt;&lt;/code&gt; 获取对应内存页的信息，并尝试&lt;strong&gt;在 Base Address 上设置数据断点&lt;/strong&gt;。但&lt;strong&gt;仍未命中&lt;/strong&gt;，程序依旧直接 crash；且 crash 后该页已被回收。&lt;/p&gt;

    &lt;p&gt;释放前的内存页信息：&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt; 0:008&amp;gt; !address 20227690810
    
                                         
 Mapping file section regions...
 Mapping module regions...
 Mapping PEB regions...
 Mapping TEB and stack regions...
 Mapping heap regions...
 Mapping page heap regions...
 Mapping other regions...
 Mapping stack trace database regions...
 Mapping activation context regions...
    
 Usage:                  &amp;lt;unknown&amp;gt;
 Base Address:           00000202`27690000
 End Address:            00000202`2769f000
 Region Size:            00000000`0000f000 (  60.000 kB)
 State:                  00001000          MEM_COMMIT
 Protect:                00000004          PAGE_READWRITE
 Type:                   00020000          MEM_PRIVATE
 Allocation Base:        00000202`27690000
 Allocation Protect:     00000004          PAGE_READWRITE
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;释放后的内存页信息：&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt; 0:000&amp;gt; !address 20227690810
    
                                         
 Mapping file section regions...
 Mapping module regions...
 Mapping PEB regions...
 Mapping TEB and stack regions...
 Mapping heap regions...
 Mapping page heap regions...
 Mapping other regions...
 Mapping stack trace database regions...
 Mapping activation context regions...
    
 Usage:                  Free
 Base Address:           00000202`27661000
 End Address:            00000202`276a0000
 Region Size:            00000000`0003f000 ( 252.000 kB)
 State:                  00010000          MEM_FREE
 Protect:                00000001          PAGE_NOACCESS
 Type:                   &amp;lt;info not present at the target&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;第五次尝试：&lt;strong&gt;监控 Win32 系统内部 API &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NtFreeVirtualMemory&lt;/code&gt; 的调用&lt;/strong&gt;；每次命中时打印其参数（Base Address、大小等）。&lt;/p&gt;

    &lt;p&gt;该函数在 x64 下的调用约定为：&lt;/p&gt;

    &lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;NtFreeVirtualMemory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;HANDLE&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;ProcessHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// rcx&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;PVOID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;BaseAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// rdx&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;PSIZE_T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegionSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// r8&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;ULONG&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;FreeType&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;// r9&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;设置一个打印型断点：&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt; bp ntdll!NtFreeVirtualMemory &quot;.if ((@r9d &amp;amp; 0x8000) != 0) {.printf \&quot;MEM_RELEASE: Base=%p Size=%p Ret=%p\n\&quot;, poi(@rdx), poi(@r8), poi(@rsp); kb; gc; } .else { gc }&quot;
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;拿到指针的值后，用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!address&lt;/code&gt; 得到对应的 Base Address，然后退出程序（此时会触发 crash）。观察 Command 面板输出，发现这块内存在退出阶段确实被抢先一步释放了：&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt; MEM_RELEASE: Base=00000223f6b90000 Size=0000000000000000 Ret=00007ffadb260b5f  # RetAddr               : Args to Child                                                           : Call Site
 00 00007ffa`db260b5f     : 00000223`f1080000 00000000`00000000 00000000`00000000 00000223`f1082480 : ntdll!NtFreeVirtualMemory
 01 00007ffa`db26062a     : 00000223`f6b90000 000000be`c394f958 00000000`7ffe0388 00000223`f1080000 : ntdll!RtlpSecMemFreeVirtualMemory+0x2f
 02 00007ffa`db2602e1     : 00000000`00000000 00000223`f6b90000 00000223`f6b90000 00000223`f1080110 : ntdll!RtlpDestroyHeapSegment+0x5e
 03 00007ffa`d8b09f5b     : 00000000`00000000 00000000`00000000 00000000`000c3000 00000223`f88f0000 : ntdll!RtlDestroyHeap+0x101
 04 00007ffa`ae298d17     : 00000000`00000000 00000000`000003b0 00000000`00000000 00000000`00000000 : KERNELBASE!HeapDestroy+0xb
 05 00007ffa`ae2780bb     : 00000000`00000000 000000be`c394fab9 00000000`00000000 00000000`00000000 : gdiplus!InternalGdiplusShutdown+0x60f
 06 00007ff6`9f9e3e5f     : 00000223`f0e8c630 00000000`00000000 00007ff6`9fa0db58 00000000`06cf0000 : gdiplus!GdiplusShutdown+0x9b
 07 00007ff6`9fa07be2     : 00000000`0000000a 00000000`00000000 00000000`00000000 00000000`00000000 : DuilibDemo!wWinMain+0x243 [D:\zoom-src-billable-hours-6x\win-common\test\DuilibDemo\DuilibDemo.cpp @ 126] 
 08 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : DuilibDemo!invoke_main+0x21 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 118] 
 09 00007ffa`d987e957     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : DuilibDemo!__scrt_common_main_seh+0x106 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 
 0a 00007ffa`db2a7c1c     : 00000000`00000000 00000000`00000000 000004f0`fffffb30 000004d0`fffffb30 : KERNEL32!BaseThreadInitThunk+0x17
 0b 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x2c
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;也就是说，&lt;strong&gt;GDI+ 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GdiplusShutdown&lt;/code&gt; 会释放其申请的堆内存&lt;/strong&gt;。此时单例持有的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bitmap&lt;/code&gt;指针所指向的内存区域已经失效，随后再执行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete&lt;/code&gt; 就会触发二次释放，最终导致 crash。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;程序退出阶段的内存释放操作一定要谨慎，尤其是在持有第三方模块对象指针时：其 new/delete operator 可能被重载。&lt;/li&gt;
  &lt;li&gt;如果确实需要持有第三方模块的对象指针，在卸载该模块时务必确认是否还有其他地方持有该模块的对象，尤其要关注对象的内存管理是否（不经意地）被该模块接管。&lt;/li&gt;
  &lt;li&gt;Gdi+ 对象涉及的动态内存由其自行管理，delete 时要特别谨慎，尤其不要在 Gdi+ 模块释放后再 delete 相关对象。因为模块释放时会在内部将其申请的堆内存统一清理掉。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;案例代码&quot;&gt;案例代码&lt;/h2&gt;

&lt;p&gt;由于涉及公司内部代码，这里提供一份同样能够触发 crash 的示例代码（需要事先在 Visual Studio 里创建一个 Windows Desktop Application 项目），程序退出时必然崩溃：&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// vvv&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;objbase.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#define GDIPVER 0x0110
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;gdiplus.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#pragma comment(lib, &quot;gdiplus.lib&quot;)
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;/// ^^^&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;framework.h&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&quot;WindowsProject1.h&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// vvv&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BitmapOwner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;BitmapOwner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Gdiplus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bitmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BitmapOwner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Gdiplus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bitmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitmap_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;BitmapOwner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetBitmapOwner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BitmapOwner&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;/// ^^^&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#define MAX_LOADSTRING 100
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Global Variables:&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;HINSTANCE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hInst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                                &lt;span class=&quot;c1&quot;&gt;// current instance&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WCHAR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;szTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MAX_LOADSTRING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;// The title bar text&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WCHAR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;szWindowClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MAX_LOADSTRING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;// the main window class name&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Forward declarations of functions included in this code module:&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ATOM&lt;/span&gt;                &lt;span class=&quot;nf&quot;&gt;MyRegisterClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HINSTANCE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt;                &lt;span class=&quot;nf&quot;&gt;InitInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HINSTANCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;LRESULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CALLBACK&lt;/span&gt;    &lt;span class=&quot;nf&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UINT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WPARAM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LPARAM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;INT_PTR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CALLBACK&lt;/span&gt;    &lt;span class=&quot;nf&quot;&gt;About&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UINT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WPARAM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LPARAM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;APIENTRY&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;wWinMain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_In_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HINSTANCE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;n&quot;&gt;_In_opt_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HINSTANCE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hPrevInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;n&quot;&gt;_In_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LPWSTR&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;lpCmdLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;n&quot;&gt;_In_&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;nCmdShow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UNREFERENCED_PARAMETER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hPrevInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UNREFERENCED_PARAMETER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lpCmdLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		
		&lt;span class=&quot;c1&quot;&gt;// vvv&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoInitialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ULONG_PTR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gdiToken&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Gdiplus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GdiplusStartupInput&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gdiSI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Gdiplus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GdiplusStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gdiToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gdiSI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;GetBitmapOwner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// will initialize a singleton&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;/// ^^^&lt;/span&gt;
		
    &lt;span class=&quot;c1&quot;&gt;// Initialize global strings&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LoadStringW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDS_APP_TITLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;szTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAX_LOADSTRING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LoadStringW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDC_WINDOWSPROJECT1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;szWindowClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAX_LOADSTRING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MyRegisterClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Perform application initialization:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InitInstance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nCmdShow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;HACCEL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hAccelTable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LoadAccelerators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAKEINTRESOURCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDC_WINDOWSPROJECT1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;MSG&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Main message loop:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TranslateAccelerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hwnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hAccelTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;TranslateMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;DispatchMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		
		&lt;span class=&quot;c1&quot;&gt;/// vvv&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Gdiplus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GdiplusShutdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gdiToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CoUninitialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// ^^^&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ATOM&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MyRegisterClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HINSTANCE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WNDCLASSEXW&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cbSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WNDCLASSEX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CS_HREDRAW&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CS_VREDRAW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lpfnWndProc&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cbClsExtra&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cbWndExtra&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hIcon&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LoadIcon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAKEINTRESOURCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDI_WINDOWSPROJECT1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hCursor&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LoadCursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDC_ARROW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hbrBackground&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HBRUSH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;COLOR_WINDOW&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lpszMenuName&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAKEINTRESOURCEW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDC_WINDOWSPROJECT1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lpszClassName&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;szWindowClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hIconSm&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LoadIcon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAKEINTRESOURCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDI_SMALL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegisterClassExW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wcex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InitInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HINSTANCE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nCmdShow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;hInst&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Store instance handle in our global variable&lt;/span&gt;

   &lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateWindowW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;szWindowClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;szTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WS_OVERLAPPEDWINDOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;CW_USEDEFAULT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CW_USEDEFAULT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;n&quot;&gt;ShowWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nCmdShow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;UpdateWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;LRESULT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CALLBACK&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WndProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WPARAM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LPARAM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_COMMAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wmId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LOWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Parse the menu selections:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wmId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDM_ABOUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;DialogBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hInst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAKEINTRESOURCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDD_ABOUTBOX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;About&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDM_EXIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;DestroyWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;nl&quot;&gt;default:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefWindowProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_PAINT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;PAINTSTRUCT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;BeginPaint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// TODO: Add any drawing code here...&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EndPaint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_DESTROY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PostQuitMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DefWindowProc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hWnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;INT_PTR&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CALLBACK&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;About&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HWND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hDlg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UINT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WPARAM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LPARAM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UNREFERENCED_PARAMETER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_INITDIALOG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INT_PTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WM_COMMAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LOWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDOK&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LOWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDCANCEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;EndDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hDlg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LOWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INT_PTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INT_PTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2026/06/26/Debug-Gdi-Plus-Object-Double-Free-Crash/</link>
        <guid isPermaLink="true">https://www.jeza.net/2026/06/26/Debug-Gdi-Plus-Object-Double-Free-Crash/</guid>
        
        <category>随记</category>
        
        <category>C++</category>
        
        <category>调试</category>
        
        <category>杂七杂八</category>
        
        
      </item>
    
      <item>
        <title>在JetBrains IDE中使用Claude Code集成Claude Code的简单做法 —— 以Windows系统为例</title>
        <description>&lt;p&gt;这两年AI的浪潮愈发汹涌，给传统的IDE带来了不少的挑战。一直被诟病落后于AI时代的JetBrains虽然也积极地集成Copilot，推出自家的AI工具（Junie），但还是抵挡不住Cursor、Claude Code的强烈冲击。大家的开发体验从基于IDE的编码，逐步演变成通过CLI的方式进行Vibe Coding。但从我的角度出发，开发始终离不开IDE，代码浏览、跳转、重构、调试、版本管理等能力，仍然是成熟IDE的核心优势。如果能将Claude Code这些AI Agent集成在IDE中，相信还是能做到两全其美的效果。&lt;/p&gt;

&lt;p&gt;最近，&lt;a href=&quot;https://www.jetbrains.com/acp/&quot;&gt;JetBrains也努力地将ACP继承在自家的产品中&lt;/a&gt;，有效地改善了在PyCharm、IntelliJ IDEA等IDE中的Vibe Coding体验。其ACP Registry也提供了多个AI Agent的集成，里面也包含了Claude Agent —— 但是，这个Claude Agent并非用的是我们本地Claude Code的登陆状态或订阅！它实际上消耗的是JetBrains自家AI的Credit，因此不到几次问答后就会触及额度上限。&lt;/p&gt;

&lt;p&gt;那么，我们如何将Anthropic订阅的本地Claude Code集成在JetBrains IDE中呢？一个比较简单的做法是使用第三方ACP工具 &lt;a href=&quot;https://github.com/agentclientprotocol/claude-agent-acp&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claude-agent-acp&lt;/code&gt;&lt;/a&gt;，将Claude Code通过ACP协议接入到JetBrains的AI Chat面板。&lt;/p&gt;

&lt;h2 id=&quot;环境概述&quot;&gt;环境概述&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Windows操作系统（Windows 11）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;本地已安装好Claude Code，并且已登录&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;支持ACP协议的JetBrains IDE，至少需要2025.3.x版本，推荐使用2026.1及以上版本&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;已安装Node.js及npm&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;过程概述&quot;&gt;过程概述&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;打开PowerShell命令行。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;键入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install -g @agentclientprotocol/claude-agent-acp&lt;/code&gt;命令，全局安装&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claude-agent-acp&lt;/code&gt;。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;安装完毕后，可以先执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claude-agent-acp&lt;/code&gt;命令，确认&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claude-agent-acp&lt;/code&gt;是否可以正常运行。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;接着执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Get-Command claude-agent-acp&lt;/code&gt;，查看&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claude-agent-acp&lt;/code&gt;的完整路径，此时会有类似以下格式的输出，记住&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Source&lt;/code&gt;列里的路径。&lt;/p&gt;

    &lt;pre&gt;
 CommandType     Name                                               Version    Source
 -----------     ----                                               -------    ------
 ExternalScript  claude-agent-acp.ps1                                          C:\Users\Jeza\AppData\Roaming\npm\claude-agent-acp.ps1&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;需要特别注意的是：如果这里得到的是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ps1&lt;/code&gt;文件路径，那么在后续JetBrains的ACP配置中，需要将扩展名从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ps1&lt;/code&gt; 改成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cmd&lt;/code&gt;。比如上面的路径&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Users\Jeza\AppData\Roaming\npm\claude-agent-acp.ps1&lt;/code&gt;，在后续的配置中，需要改成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Users\Jeza\AppData\Roaming\npm\claude-agent-acp.cmd&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;打开JetBrains IDE（如PyCharm），在右侧的AI Chat面板上，点击&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Options - Add Custom Agent&lt;/code&gt;，此时IDE会打开一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acp.json&lt;/code&gt;配置文件。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/use-claude-code-in-jetbrains-ides/add-custom-agent.png&quot; alt=&quot;点击`Options - Add Custom Agent`，修改ACP配置&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acp.json&lt;/code&gt;里的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;agent_servers&lt;/code&gt;中新增一个agent，如：&lt;/p&gt;

    &lt;pre&gt;
 {
     &quot;default_mcp_settings&quot;:{},
     &quot;agent_servers&quot;: {
         &quot;claude-code-acp&quot;: {
             &quot;command&quot;: &quot;C:\\Users\\Jeza\\AppData\\Roaming\\npm\\claude-agent-acp.cmd&quot;
         }
     }
 }&lt;/pre&gt;

    &lt;p&gt;再次强调，这里的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;command&lt;/code&gt;填写以&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cmd&lt;/code&gt;结尾的绝对路径，而不是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ps1&lt;/code&gt;路径。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;按 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl + S&lt;/code&gt;保存配置，保存后，回到AI Chat面板查看下方的Agents列表。如果一切正常，里面应该会出现一个名为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claude-code-acp&lt;/code&gt;的agent。切换到该agent，如果右侧出现类似”Mode: Default”、”Mode: Opus (1M context)”的模型或模式选项，通常说明配置已经基本成功。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;最后，尝试在AI Chat中发送一条消息，确认Claude Code是否能够正常响应。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;已知问题&quot;&gt;已知问题&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;目前在JetBrains IDE 2026.1.x版本中，这种方式还不是特别稳定。遇到过AI Chat一直卡在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Working&lt;/code&gt; 状态的问题，而且此时无法正常停止，只能通过重启IDE来恢复。&lt;/li&gt;
&lt;/ul&gt;

&lt;style&gt;
.highlight .lineno {
    display: none;
}
&lt;/style&gt;

</description>
        <pubDate>Fri, 08 May 2026 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2026/05/08/Use-Claude-Code-ACP-in-Jetbrains-IDEs/</link>
        <guid isPermaLink="true">https://www.jeza.net/2026/05/08/Use-Claude-Code-ACP-in-Jetbrains-IDEs/</guid>
        
        <category>AI</category>
        
        <category>杂七杂八</category>
        
        
      </item>
    
      <item>
        <title>Git连接提示&quot;Connection closed by xxx&quot;的解决方法</title>
        <description>&lt;p&gt;今天给自己的Ubuntu系统配置git的时候，发现没法连接Github，clone/fetch/pull此类涉及网络连接的操作都会出错，并提示”Connection closed by 20.205.243.166 port 22”。一开始以为是SSH密钥或者路由器DNS有问题，但重置了一遍后还是报错。经过查阅资料发现，可能是因为网络中的某一环（防火墙/代理服务器）屏蔽掉了22端口导致的。我们可以键入以下命令尝试是否可以通过443端口SSH连接到Github上：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;ssh &lt;span class=&quot;nt&quot;&gt;-T&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 443 git@ssh.github.com&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;如果提示如下信息，说明可以通过443端口连接到Github：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;Hi {Your Github Username}! You&apos;ve successfully authenticated, but GitHub does not provide shell access.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;此时我们可以曲线救国，配置git使用443端口进行SSH连接。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;具体方法是编辑&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ssh/config&lt;/code&gt;文件（如果没有则新建一个），添加如下内容：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;[url &quot;ssh://git@ssh.github.com:443/&quot;]
insteadOf = git@github.com:&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;上述配置新增一个&lt;strong&gt;路径重写（URL Rewriting）&lt;/strong&gt;规则：每当执行Git操作（如clone、push、pull）且目标地址以“&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git@github.com:&lt;/code&gt;”开头时（通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;insteadOf&lt;/code&gt;字段指定），Git会自动将其替换为“&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh://git@ssh.github.com:443/&lt;/code&gt;”。比如，当你执行命令：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;git clone git@github.com:owner/repo.git&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Git会转换为：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;git clone ssh://git@ssh.github.com:443/owner/repo.git&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;这样就能通过443端口与Github建立SSH连接，绕过22端口被屏蔽的问题。&lt;/p&gt;

&lt;h2 id=&quot;参考资料&quot;&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/orgs/community/discussions/54558&quot;&gt;Unable to use github ssh for anything (pull/clone/push etc) #54558&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://docs.github.com/en/authentication/troubleshooting-ssh/using-ssh-over-the-https-port&quot;&gt;Using SSH over the HTTPS port&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 09 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2026/01/09/Git-Connection-Closed/</link>
        <guid isPermaLink="true">https://www.jeza.net/2026/01/09/Git-Connection-Closed/</guid>
        
        <category>Git</category>
        
        <category>杂七杂八</category>
        
        
      </item>
    
      <item>
        <title>跨仓库应用Git提交的方法</title>
        <description>&lt;p&gt;最近在处理公司项目时，遇到需要将一个Git仓库中的某些提交应用到另一个仓库中的情况。由于是两个分离的仓库，直接使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git cherry-pick&lt;/code&gt;命令并不可行。经过一番研究，发现可以通过导出补丁文件并应用到目标仓库来实现这一需求。本文分享具体的操作步骤和注意事项。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;太长不看&quot;&gt;太长不看&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;在源仓库中，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git format-patch&lt;/code&gt;命令将所需要的提交导出成补丁文件。如果需要将多个提交合并成一个补丁文件，可以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git format-patch -&amp;lt;n&amp;gt; HEAD --stdout &amp;gt; &amp;lt;patch-file&amp;gt;&lt;/code&gt;命令。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在目标仓库中，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;或者&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git am&lt;/code&gt;命令将补丁文件应用到目标仓库。如果两个仓库的目录结构不一样，可使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&amp;lt;n&amp;gt;&lt;/code&gt;选项和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--directory&lt;/code&gt;选项来调整路径。比如，将仓库A中关于文件&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/path/main.cpp&lt;/code&gt;的修改应用到仓库B中对应的文件&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Source/path/main.cpp&lt;/code&gt;，可以使用如下命令：&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git apply &amp;lt;patch-file&amp;gt; &lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-p2&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# remove 2 leading directory (&quot;a/src/&quot;) from the path in the patch&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Source&quot;&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# prepend &quot;Source/&quot; to the path&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;适用场景&quot;&gt;适用场景&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;两个仓库A, B中具有相同名字的文件，且文件的内容大致相同，希望能将仓库A中对此文件的修改”cherry-pick”到仓库B中（当然，cherry-pick只能用于同一仓库的不同分支中，并不支持跨仓库提交）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;两个仓库具有几乎一致的源码，希望能将仓库A的某个commit应用在仓库B中。这种情况一般发生在工程结构的重构，比如将一个仓库内的某个目录抽出来，单独作为一个新的仓库来维护。但前面的仓库因为某种历史原因，该目录仍未删除，形成了同一份代码（或工程），两个仓库并行维护的状态。因此在开发的过程中，需要往两个仓库提交几乎一致的代码。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;具体过程&quot;&gt;具体过程&lt;/h2&gt;

&lt;h3 id=&quot;示例仓库结构&quot;&gt;示例仓库结构&lt;/h3&gt;

&lt;p&gt;以&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepoSample&quot;&gt;一个示例仓库&lt;/a&gt;为例，我们先用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone --recursive https://github.com/JezaChen/ApplyPatchAcrossRepoSample&lt;/code&gt;将其clone到本地。里面有两个子仓库”repoA”以及”repoB”，其中repoA的目录结构如下：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;repoA/
  ├── src/
      └── path
            └── main.cpp&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;repoB的目录结构如下：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;repoB/
  ├── src/
      └── Source
            └── main.cpp&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;而&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoA/blob/main/src/path/main.cpp&quot;&gt;repoA里面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;&lt;/a&gt;和&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoB/blob/main/Source/path/main.cpp&quot;&gt;repoB里面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;&lt;/a&gt;内容大致相同。除了第8行所传入的参数不一样，其他内容是相同的。&lt;/p&gt;

&lt;h3 id=&quot;输出补丁&quot;&gt;输出补丁&lt;/h3&gt;

&lt;p&gt;我们对repoA的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;进行改动，切去&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoA/tree/dev&quot;&gt;新的分支&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev&lt;/code&gt;&lt;/a&gt;，在&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoA/commit/da93915&quot;&gt;第一个提交（da93915）&lt;/a&gt;中，将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add&lt;/code&gt;函数修改成&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The sum is: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;然后，在&lt;a href=&quot;https://github.com/JezaChen/ApplyPatchAcrossRepo-RepoA/commit/7669cbd&quot;&gt;第二个提交（7669cbd）&lt;/a&gt;中，给&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add&lt;/code&gt;函数新增一行注释&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// This function adds two integers and returns the result&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;The sum is: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;如果我们想将这两个提交应用到repoB中，要怎么做呢？最直观的想法是使用cherry-pick，但cherry-pick只能用于同一仓库的不同分支中，并不支持跨仓库提交。这时候，git的补丁机制就派上用场了，我们可以将这两个提交导出成补丁文件，然后再将补丁文件应用到repoB中。&lt;/p&gt;

&lt;p&gt;使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git format-patch&lt;/code&gt;命令导出补丁文件。在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev&lt;/code&gt;分支上，使用以下命令导出最近两次的提交：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git format-patch &lt;span class=&quot;nt&quot;&gt;-2&lt;/span&gt; HEAD&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;不幸的是，执行之后，会发现这个命令生成了两个补丁文件——能否将其合并成一个呢？当然可以！我们可以通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--stdout&lt;/code&gt;选项将其打印到终端上，然后重定向一个文件里（在例中，我们命名为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my.patch&lt;/code&gt;）：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git format-patch &lt;span class=&quot;nt&quot;&gt;-2&lt;/span&gt; HEAD &lt;span class=&quot;nt&quot;&gt;--stdout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; my.patch&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;最后，我们将所输出的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my.patch&lt;/code&gt;文件移动到repoB里。&lt;/p&gt;

&lt;h3 id=&quot;应用补丁&quot;&gt;应用补丁&lt;/h3&gt;

&lt;p&gt;补丁有了，怎么应用到目标仓库（repoB）里面呢？我们不难想到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;命令，但直接应用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply my.patch&lt;/code&gt;命令会报错，毕竟两个仓库的目录结构是有区别的。&lt;/p&gt;

&lt;p&gt;还好，Git提供了灵活的选项——我们可以利用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&amp;lt;n&amp;gt;&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--directory&lt;/code&gt;来fix上述的问题：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git apply my.patch &lt;span class=&quot;nt&quot;&gt;-p2&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Source&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;其中，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&amp;lt;n&amp;gt;&lt;/code&gt;选项用于去掉补丁文件中路径的前&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt;个目录层级，在本例中，补丁文件中的路径是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a/src/path/main.cpp&lt;/code&gt;（注意补丁文件里面有个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a/&lt;/code&gt;），我们需要去掉前两个目录层级&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a/&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/&lt;/code&gt;，使其变成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;path/main.cpp&lt;/code&gt;；&lt;/p&gt;

&lt;p&gt;而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--directory=&quot;Source&quot;&lt;/code&gt;选项则是告诉&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;命令，将补丁应用到当前目录下的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Source&lt;/code&gt;目录中。最终，我们应用补丁的路径就是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Source/path/main.cpp&lt;/code&gt;，正好对应上了repoB中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;命令执行完毕后，我们可以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt;命令查看repoB的状态，发现&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.cpp&lt;/code&gt;文件已经被修改了，且改动的内容是一致的！&lt;/p&gt;

&lt;p&gt;除了上述的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;命令外，我们也可以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git am&lt;/code&gt;命令来应用补丁文件，命令如下，可以发现与&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;命令类似：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git am my.patch &lt;span class=&quot;nt&quot;&gt;-p2&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Source&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;两者的区别在于，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git am&lt;/code&gt;应用补丁后会自动帮你提交，甚至能保留源仓库中的提交信息（比如作者、提交时间、提交说明等）；而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git apply&lt;/code&gt;则只会应用补丁的内容，不会保留原有的提交信息。&lt;/strong&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 05 Nov 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/11/05/Git-Apply-Patch-Across-Repos/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/11/05/Git-Apply-Patch-Across-Repos/</guid>
        
        <category>Git</category>
        
        <category>杂七杂八</category>
        
        
      </item>
    
      <item>
        <title>contextlib.contextmanager源码分析</title>
        <description>&lt;p&gt;之前一直想写&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;的实现原理，但也一直都没时间，今天看了&lt;a href=&quot;https://realpython.com/python-with-statement/&quot;&gt;Real Python关于上下文管理器的文章&lt;/a&gt;，打算补一下之前的坑。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;预备知识&quot;&gt;预备知识&lt;/h2&gt;

&lt;p&gt;Python的语法糖很多，而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;主要涉及了三个比较常用的语法糖：&lt;em&gt;装饰器&lt;/em&gt;、&lt;em&gt;生成器&lt;/em&gt;和&lt;em&gt;上下文管理器&lt;/em&gt;（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句）。&lt;/p&gt;

&lt;h3 id=&quot;装饰器&quot;&gt;装饰器&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;装饰器&lt;/em&gt;本质上原理很简单：它无非就是起到一种“锦上添花”的功能，在原有的函数上补充功能。被包装的函数以参数的形式传入到&lt;em&gt;装饰器&lt;/em&gt;。也就是&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@decorator&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;本质上等价于&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;decorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;生成器&quot;&gt;生成器&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;生成器&lt;/em&gt;则可以看成能&lt;strong&gt;中途停下来（暂停的时候还能通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;往外部抛出值），等待下一步指令并继续执行&lt;/strong&gt;（外部可调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt;方法传进指令，也可以直接通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next(...)&lt;/code&gt;让它继续）的函数。这里不再赘述，直接来个简单的例子：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gen(): Start...&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;first_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;First&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gen(): &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;second_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gen(): &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;second_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__main__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;recv1: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;recv2: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Command1&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;recv3: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Command2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This will raise StopIteration
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可以看到，我们首先调用&lt;em&gt;生成器函数&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gen&lt;/code&gt;得到一个&lt;em&gt;生成器迭代器&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g&lt;/code&gt;，然后使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next(g)&lt;/code&gt;激活&lt;em&gt;生成器迭代器&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g&lt;/code&gt;（此时会输出&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;gen(): Start...&quot;&lt;/code&gt;），激活后，会返回一个字符串&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;First&quot;&lt;/code&gt;。然后，我们使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g.send(&apos;Command1&apos;)&lt;/code&gt;发送字符串&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;Command1&apos;&lt;/code&gt;到&lt;em&gt;生成器迭代器&lt;/em&gt;中，此时&lt;em&gt;生成器迭代器&lt;/em&gt;内的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_command = yield &quot;First&quot;&lt;/code&gt;收到外部传入的参数并赋值&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_command&lt;/code&gt;，也就是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_command=&apos;Command1&apos;&lt;/code&gt;，并继续执行下去。当&lt;em&gt;生成器迭代器&lt;/em&gt;到达终点时，其会抛出一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StopIteration&lt;/code&gt;异常告知外部已经。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;需要注意的是，如果外部不使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt;发送参数而是直接调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next&lt;/code&gt;，则&lt;em&gt;生成器迭代器&lt;/em&gt;内通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;表达式所收到的值为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里厘清一下生成器相关的术语：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/zh-cn/3.13/glossary.html#term-generator&quot;&gt;&lt;em&gt;生成器函数&lt;/em&gt;&lt;/a&gt;（generator function，大多数场合下，&lt;em&gt;生成器&lt;/em&gt;（generator）指的是&lt;em&gt;生成器函数&lt;/em&gt;）：函数体内包含&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;语句的函数，调用它会返回一个&lt;em&gt;生成器迭代器&lt;/em&gt;。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.python.org/zh-cn/3.13/glossary.html#term-generator&quot;&gt;&lt;em&gt;生成器迭代器&lt;/em&gt;&lt;/a&gt;（generator iterator）：&lt;em&gt;生成器函数&lt;/em&gt;所创建的对象，其实现了迭代器协议（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__iter__&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__next__&lt;/code&gt;方法），具有&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;throw&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;close&lt;/code&gt;等方法。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一般来说，&lt;em&gt;生成器&lt;/em&gt;指的是&lt;em&gt;生成器函数&lt;/em&gt;，但有时候也会指&lt;em&gt;生成器迭代器&lt;/em&gt;，具体要看上下文。&lt;/p&gt;

&lt;h3 id=&quot;上下文管理器&quot;&gt;上下文管理器&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;上下文管理器&lt;/em&gt;则是实现了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;这两个魔术方法的类，使用时，将该类的&lt;strong&gt;实例&lt;/strong&gt;传入到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句即可。&lt;/p&gt;

&lt;p&gt;我们可以简单实现一个&lt;em&gt;上下文管理器&lt;/em&gt;，其会在进出上下文的时候输出一些信息，并且捕获&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句体内的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZeroDivisionError&lt;/code&gt;异常。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Entering the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Exiting the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Handled ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Suppress the exception
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Propagate other exceptions&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;我们可以写个简单的使用例子：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__main__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;OK, no exception&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This will raise ZeroDivisionError
&lt;/span&gt;    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Go ahead, no exception propagated&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;This line will not be printed due to IndexError&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;运行程序，可以看到无论是否有异常，&lt;em&gt;上下文管理器&lt;/em&gt;都能执行到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;方法，从而确保其所托管的资源能释放。当然，如果异常继续传播，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句之后的代码也不会执行了（除非遇到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finally&lt;/code&gt;语句）。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;Entering the context
s = &apos;Hello&apos;
Exiting the context
exc_type  = None
exc_value = None
traceback = None
OK, no exception

Entering the context
Exiting the context
exc_type  = &amp;lt;class &apos;ZeroDivisionError&apos;&amp;gt;
exc_value = ZeroDivisionError(&apos;division by zero&apos;)
traceback = &amp;lt;traceback object at 0x000001AA8C7CFA40&amp;gt;
Handled ZeroDivisionError
Go ahead, no exception propagated

Entering the context
Exiting the context
exc_type  = &amp;lt;class &apos;IndexError&apos;&amp;gt;
exc_value = IndexError(&apos;list assignment index out of range&apos;)
traceback = &amp;lt;traceback object at 0x000001AA8C7CF9C0&amp;gt;
python-BaseException
Traceback (most recent call last):
  File &quot;C:\Users\Jeza\PyQtEx\test1.py&quot;, line 43, in &amp;lt;module&amp;gt;
    a[1] = -1
    ~^^^
IndexError: list assignment index out of range&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;contextmanager的简单用法&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;的简单用法&lt;/h2&gt;

&lt;p&gt;主角终于登场，现在我们使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;对上文的&lt;em&gt;上下文管理器类&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SimpleCtxManager&lt;/code&gt;做个等价的改造：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@contextlib.contextmanager&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Entering the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;exc_type = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;exc_value = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;traceback = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__traceback__&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Handled ZeroDivisionError:&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Exiting the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可以看到，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;是一个&lt;em&gt;装饰器&lt;/em&gt;，其所包装的是一个&lt;em&gt;生成器函数&lt;/em&gt;，其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;语句前面的代码可以看成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;的逻辑，而后面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;except…finally…&lt;/code&gt;语句则可以看成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;的逻辑（处理异常/清理资源）。对比发现，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt; 不仅能让代码易读（看起来更流程化），而且不用专门写一个类，仅需一个&lt;em&gt;生成器函数&lt;/em&gt;即可做到一样的功能。&lt;/p&gt;

&lt;h2 id=&quot;contextmanager的源码分析&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;的源码分析&lt;/h2&gt;

&lt;p&gt;OK，终于讲到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;的原理了。我们可以在Python官方标准库中找到&lt;a href=&quot;https://github.com/python/cpython/blob/main/Lib/contextlib.py&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.py&lt;/code&gt;的源代码&lt;/a&gt;，定位到&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L272&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;的实现&lt;/a&gt;：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;contextmanager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@wraps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;helper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_GeneratorContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;helper&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;由代码看出，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;是一个&lt;em&gt;装饰器&lt;/em&gt;，其接受一个函数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func&lt;/code&gt;作为参数，在闭包&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helper&lt;/code&gt;内用它来继续构造&lt;em&gt;上下文管理器&lt;/em&gt;类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManager&lt;/code&gt;的实例并返回。我们将&lt;em&gt;装饰器&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;所返回的闭包&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helper&lt;/code&gt;视作为一个&lt;em&gt;上下文管理器&lt;/em&gt;工厂（在外部看起来，它其实用法和&lt;em&gt;上下文管理器&lt;/em&gt;是一样的），其接受任意的参数，负责在使用处构造出真正的&lt;em&gt;上下文管理器&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManager&lt;/code&gt;的实例。&lt;/p&gt;

&lt;h3 id=&quot;三个基类的分析&quot;&gt;三个基类的分析&lt;/h3&gt;

&lt;p&gt;我们继续研究&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L125&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManager&lt;/code&gt;类的实现&lt;/a&gt;，首先看看它的基类：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;_GeneratorContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_GeneratorContextManagerBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ContextDecorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可以看到其继承了三个基类，我们首先看第一个基类&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L101&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManagerBase&lt;/code&gt;的实现&lt;/a&gt;，这也是所有基于&lt;em&gt;生成器&lt;/em&gt;的&lt;em&gt;上下文管理器&lt;/em&gt;的公共基类：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;_GeneratorContextManagerBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Shared functionality for @contextmanager and @asynccontextmanager.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Issue 19330: ensure context manager instances have good docstrings
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__doc__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__doc__&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__doc__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Unfortunately, this still doesn&apos;t provide good help output when
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# inspecting the created context manager instances, since pydoc
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# currently bypasses the instance docstring and shows the docstring
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# for the class instead.
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# See http://bugs.python.org/issue19404 for more details.
&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_recreate_cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# _GCMB instances are one-shot context managers, so the
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# CM must be recreated each time a decorated function is
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# called
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;__class__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;不难分析，这个基类所实现的逻辑比较简单：保存&lt;em&gt;生成器函数&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func&lt;/code&gt;以及调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func&lt;/code&gt;所需的参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;args&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kwds&lt;/code&gt; 、将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;func&lt;/code&gt;的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__doc__&lt;/code&gt;属性复制到&lt;em&gt;生成器&lt;/em&gt;里面去，以及提供一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_recreate_cm&lt;/code&gt;方法，以用来&lt;strong&gt;重新创建一个新的&lt;em&gt;上下文管理器&lt;/em&gt;实例&lt;/strong&gt;，保证&lt;strong&gt;该实例作为&lt;em&gt;装饰器&lt;/em&gt;使用&lt;/strong&gt;时，在每一次函数调用都能秽土重生，&lt;strong&gt;重新生成一个全新的实例&lt;/strong&gt;用来执行上下文——因为&lt;em&gt;生成器迭代器&lt;/em&gt;是一次性的，一旦激活并&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next&lt;/code&gt;下去，就没有回头路了。需要回头？只能重来。（让&lt;em&gt;生成器函数&lt;/em&gt;再生一个出来）&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;再次强调下，这里的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_recreate_cm&lt;/code&gt;方法仅用于该上下文管理器实例作为装饰器使用的场合。其覆写了第三个基类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContextDecorator&lt;/code&gt;的同名方法，后面会讲到。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;这个上下文管理器的实例如何作为装饰器使用呢？后面第三个基类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContextDecorator&lt;/code&gt;的分析会有例子。这两个基类的关系很密切。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;OK，我们来看第二个基类&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L17&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbstractContextManager&lt;/code&gt;的实现&lt;/a&gt;：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ABC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;An abstract base class for context managers.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;__class_getitem__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;classmethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenericAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Return `self` upon entering the runtime context.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@abc.abstractmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Raise any exception triggered within the runtime context.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@classmethod&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__subclasshook__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_collections_abc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_check_methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NotImplemented&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;这个没什么好说的，其就是所有&lt;em&gt;上下文管理器&lt;/em&gt;的&lt;em&gt;基类&lt;/em&gt;。当然，由于Python是鸭子类型语言，并不强制&lt;em&gt;上下文管理器&lt;/em&gt;的实现必须继承这个基类，这个基类只是用来&lt;strong&gt;加强类型检查&lt;/strong&gt;，避免&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;或&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;方法没有实现（即上述的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__subclasshook__&lt;/code&gt;所做的）。比如在下面的例子中，我们定义&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WrongCtxManager&lt;/code&gt;类的时候，犯了个很致命的错误——只实现了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;却漏了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;——还好！由于这个类继承了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AbstractContextManager&lt;/code&gt;，在实例化的时候Python解释器会“贴心”地抛出异常，告知你没有实现完整的上下文管理协议。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contextlib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WrongCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Entering the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WrongCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# error!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;del&gt;可见当鹅比当鸭好！&lt;/del&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我们来看第三个基类&lt;a href=&quot;https://github.com/python/cpython/blob/3.11/Lib/contextlib.py#L62&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContextDecorator&lt;/code&gt;&lt;/a&gt;：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ContextDecorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;A base class or mixin that enables context managers to work as decorators.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_recreate_cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Return a recreated instance of self.

        Allows an otherwise one-shot context manager like
        _GeneratorContextManager to support use as
        a decorator via implicit recreation.

        This is a private interface just for _GeneratorContextManager.
        See issue #11647 for details.
        &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__call__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@wraps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;inner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_recreate_cm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inner&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;这个基类更像是一个mixin，为子类实现了一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__call__&lt;/code&gt;魔术方法，使得&lt;strong&gt;上下文管理器实例可作为&lt;em&gt;装饰器&lt;/em&gt;使用&lt;/strong&gt;。在被包装函数调用的前后，执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;方法。关于&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_recreate_cm&lt;/code&gt;，前面已经提前路透了，这里不再多说了。&lt;/p&gt;

&lt;p&gt;以前面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SimpleCtxManager&lt;/code&gt;为例，我们为它加上父类&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContextDecorator&lt;/code&gt;，此时&lt;strong&gt;其实例&lt;/strong&gt;就可以作为&lt;em&gt;装饰器&lt;/em&gt;来用了。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contextlib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContextDecorator&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContextDecorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Inherit from ContextDecorator
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Entering the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Exiting the context&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_type&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exc_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Handled ZeroDivisionError&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Suppress the exception
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Propagate other exceptions
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleCtxManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@cm&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Use the instance as a decorator
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;func...&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; 

&lt;span class=&quot;c1&quot;&gt;# Outputs:
# Entering the context
# func...
# Exiting the context
# exc_type  = None
# exc_value = None
# traceback = None&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;回到contextlibcontextmanager&quot;&gt;回到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;好了，分析完三个基类后，我们终于轮到分析主角&lt;a href=&quot;https://github.com/python/cpython/blob/main/Lib/contextlib.py#L129&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;&lt;/a&gt;了，可以看到，通过继承上述的三个基类后，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextlib.contextmanager&lt;/code&gt;只需将精力花在最核心的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;两个方法：&lt;/p&gt;

&lt;p&gt;&lt;em&gt;（为减少篇幅，下面的源码去掉了注释）&lt;/em&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;_GeneratorContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_GeneratorContextManagerBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AbstractContextManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ContextDecorator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__enter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;del&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RuntimeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;generator didn&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;t yield&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__exit__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RuntimeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;generator didn&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;t stop&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;typ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RuntimeError&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__traceback__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__cause__&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__traceback__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;BaseException&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;exc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__traceback__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceback&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RuntimeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;generator didn&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;t stop after throw()&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可以看到，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_GeneratorContextManager&lt;/code&gt;是幕后真正起作用的&lt;em&gt;上下文管理器&lt;/em&gt;。我们抛开一些细节性的校验逻辑，分析下&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;的主要思路：使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next(self.gen)&lt;/code&gt;激活&lt;em&gt;生成器迭代器&lt;/em&gt;（也就是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;包裹的&lt;em&gt;生成器函数&lt;/em&gt;所返回的&lt;em&gt;生成器迭代器&lt;/em&gt;），使其走到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;语句处停下——这也意味着，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;语句&lt;strong&gt;前面所执行的代码&lt;/strong&gt;拿来充当&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__enter__&lt;/code&gt;的逻辑了！&lt;/p&gt;

&lt;p&gt;我们再看&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;的实现思路，分两种情况：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句块&lt;strong&gt;没有抛出异常&lt;/strong&gt;：使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next(self.gen)&lt;/code&gt;恢复&lt;em&gt;生成器迭代器&lt;/em&gt;，并&lt;strong&gt;期望它能执行到最后（包括走完finally语句块）&lt;/strong&gt;。如果发现&lt;em&gt;生成器迭代器&lt;/em&gt;暂停了（说明又遇到了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;），则这个&lt;em&gt;生成器函数&lt;/em&gt;的实现不符合&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contextmanager&lt;/code&gt;的规范，抛出&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RuntimeError&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;如果&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt;语句块&lt;strong&gt;抛出了异常&lt;/strong&gt;：将异常转发到&lt;em&gt;生成器迭代器&lt;/em&gt;的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt; 表达式处（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self.gen.throw(typ, value, traceback)&lt;/code&gt;），给它一个机会做清理或选择抑制异常，然后再根据它所抛出的异常再做处理——
    &lt;ul&gt;
      &lt;li&gt;如果抛出的是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StopIteration&lt;/code&gt;&lt;strong&gt;且其异常实例不同于所传入的异常实例&lt;/strong&gt;（确保了这个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StopIteration&lt;/code&gt;是&lt;em&gt;生成器迭代器&lt;/em&gt;&lt;strong&gt;自己执行完毕&lt;/strong&gt;所抛出的，而非重新&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raise&lt;/code&gt;了所传入的异常（尤其这个所传的异常也是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StopIteration&lt;/code&gt;）），意味着它选择了抑制异常，此时&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;返回&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt;；&lt;/li&gt;
      &lt;li&gt;如果抛出的是其他异常，则说明&lt;em&gt;生成器&lt;/em&gt;选择了不抑制异常，此时&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__exit__&lt;/code&gt;返回&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;False&lt;/code&gt;，让异常继续传播。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;里面还有一些细节也挺有意思的，这里就先不讲了，有空的话再原地补坑。先这样，睡觉~&lt;/p&gt;

</description>
        <pubDate>Wed, 10 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/09/10/Contextlib-ContextManager-Source/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/09/10/Contextlib-ContextManager-Source/</guid>
        
        <category>Python</category>
        
        <category>Python进阶</category>
        
        <category>上下文管理器</category>
        
        
      </item>
    
      <item>
        <title>穷哥们的电子书及笔记多平台同步免费方案</title>
        <description>&lt;p&gt;随着电子书资源如雨后春笋般地涌现在互联网，很多人都从传统的纸质书阅读转移到电子书上。对于一些专业相关的书籍，电子书往往都是PDF格式，且需要做大量的笔记。我们往往希望这些笔记能够多端同步，可随时随地地翻阅以前的读书心得。而目前也有不少的解决方案，比如&lt;a href=&quot;https://www.marginnote.com/&quot;&gt;MarginNote&lt;/a&gt;，&lt;a href=&quot;https://www.goodnotes.com/&quot;&gt;GoodNotes&lt;/a&gt;这些，但这些应用往往需要收费，且可同步的平台往往也受限颇多。对于穷哥们而言，我们需要探索更实惠且更实用的阅读及同步方案，在不花一分钱的前提下，能够&lt;del&gt;白嫖&lt;/del&gt;享受同步的便利。&lt;/p&gt;

&lt;h2 id=&quot;使用zotero进行笔记管理及多端同步&quot;&gt;使用Zotero进行笔记管理及多端同步&lt;/h2&gt;

&lt;p&gt;一般对于苹果全家桶而言，使用&lt;a href=&quot;https://pdfexpert.com/&quot;&gt;PDF Expert&lt;/a&gt;，&lt;a href=&quot;https://www.marginnote.com/&quot;&gt;MarginNote&lt;/a&gt;这些同步PDF及笔记足矣，但这些App大多并不支持Windows，Linux平台，说不上是一个合适的多端同步方案。还好，在学术圈中，还有一个大名鼎鼎的软件——&lt;strong&gt;Zotero&lt;/strong&gt;。虽说Zotero的使用场合大多都是文献管理，但用在PDF电子书阅读上也足够合适。&lt;/p&gt;

&lt;p&gt;Zotero官网地址：&lt;a href=&quot;https://www.zotero.org/&quot;&gt;https://www.zotero.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;建议注册一个Zotero账号&lt;/strong&gt;，方便能同步自己的设置到各端。&lt;/p&gt;

&lt;h2 id=&quot;使用chrome内嵌pdf阅读器进行ocr分析&quot;&gt;使用Chrome内嵌PDF阅读器进行OCR分析&lt;/h2&gt;

&lt;p&gt;对于一些扫描版的PDF，我们没办法直接选择文本进行高亮。在过去，一般都需要借助一些专业PDF工具进行OCR扫描，这些工具往往需要收费，即便有免费使用往往也会存在额度，对于大部头而言可谓杯水车薪。还好，Chrome从2024年的&lt;a href=&quot;https://chromeos.dev/en/releases/chromeos-126&quot;&gt;126版本&lt;/a&gt;后，其内嵌PDF阅读器已经支持了&lt;strong&gt;免费的本地OCR扫描功能&lt;/strong&gt;！穷哥们的我们可以利用这个功能，不用一分钱即可轻松给扫描版的PDF文件提取文字。&lt;/p&gt;

&lt;p&gt;怎么启用这个功能呢？很简单，直接在Chrome里打开PDF文件即可。如果是扫描版PDF，此时Chrome会自动开启OCR，并在左下方会出现一个“正在从PDF文件中提取文本…”的提示。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/chrome_extracting_text_from_pdf.png&quot; alt=&quot;Chrome上“正在从PDF文件中提取文本”的提示&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;Chrome上“正在从PDF文件中提取文本”的提示&lt;/p&gt;

&lt;p&gt;耐心等待，直至提示消失，此时我们可以发现所有的页面都可以直接选择文字了。完美！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/select_text_from_pdf_in_chrome.png&quot; alt=&quot;OCR转换后，所有的页面都可以直接选择文字了&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;OCR转换后，所有的页面都可以直接选择文字了&lt;/p&gt;

&lt;p&gt;遗憾的是，Chrome并不支持将这些文本保存在原PDF中。我们需要曲线救国，使用&lt;strong&gt;打印功能&lt;/strong&gt;将文本保留下来。注意目标打印机选择“另存为PDF”，别选错成自己现实的打印机了，要不然浪费一堆纸🤪&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/print_pdf_in_chrome.png&quot; alt=&quot;使用打印功能将文本保留下来&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;使用打印功能将文本保留下来&lt;/p&gt;

&lt;p&gt;可以发现，这时候“打印”下来的PDF文件是可以选择文本的了，但遗憾的是，“打印”之后，&lt;strong&gt;书签内容丢失了😨&lt;/strong&gt;，我们还需要借助PDF工具，将书签迁移过来。&lt;/p&gt;

&lt;p&gt;还好还好，书签导入导出功能在很多PDF编辑器中是免费的（毕竟技术含量不算高，收费就过分了）。我们可以使用福昕PDF编辑器，从原来的扫描版PDF导出书签内容，然后导入到“打印”后的PDF就好啦。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/export_or_import_bookmarks_in_foxit_pdf_editor.png&quot; alt=&quot;在福昕PDF编辑器导出/导入书签内容&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;在福昕PDF编辑器导出/导入书签内容&lt;/p&gt;

&lt;p&gt;此时，我们再导入到Zotero里面，就可以轻松选择并高亮文本了。&lt;/p&gt;

&lt;h2 id=&quot;使用infini-cloud进行webdav同步&quot;&gt;使用Infini Cloud进行WebDAV同步&lt;/h2&gt;

&lt;p&gt;我们还缺最后一步，也是最关键的一步：PDF及笔记的同步。&lt;/p&gt;

&lt;p&gt;如果使用Zotero进行PDF及笔记的同步，一般有两种办法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;使用Zotero官方的云同步服务：小贵，和账号强绑定&lt;/li&gt;
  &lt;li&gt;使用WebDAV协议同步：自定义空间比较大，需要自己寻找支持WebDAV云盘&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于穷哥们而言，使用第二个方法可以省下不少银子。但国内可选的云盘并不大，大厂的云盘往往不支持WebDAV，而坚果云的价格也略贵。我们可以将目光投在国外的云盘上，看看有没有羊毛可薅。&lt;/p&gt;

&lt;p&gt;InfiniCLOUD是一个性价比较高的WebDAV云盘，注册后即可有20G的免费空间，相比坚果云的免费方案要实用不少。虽然体验下来，InfiniCLOUD网页版的速度不敢恭维，但用作Zotero WebDAV同步此类实时性要求不高的场合还是足够的。&lt;/p&gt;

&lt;p&gt;InfiniCLOUD地址：&lt;a href=&quot;https://infini-cloud.net/en/&quot;&gt;https://infini-cloud.net/en/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;注册成功后，我们可在&lt;a href=&quot;https://infini-cloud.net/en/modules/mypage/usage/&quot;&gt;My Page页面&lt;/a&gt;中，开启Apps Connection&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/turn_on_apps_connection.png&quot; alt=&quot;开启Apps Connection&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;开启Apps Connection&lt;/p&gt;

&lt;p&gt;将InfiniCLOUD提供的WebDAV地址、账号及密码填到Zotero的设置里，然后开启Sync功能即可。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/zotero_webdav_settings.png&quot; alt=&quot;Zotero WebDAV设置&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;Zotero WebDAV设置&lt;/p&gt;

&lt;p&gt;Enjoy Reading!&lt;/p&gt;

&lt;h2 id=&quot;附再白嫖infinicloud-5g的免费空间&quot;&gt;附：再白嫖InfiniCLOUD 5G的免费空间&lt;/h2&gt;

&lt;p&gt;可以在&lt;a href=&quot;https://infini-cloud.net/en/modules/mypage/usage/&quot;&gt;My Page&lt;/a&gt; → Referral Bonus → Enter Friends Referral Code 填我的邀请码，能再白嫖5G的免费空间🤪：K8K8Z&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/poor-guys-sync-pdf/infini_cloud_referral_bonus.png&quot; alt=&quot;InfiniCLOUD推荐奖励&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;InfiniCLOUD推荐奖励&lt;/p&gt;

</description>
        <pubDate>Sat, 06 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/09/06/Poor-Guys-Sync-Pdf/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/09/06/Poor-Guys-Sync-Pdf/</guid>
        
        <category>杂七杂八</category>
        
        <category>PDF</category>
        
        <category>电子书</category>
        
        <category>同步</category>
        
        
      </item>
    
      <item>
        <title>在PyCharm中调试PyQt6程序报错&quot;This application failed to start because no Qt platform plugin could be initialized.&quot;的解决办法</title>
        <description>&lt;p&gt;在使用PyCharm开发PyQt6程序的时候，有时候调试会遇上一些比较诡异的问题。比如在macOS上，会出现以下报错：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;objc[69622]: Class QDarwinBluetoothPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x1070333f8) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac6f08). One of the two will be used. Which one is undefined.
objc[69622]: Class QDarwinCalendarPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x107033470) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac6f80). One of the two will be used. Which one is undefined.
objc[69622]: Class QDarwinCameraPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x1070334c0) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac6fd0). One of the two will be used. Which one is undefined.
objc[69622]: Class QDarwinContactsPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x107033510) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac7020). One of the two will be used. Which one is undefined.
objc[69622]: Class QDarwinMicrophonePermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x107033560) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac70c0). One of the two will be used. Which one is undefined.
qt.qpa.plugin: Could not load the Qt platform plugin &quot;cocoa&quot; in &quot;&quot; even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: offscreen, minimal, cocoa.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;同样地，在Windows上可能也会出现类似的问题：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/debug-pyqt6-error-in-pycharm/err-on-windows.png&quot; alt=&quot;Windows上类似的错误&quot; width=&quot;400&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;Windows上类似的错误&lt;/p&gt;

&lt;p&gt;看起来是缺少插件或者环境问题导致的。但是诡异的是，直接运行却不会出现上述的问题，只有PyCharm上调试才会出现。那这种大概率是PyCharm内部机制影响到运行环境了。&lt;/p&gt;

&lt;h2 id=&quot;修复方法&quot;&gt;修复方法&lt;/h2&gt;

&lt;p&gt;如果出现上述问题，可以先使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip freeze&lt;/code&gt;观察下Python环境是否同时安装了PySide6和PyQt6，如果有，那大概率是PyCharm里面的pydevd调试器的补丁代码影响到程序运行了。&lt;/p&gt;

&lt;p&gt;打开PyCharm的设置，在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Python -&amp;gt; Debugger&lt;/code&gt;面板中，&lt;strong&gt;如果&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt compatible&lt;/code&gt;已勾选，且所选项为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Auto&lt;/code&gt;，此时需要调整为程序所用的框架，即&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt6&lt;/code&gt;&lt;/strong&gt;。保存设置后，重新调试就好啦。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/debug-pyqt6-error-in-pycharm/pycharm-settings.png&quot; alt=&quot;调整PyCharm里的`PyQt compatible`设置&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;image-caption&quot;&gt;调整PyCharm里的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt compatible&lt;/code&gt;设置&lt;/p&gt;

&lt;h2 id=&quot;为什么会这样&quot;&gt;为什么会这样？&lt;/h2&gt;

&lt;p&gt;或许你会好奇为什么会出现这种情况，且为什么直接运行的时候又不会报错呢？其实这是pydevd对Qt框架注入测试代码的时候（&lt;a href=&quot;https://github.com/fabioz/PyDev.Debugger/blob/main/_pydev_bundle/pydev_monkey_qt.py#L20&quot;&gt;参见文件&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_pydev_bundle/pydev_monkey_qt&lt;/code&gt;&lt;/a&gt;），如果上述设置所选的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt compatible&lt;/code&gt;为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auto&lt;/code&gt;，那么它会根据自己的优先级寻找当前环境&lt;strong&gt;所安装&lt;/strong&gt;的目标框架（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyside6&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyside2&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyside&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyqt6&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyqt5&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyqt4&lt;/code&gt;），而非真正的”auto”（智能识别程序所用的Qt框架）。如果pydevd发现PySide6已安装，此时只会注入PySide6，而不会注入PyQt6的。&lt;/p&gt;

&lt;p&gt;而注入的过程本质是通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import&lt;/code&gt;语句导入相应的框架，然后再对其内部的对象进行猴子补丁。这就意味着，对于&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt6&lt;/code&gt;程序，它会在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyQt6&lt;/code&gt;导入前，先导入了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PySide6&lt;/code&gt;，相当于&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# --&amp;gt; Inserted by pydevd
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PySide6&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ... monkey patching for PySide6
# &amp;lt;-- Inserted Finished
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Here is your code...
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PyQt6&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ... your code&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;所以也就是为什么macOS里的PyCharm会抱怨&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class QDarwinBluetoothPermissionHandler is implemented in both /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PySide6/QtCore.abi3.so (0x1070333f8) and /Users/jeza/PyqtInspect/.venv-313/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so (0x106ac6f08). One of the two will be used. Which one is undefined.&lt;/code&gt;了😅。&lt;/p&gt;

</description>
        <pubDate>Sun, 31 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/08/31/Debug-PyQt6-Error-in-PyCharm/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/08/31/Debug-PyQt6-Error-in-PyCharm/</guid>
        
        <category>杂七杂八</category>
        
        <category>Python</category>
        
        <category>PyQt</category>
        
        
      </item>
    
      <item>
        <title>macOS 15使用brew安装binutils找不到命令(gobjdump, greadelf)的解决办法</title>
        <description>&lt;p&gt;最近在看《程序员的自我修养》，想在自己的mac上试验一下ELF文件的解析，找了下教程，使用brew安装了binutils后，直接在命令行输入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gobjdump&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;greadelf&lt;/code&gt;还是找不着命令。最后搜了下，发现brew安装的二进制文件放在了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/homebrew/opt&lt;/code&gt;上了。遂直接添加到环境变量即可。&lt;/p&gt;

&lt;!--more--&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/opt/homebrew/opt/binutils/bin:&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
        <pubDate>Sat, 30 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/08/30/macOS-use-binutils/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/08/30/macOS-use-binutils/</guid>
        
        <category>杂七杂八</category>
        
        <category>超级短</category>
        
        
      </item>
    
      <item>
        <title>Cursor连接Github Codespaces札记</title>
        <description>&lt;p&gt;Cursor可谓是今年AI Agent编程的网红产品了，其本质是在VS Code的基础上新增AI相关的功能。因此，VS Code的特性和大部分插件，它基本也支持。那么，按理说它也同样支持连接到Github Codespaces上进行开发，再加上强大的AI Agent，岂不美滋滋呢！&lt;/p&gt;

&lt;p&gt;可惜的是，由于某种限制，Cursor官方并不能直接连接Github Codespaces。即便通过导入的方法安装VS Code的Github Codespaces扩展，尝试了下，也只能登录和查看Codespaces列表，但无法连接到Codespaces上进行代码编辑。只能换别的路子尝试下。&lt;/p&gt;

&lt;p&gt;经过周末的一番尝试，终于通过SSH扩展以及Github CLI成功连接上Github Codespaces了，在codespace上使用AI和在本地代码开发一样丝滑，下面简单分享一下搭建的过程。&lt;/p&gt;

&lt;h2 id=&quot;前提条件&quot;&gt;前提条件&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Cursor扩展：Remote - SSH&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;需要安装好Github命令行工具&lt;a href=&quot;https://cli.github.com/&quot;&gt;Github CLI (gh)&lt;/a&gt;，然后根据&lt;a href=&quot;https://cli.github.com/manual/&quot;&gt;官方指引&lt;/a&gt;登录Github和授权访问codespaces上的内容。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;配置过程&quot;&gt;配置过程&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;先启动&lt;/strong&gt;需要连接的codespace（可以在Github网页上启动；也可以使用Github CLI键入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh&lt;/code&gt;，根据交互式提示连接到codespace上启动，SSH连接成功后键入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt;退出即可），然后输入命令&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh --config&lt;/code&gt;生成SSH配置，不处于运行状态的codespace将被跳过（此时会输出”skiping unavaliable codespace xxx”）。如果观察到该命令有有效的输出（开头是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Host xxxx&lt;/code&gt;），则说明配置生成成功了。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/gh-codespace-config-screenshot.png&quot; alt=&quot;`gh codespace ssh --config`输出截图&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh --config&lt;/code&gt;输出截图&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;回去Cursor，键入F1拉起命令面板，输入”SSH”，进入”Remote SSH - Open SSH Configuration File…”，打开SSH配置文件。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;将第一步&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh --config&lt;/code&gt;的输出（如果出现”skiping unavaliable codespace”开头的行，不要将这些行复制过去）添加到配置文件中（在后面追加，不要覆盖原来的内容）。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/copy-config-output.png&quot; alt=&quot;将`gh codespace ssh --config`的输出复制到SSH配置文件中&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh codespace ssh --config&lt;/code&gt;的输出复制到SSH配置文件中&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;保存后，键入F1拉起命令面板，同样输入”SSH”，进入”Remote SSH - Connect to Host…”连接刚刚配置好的Host。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/connect-to-ssh-1.png&quot; alt=&quot;在命令面板输入SSH，进入&amp;quot;Remote SSH - Connect to Host…&amp;quot;&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;在命令面板输入SSH，进入”Remote SSH - Connect to Host…”&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/connect-to-ssh-2.png&quot; alt=&quot;找到刚刚配置好的Host进行连接&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;找到刚刚配置好的Host进行连接&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;大功告成，此时在新拉起的Cursor界面上点击”Open Folder”即可，一般而言项目目录在”/workspaces/&lt;repo name=&quot;&quot;&gt;&quot;，Let’s use cursor to code!&lt;/repo&gt;&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/img/posts/cursor-connect-to-github-codespaces/open-folder-to-code.png&quot; alt=&quot;在新拉起的Cursor界面上点击&amp;quot;Open Folder&amp;quot;，打开项目所在目录&quot; /&gt;&lt;/p&gt;

    &lt;p class=&quot;image-caption&quot;&gt;在新拉起的Cursor界面上点击”Open Folder”，打开项目所在目录&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

</description>
        <pubDate>Sun, 20 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/07/20/Cursor-Connect-to-Github-Codespaces/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/07/20/Cursor-Connect-to-Github-Codespaces/</guid>
        
        <category>Cursor</category>
        
        <category>Github</category>
        
        <category>Github Codespaces</category>
        
        <category>杂七杂八</category>
        
        
      </item>
    
      <item>
        <title>C++模板元编程学习笔记 1: 模板的模板参数</title>
        <description>&lt;p&gt;模板的模板参数是C++模板元编程一个重要的基础概念。它允许我们在模板中使用其他模板作为参数，从而实现更复杂的类型和行为。&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;首先看一段简单的代码示例：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;tuple&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;first_type_getter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;first_type_getter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 不能这样子写&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 这样的话，first_type需要提供不止一个模板参数的&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 原本的 first_type&amp;lt;tuple&amp;lt;int, float, double&amp;gt;&amp;gt; 就要变成 first_type&amp;lt;tuple, int, float, double&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// template&amp;lt;template&amp;lt;typename...&amp;gt; typename Ts, typename T1, typename... TN&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// using first_type = typename first_type_getter&amp;lt;Ts&amp;lt;T1, TN...&amp;gt;&amp;gt;::type;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_type_getter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_same_v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_same_v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 0&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_same_v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 0&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;在这里，我们首先定义了一个通用的模板 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type_getter&lt;/code&gt;，它接受一个类型参数。然后，我们对这个模板&lt;strong&gt;进行了偏特化&lt;/strong&gt;，通过&lt;em&gt;模板的模板参数&lt;/em&gt;使其能够深入获取&lt;strong&gt;参数内部的类型&lt;/strong&gt;。这个偏特化版本提取了模板参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ts&lt;/code&gt;中的第一个类型 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T1&lt;/code&gt;，并将其定义为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt;。在后续使用时，我们就可以通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type&amp;lt;std::tuple&amp;lt;int, float, double&amp;gt;&amp;gt;&lt;/code&gt;来获取元组的第一个类型&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;模板的模板参数语法&quot;&gt;模板的模板参数语法&lt;/h3&gt;

&lt;p&gt;需要注意的是，使用模板的模板参数时，正确的语法应该长这样 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template&amp;lt;template&amp;lt;typename...&amp;gt; typename Ts&amp;gt;&lt;/code&gt;。其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template&amp;lt;typename...&amp;gt;&lt;/code&gt;表示这个参数是一个模板，然后后面&lt;strong&gt;还需要加上&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;typename&lt;/code&gt;&lt;/strong&gt;以表明这是一个类型参数。&lt;/p&gt;

&lt;h3 id=&quot;在上述的例子中即便ts的实参是一个类型别名编译器仍然能够正确解析&quot;&gt;在上述的例子中，即便Ts的实参是一个类型别名，编译器仍然能够正确解析。&lt;/h3&gt;

&lt;p&gt;比如，如果我们使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using MyTuple = std::tuple&amp;lt;int, float, double&amp;gt;;&lt;/code&gt;，然后调用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type&amp;lt;MyTuple&amp;gt;&lt;/code&gt;，编译器仍然能够正确解析出 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; 作为第一个类型。&lt;/p&gt;

&lt;h3 id=&quot;为什么这里需要以偏特化的方式来实现第一个类型获取&quot;&gt;为什么这里需要以偏特化的方式来实现第一个类型获取？&lt;/h3&gt;

&lt;p&gt;我们先假定不用偏特化的形式，直接定义一个模板：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-cpp&quot; data-lang=&quot;cpp&quot;&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;first_type_getter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;如此，我们可以使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type_getter&amp;lt;std::tuple&amp;lt;int, float, double&amp;gt;&amp;gt;&lt;/code&gt;来获取第一个类型吗？答案是否定的。因为在这种情况下，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;first_type_getter&lt;/code&gt;的实例化&lt;strong&gt;需要提供不止一个模板参数&lt;/strong&gt;（因为形参列表有多个参数），和我们的本意相悖。由于我们只需要传一个模板实参，且目的是&lt;strong&gt;从该模板实参中提取出内部的参数&lt;/strong&gt;，所以只能&lt;strong&gt;先定义一个通用的、接受一个类型参数的模板&lt;/strong&gt;，然后再对其进行偏特化。&lt;/p&gt;
</description>
        <pubDate>Wed, 16 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://www.jeza.net/2025/07/16/Cpp-Template-Learning-1/</link>
        <guid isPermaLink="true">https://www.jeza.net/2025/07/16/Cpp-Template-Learning-1/</guid>
        
        <category>C++</category>
        
        <category>C++进阶</category>
        
        <category>C++模板</category>
        
        <category>C++元编程</category>
        
        
      </item>
    
  </channel>
</rss>
