summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormuqiuhan <[email protected]>2026-03-22 15:14:32 +0000
committermuqiuhan <[email protected]>2026-03-22 15:14:32 +0000
commit96fbc0ca754356607aabe6a549636d7149fc3dc2 (patch)
tree29c126a81da6b824d2d162feffd4b9dcc723335c
parent153910d9a2c00d1abc492f330964329ef5ec6d75 (diff)
downloadblog-96fbc0ca754356607aabe6a549636d7149fc3dc2.tar.gz
deploy: bf86ca589e2db49e19cd07097827fc6c0c56dd0b
-rw-r--r--2026/03/21/小谈-NestJS-Zod/index.html5
-rw-r--r--2026/03/22/在-OCaml-中通过-CTypes-绑定-libseccomp/index.html420
-rw-r--r--archives/2026/03/index.html18
-rw-r--r--archives/2026/index.html18
-rw-r--r--archives/index.html36
-rw-r--r--archives/page/10/index.html36
-rw-r--r--archives/page/11/index.html36
-rw-r--r--archives/page/12/index.html36
-rw-r--r--archives/page/13/index.html36
-rw-r--r--archives/page/14/index.html36
-rw-r--r--archives/page/15/index.html36
-rw-r--r--archives/page/16/index.html36
-rw-r--r--archives/page/17/index.html36
-rw-r--r--archives/page/18/index.html18
-rw-r--r--archives/page/2/index.html36
-rw-r--r--archives/page/3/index.html36
-rw-r--r--archives/page/4/index.html36
-rw-r--r--archives/page/5/index.html36
-rw-r--r--archives/page/6/index.html36
-rw-r--r--archives/page/7/index.html36
-rw-r--r--archives/page/8/index.html36
-rw-r--r--archives/page/9/index.html36
-rw-r--r--search.xml419
-rw-r--r--tags/Technique/index.html36
-rw-r--r--tags/Technique/page/2/index.html36
-rw-r--r--tags/Technique/page/3/index.html36
-rw-r--r--tags/Technique/page/4/index.html36
-rw-r--r--tags/Technique/page/5/index.html36
-rw-r--r--tags/Technique/page/6/index.html18
29 files changed, 1192 insertions, 516 deletions
diff --git a/2026/03/21/小谈-NestJS-Zod/index.html b/2026/03/21/小谈-NestJS-Zod/index.html
index 70f6b8b2..2100383d 100644
--- a/2026/03/21/小谈-NestJS-Zod/index.html
+++ b/2026/03/21/小谈-NestJS-Zod/index.html
@@ -241,6 +241,11 @@
<nav class="post-nav">
<div class="prev-item">
+ <div class="icon arrow-left"></div>
+ <div class="post-link">
+ <a href="/2026/03/22/%E5%9C%A8-OCaml-%E4%B8%AD%E9%80%9A%E8%BF%87-CTypes-%E7%BB%91%E5%AE%9A-libseccomp/">Prev</a>
+ </div>
+
</div>
<div class="next-item">
diff --git a/2026/03/22/在-OCaml-中通过-CTypes-绑定-libseccomp/index.html b/2026/03/22/在-OCaml-中通过-CTypes-绑定-libseccomp/index.html
new file mode 100644
index 00000000..f593df4f
--- /dev/null
+++ b/2026/03/22/在-OCaml-中通过-CTypes-绑定-libseccomp/index.html
@@ -0,0 +1,420 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+<meta http-equiv="X-UA-Compatible" content="ie=edge">
+
+ <meta name="author" content="韩暮秋">
+
+
+ <meta name="subtitle" content="暮秋小屋">
+
+
+ <meta name="description" content="这里是暮秋小屋,思念和灵感的寄存处">
+
+
+ <meta name="keywords" content="韩暮秋,MuqiuHan,'Muqiu Han', 'muqiu han', muqiuhan">
+
+
+
+
+ <title>
+
+ 在 OCaml 中通过 CTypes 绑定 libseccomp |
+ 暮秋小屋
+ </title>
+
+
+
+ <link rel="icon" href="/favicon.ico">
+
+
+
+
+ <!-- stylesheets list from _config.yml -->
+
+ <link rel="stylesheet" href="/css/style.css">
+
+
+
+
+ <link rel="preload" href="/fonts/latin-base.woff2" as="font" type="font/woff2" crossorigin>
+ <link rel="preload" href="/fonts/cjk-common.woff2" as="font" type="font/woff2" crossorigin>
+
+
+
+ <!-- scripts list from _config.yml -->
+
+ <script
+ src="/js/menu.js"></script>
+
+
+
+
+
+ <script
+ src="https://polyfill.alicdn.com/polyfill.js?features=es6"></script>
+ <script
+ id="MathJax-script"
+ async
+ src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/mathjax/3.2.0/es5/tex-mml-chtml.js"></script>
+
+
+
+
+ <meta name="generator" content="Hexo 6.3.0"></head>
+ <body>
+ <div class="wrapper">
+
+ <div class="header">
+ <div class="flex-container">
+ <div class="header-inner">
+ <div class="site-brand-container">
+ <a href="/">
+
+ 暮秋小屋
+
+ </a>
+ </div>
+ <div id="menu-btn" class="menu-btn" onclick="toggleMenu()">
+ 菜单
+ </div>
+ <nav class="site-nav">
+ <ul class="menu-list">
+
+
+ <li class="menu-item">
+ <a href="/">
+ 主页
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/categories/gallery/">
+ 日记本
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Medicine/">
+ 泛医学
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Technique/">
+ 计算机
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Life/">
+ 生活
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/archives">
+ 全部
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/about">
+ 关于
+ </a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/search">搜索</a>
+ </li>
+
+ </ul>
+ </nav>
+ </div>
+ </div>
+</div>
+
+
+ <div class="main">
+ <div class="flex-container">
+ <article id="post">
+
+
+ <div class="post-head">
+ <div class="post-info">
+ <div class="tag-list">
+
+
+ <span class="post-tag">
+ <a href="/tags/Technique/">
+ Technique
+ </a>
+ </span>
+
+
+ </div>
+ <div class="post-title">
+
+
+ 在 OCaml 中通过 CTypes 绑定 libseccomp
+
+
+ </div>
+ <span class="post-date">
+ Mar 22, 2026
+ </span>
+ </div>
+ <div class="post-img">
+
+ <div class="h-line-primary"></div>
+
+ </div>
+</div>
+ <div class="post-content">
+ <p>CTypes 允许我们用纯 OCaml 代码绑定 C lib 而不用编写任何 C 代码。这篇文章是以 <a target="_blank" rel="noopener" href="https://github.com/muqiuhan/bonding">bonding</a> 项目中的 <code>libseccomp</code> 绑定为例写的。</p>
+<p>CTypes 的核心概念是建立 OCaml 类型与 C 类型的双向映射,ctypes 中有如下基本类型:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">open</span> <span class="type">Ctypes</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* C 类型与 OCaml 类型的对应关系 *)</span></span><br><span class="line"><span class="keyword">let</span> c_int : <span class="built_in">int</span> typ = <span class="built_in">int</span> <span class="comment">(* int *)</span></span><br><span class="line"><span class="keyword">let</span> c_uint : <span class="type">Unsigned</span>.<span class="type">UInt</span>.t typ = uint <span class="comment">(* unsigned int *)</span></span><br><span class="line"><span class="keyword">let</span> c_long : <span class="type">Signed</span>.<span class="type">Long</span>.t typ = long <span class="comment">(* long *)</span></span><br><span class="line"><span class="keyword">let</span> c_int64 : <span class="type">Unsigned</span>.<span class="type">UInt64</span>.t typ = uint64_t <span class="comment">(* int64_t / uint64_t *)</span></span><br><span class="line"><span class="keyword">let</span> c_float : <span class="built_in">float</span> typ = <span class="built_in">float</span> <span class="comment">(* float *)</span></span><br><span class="line"><span class="keyword">let</span> c_double : <span class="built_in">float</span> typ = double <span class="comment">(* double *)</span></span><br><span class="line"><span class="keyword">let</span> c_char : <span class="built_in">char</span> typ = <span class="built_in">char</span> <span class="comment">(* char *)</span></span><br><span class="line"><span class="keyword">let</span> c_void : <span class="built_in">unit</span> typ = void <span class="comment">(* void *)</span></span><br></pre></td></tr></table></figure>
+<h2 id="指针"><a class="header-anchor" href="#指针">¶</a>指针</h2>
+<p>对于 C 的指针,CTypes 提供了多种指针表示:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 通用指针:void* *)</span></span><br><span class="line"><span class="keyword">let</span> void_ptr : <span class="built_in">unit</span> ptr typ = ptr void</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 类型化指针:int* *)</span></span><br><span class="line"><span class="keyword">let</span> int_ptr : <span class="built_in">int</span> ptr typ = ptr <span class="built_in">int</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 不透明指针:用于表示我们不知道内部结构的 C 结构体 *)</span></span><br><span class="line"><span class="keyword">let</span> scmp_filter_ctx : <span class="built_in">unit</span> ptr typ = ptr void</span><br></pre></td></tr></table></figure>
+<p>bonding 中的 seccomp_stubs.ml 中是这么写的:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> scmp_filter_ctx = <span class="built_in">unit</span> ptr</span><br><span class="line"><span class="keyword">let</span> scmp_filter_ctx : scmp_filter_ctx typ = ptr void</span><br></pre></td></tr></table></figure>
+<p>这里 <code>scmp_filter_ctx</code> 是 libseccomp 的核心类型,它是一个不透明指针。C 库不暴露其内部结构,我们只能通过库函数操作它。使用 <code>ptr void</code> 是表示不透明指针的标准做法。</p>
+<h2 id="结构体"><a class="header-anchor" href="#结构体">¶</a>结构体</h2>
+<p>对于结构体,ctypes 通过 <code>structure</code> 和 <code>field</code> 函数定义:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 定义 C 结构体:</span></span><br><span class="line"><span class="comment"> struct scmp_arg_cmp &#123;</span></span><br><span class="line"><span class="comment"> unsigned int arg;</span></span><br><span class="line"><span class="comment"> unsigned int op;</span></span><br><span class="line"><span class="comment"> uint64_t datum_a;</span></span><br><span class="line"><span class="comment"> uint64_t datum_b;</span></span><br><span class="line"><span class="comment"> &#125;;</span></span><br><span class="line"><span class="comment">*)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> scmp_arg_cmp : <span class="built_in">unit</span> structure typ =</span><br><span class="line"> <span class="keyword">let</span> s = structure <span class="string">&quot;scmp_arg_cmp&quot;</span> <span class="keyword">in</span> <span class="comment">(* 1. 创建结构体模板 *)</span></span><br><span class="line"> <span class="keyword">let</span> _arg = field s <span class="string">&quot;arg&quot;</span> uint <span class="keyword">in</span> <span class="comment">(* 2. 添加字段 *)</span></span><br><span class="line"> <span class="keyword">let</span> _op = field s <span class="string">&quot;op&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _datum_a = field s <span class="string">&quot;datum_a&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _datum_b = field s <span class="string">&quot;datum_b&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> seal s; <span class="comment">(* 3. 封印结构体 *)</span></span><br><span class="line"> s <span class="comment">(* 4. 返回类型描述符 *)</span></span><br></pre></td></tr></table></figure>
+<p>注意:</p>
+<ul>
+<li><code>structure &quot;name&quot;</code> 创建一个未完成的类型描述符</li>
+<li><code>field s &quot;fieldname&quot; typ</code> 添加字段,返回字段访问器</li>
+<li><code>seal s</code> 完成结构体定义,之后不可再添加字段</li>
+<li>返回的 <code>s</code> 可以作为类型使用</li>
+</ul>
+<p>例如:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> scmp_arg_cmp : <span class="built_in">unit</span> structure typ =</span><br><span class="line"> <span class="keyword">let</span> s = structure <span class="string">&quot;scmp_arg_cmp&quot;</span> <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _arg = field s <span class="string">&quot;arg&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _op = field s <span class="string">&quot;op&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _datum_a = field s <span class="string">&quot;datum_a&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _datum_b = field s <span class="string">&quot;datum_b&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> seal s;</span><br><span class="line"> s</span><br></pre></td></tr></table></figure>
+<p>定义结构体后,可以通过字段访问器读写值:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 写入字段值 *)</span></span><br><span class="line"><span class="keyword">let</span> set_arg_cmp_values struct_ptr arg_val op_val datum_a_val datum_b_val =</span><br><span class="line"> <span class="keyword">let</span> s = structure <span class="string">&quot;scmp_arg_cmp&quot;</span> <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> arg = field s <span class="string">&quot;arg&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> op = field s <span class="string">&quot;op&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> datum_a = field s <span class="string">&quot;datum_a&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> datum_b = field s <span class="string">&quot;datum_b&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> seal s;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">(* 使用 setf 设置字段值 *)</span></span><br><span class="line"> setf struct_ptr arg (<span class="type">Unsigned</span>.<span class="type">UInt</span>.of_int arg_val);</span><br><span class="line"> setf struct_ptr op (<span class="type">Unsigned</span>.<span class="type">UInt</span>.of_int op_val);</span><br><span class="line"> setf struct_ptr datum_a (<span class="type">Unsigned</span>.<span class="type">UInt64</span>.of_int64 datum_a_val);</span><br><span class="line"> setf struct_ptr datum_b (<span class="type">Unsigned</span>.<span class="type">UInt64</span>.of_int64 datum_b_val)</span><br></pre></td></tr></table></figure>
+<p>这里展示了如何创建辅助函数来操作结构体。注意:</p>
+<ul>
+<li>每次都需要重新定义结构体和字段访问器</li>
+<li><code>setf</code> 的参数顺序:结构体指针、字段访问器、值</li>
+<li>OCaml 的无符号类型需要显式转换</li>
+</ul>
+<h2 id="动态库"><a class="header-anchor" href="#动态库">¶</a>动态库</h2>
+<p>使用 CTypes 绑定 C 库只需要:加载库、然后绑定函数。</p>
+<p>首先,加载共享库:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> libseccomp =</span><br><span class="line"> <span class="keyword">let</span> paths = [ <span class="string">&quot;libseccomp.so.2&quot;</span>; <span class="string">&quot;libseccomp.so.1&quot;</span>; <span class="string">&quot;libseccomp.so&quot;</span> ] <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">rec</span> try_paths = <span class="keyword">function</span></span><br><span class="line"> | <span class="literal">[]</span> -&gt;</span><br><span class="line"> <span class="type">Dl</span>.dlopen ~filename:<span class="string">&quot;libseccomp.so.2&quot;</span> ~flags:[ <span class="type">Dl</span>.<span class="type">RTLD_NOW</span> ]</span><br><span class="line"> | path :: rest -&gt; (</span><br><span class="line"> <span class="keyword">try</span> <span class="type">Dl</span>.dlopen ~filename:path ~flags:[ <span class="type">Dl</span>.<span class="type">RTLD_NOW</span> ] <span class="keyword">with</span></span><br><span class="line"> | _ -&gt;</span><br><span class="line"> try_paths rest)</span><br><span class="line"> <span class="keyword">in</span></span><br><span class="line"> try_paths paths</span><br></pre></td></tr></table></figure>
+<p>这段代码用了一个提升动态库加载稳定性的小技巧:</p>
+<ul>
+<li>尝试多个可能的库文件名(不同系统可能版本不同)</li>
+<li>依次尝试,直到成功</li>
+<li>如果全部失败,使用默认路径(触发异常)</li>
+</ul>
+<p>注意 <code>Dl.dlopen</code> 的参数:</p>
+<ul>
+<li><code>filename</code>: 库文件名(可以是绝对路径或库文件名)</li>
+<li><code>flags</code>: 加载标志
+<ul>
+<li><code>RTLD_NOW</code>: 立即解析所有符号</li>
+<li><code>RTLD_LAZY</code>: 延迟解析符号</li>
+</ul>
+</li>
+</ul>
+<p>加载完成后使用 <code>Foreign.foreign</code> 绑定到函数,基本语法:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> function_name = </span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:library</span><br><span class="line"> <span class="string">&quot;c_function_name&quot;</span></span><br><span class="line"> (arg1_type @-&gt; arg2_type @-&gt; ... @-&gt; returning return_type)</span><br></pre></td></tr></table></figure>
+<p>单参数函数:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_init =</span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:libseccomp</span><br><span class="line"> <span class="string">&quot;seccomp_init&quot;</span></span><br><span class="line"> (uint32_t @-&gt; returning scmp_filter_ctx)</span><br></pre></td></tr></table></figure>
+<p>对应的 C 函数原型:</p>
+<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scmp_filter_ctx <span class="title function_">seccomp_init</span><span class="params">(<span class="type">uint32_t</span> def_action)</span>;</span><br></pre></td></tr></table></figure>
+<p><code>Foreign.foreign</code> 的参数:</p>
+<ul>
+<li><code>~from:libseccomp</code>: 指定函数所在的库</li>
+<li><code>&quot;seccomp_init&quot;</code>: C 函数名</li>
+<li><code>uint32_t @-&gt; returning scmp_filter_ctx</code>: 函数签名
+<ul>
+<li><code>@-&gt;</code> 分隔参数</li>
+<li><code>returning</code> 标记返回类型</li>
+</ul>
+</li>
+</ul>
+<p>对于多参数函数:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_rule_add =</span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:libseccomp</span><br><span class="line"> <span class="string">&quot;seccomp_rule_add&quot;</span></span><br><span class="line"> (scmp_filter_ctx @-&gt; uint32_t @-&gt; <span class="built_in">int</span> @-&gt; uint @-&gt; returning <span class="built_in">int</span>)</span><br></pre></td></tr></table></figure>
+<p><strong>C 函数原型</strong>:</p>
+<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">seccomp_rule_add</span><span class="params">(scmp_filter_ctx ctx, <span class="type">uint32_t</span> action, </span></span><br><span class="line"><span class="params"> <span class="type">int</span> syscall, <span class="type">unsigned</span> <span class="type">int</span> arg_cnt)</span>;</span><br></pre></td></tr></table></figure>
+<p>对于无参数函数:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_arch_native =</span><br><span class="line"> <span class="type">Foreign</span>.foreign </span><br><span class="line"> ~from:libseccomp </span><br><span class="line"> <span class="string">&quot;seccomp_arch_native&quot;</span> </span><br><span class="line"> (void @-&gt; returning uint32_t)</span><br></pre></td></tr></table></figure>
+<p>对于指针参数:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_load =</span><br><span class="line"> <span class="type">Foreign</span>.foreign </span><br><span class="line"> ~from:libseccomp </span><br><span class="line"> <span class="string">&quot;seccomp_load&quot;</span> </span><br><span class="line"> (scmp_filter_ctx @-&gt; returning <span class="built_in">int</span>)</span><br></pre></td></tr></table></figure>
+<p><strong>C 函数原型</strong>:</p>
+<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">seccomp_load</span><span class="params">(scmp_filter_ctx ctx)</span>;</span><br></pre></td></tr></table></figure>
+<p>对于字符串参数:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_syscall_resolve_name =</span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:libseccomp</span><br><span class="line"> <span class="string">&quot;seccomp_syscall_resolve_name&quot;</span></span><br><span class="line"> (<span class="built_in">string</span> @-&gt; returning <span class="built_in">int</span>)</span><br></pre></td></tr></table></figure>
+<p>这里 CTypes 会自动处理 OCaml 字符串与 C 字符串的转换。</p>
+<p>对于返回 NULL 的情况:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_syscall_resolve_num_arch =</span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:libseccomp</span><br><span class="line"> <span class="string">&quot;seccomp_syscall_resolve_num_arch&quot;</span></span><br><span class="line"> (uint32_t @-&gt; <span class="built_in">int</span> @-&gt; returning string_opt)</span><br></pre></td></tr></table></figure>
+<p><code>string_opt</code> 处理可能返回 NULL 的字符串指针。</p>
+<h2 id="内存管理"><a class="header-anchor" href="#内存管理">¶</a>内存管理</h2>
+<p>C 需要手动管理内存,CTypes 提供了相应的工具。</p>
+<p>分配单个值:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 分配并初始化单个值 *)</span></span><br><span class="line"><span class="keyword">let</span> ptr = allocate <span class="built_in">int</span> <span class="number">42</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 读取值 *)</span></span><br><span class="line"><span class="keyword">let</span> <span class="keyword">value</span> = !@ ptr</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 写入值 *)</span></span><br><span class="line">ptr &lt;-@ <span class="number">100</span></span><br></pre></td></tr></table></figure>
+<p>分配数组:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 分配数组(未初始化) *)</span></span><br><span class="line"><span class="keyword">let</span> arr = allocate_n <span class="built_in">int</span> ~count:<span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 分配并初始化数组 *)</span></span><br><span class="line"><span class="keyword">let</span> arr = allocate_n <span class="built_in">int</span> ~count:<span class="number">5</span> ~initial:<span class="number">0</span></span><br></pre></td></tr></table></figure>
+<p>例如:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> cmp_array = <span class="type">Ctypes</span>.allocate_n <span class="type">Stubs</span>.<span class="type">Types</span>.scmp_arg_cmp ~count:n <span class="keyword">in</span></span><br></pre></td></tr></table></figure>
+<h3 id="指针算术"><a class="header-anchor" href="#指针算术">¶</a>指针算术</h3>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = allocate_n <span class="built_in">int</span> ~count:<span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 访问第 i 个元素 *)</span></span><br><span class="line"><span class="keyword">let</span> elem_ptr = arr +@ i</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 读写元素 *)</span></span><br><span class="line"><span class="keyword">let</span> <span class="keyword">value</span> = !@ elem_ptr</span><br><span class="line">elem_ptr &lt;-@ <span class="number">42</span></span><br></pre></td></tr></table></figure>
+<p>例如:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Stubs</span>.<span class="type">Types</span>.set_arg_cmp_values</span><br><span class="line"> !@(cmp_array +@ i) <span class="comment">(* 第 i 个结构体的指针 *)</span></span><br><span class="line"> cmp.arg</span><br><span class="line"> (cmp_op_to_int cmp.op)</span><br><span class="line"> cmp.datum_a</span><br><span class="line"> cmp.datum_b</span><br></pre></td></tr></table></figure>
+<p><code>!@</code> 运算符解引用指针,<code>+@</code> 进行指针算术。</p>
+<h3 id="指针转换"><a class="header-anchor" href="#指针转换">¶</a>指针转换</h3>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 类型化指针转 void 指针 *)</span></span><br><span class="line"><span class="keyword">let</span> void_p = to_voidp int_ptr</span><br><span class="line"></span><br><span class="line"><span class="comment">(* void 指针转类型化指针 *)</span></span><br><span class="line"><span class="keyword">let</span> int_p = from_voidp <span class="built_in">int</span> void_p</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 获取空指针 *)</span></span><br><span class="line"><span class="keyword">let</span> null_ptr : <span class="built_in">unit</span> ptr = null</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 检查是否为空指针 *)</span></span><br><span class="line"><span class="keyword">if</span> is_null ptr <span class="keyword">then</span> ...</span><br></pre></td></tr></table></figure>
+<p><strong>实例解析(<a target="_blank" rel="noopener" href="http://seccomp.ml:96">seccomp.ml:96</a>)</strong>:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="type">Ctypes</span>.ptr_compare ctx <span class="type">Ctypes</span>.null = <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> <span class="type">Error</span> <span class="type">Init_failed</span></span><br></pre></td></tr></table></figure>
+<p>检查 <code>seccomp_init</code> 是否返回了 NULL(失败)。</p>
+<h2 id="一些模式"><a class="header-anchor" href="#一些模式">¶</a>一些模式</h2>
+<h3 id="类型视图(Type-Views)"><a class="header-anchor" href="#类型视图(Type-Views)">¶</a>类型视图(Type Views)</h3>
+<p>当 C 类型与 OCaml 类型不能直接对应时,使用 <code>view</code> 创建自定义转换:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 将 C int 转换为 OCaml bool *)</span></span><br><span class="line"><span class="keyword">let</span> c_bool = view</span><br><span class="line"> ~read:(<span class="keyword">fun</span> i -&gt; i &lt;&gt; <span class="number">0</span>)</span><br><span class="line"> ~write:(<span class="keyword">fun</span> b -&gt; <span class="keyword">if</span> b <span class="keyword">then</span> <span class="number">1</span> <span class="keyword">else</span> <span class="number">0</span>)</span><br><span class="line"> <span class="built_in">int</span></span><br></pre></td></tr></table></figure>
+<h3 id="枚举类型映射"><a class="header-anchor" href="#枚举类型映射">¶</a>枚举类型映射</h3>
+<p>在 seccomp 实现中,使用 OCaml 变体和转换函数映射 C 枚举:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* OCaml 端的类型定义(seccomp.ml:18-25) *)</span></span><br><span class="line"><span class="keyword">type</span> action =</span><br><span class="line"> | <span class="type">Allow</span></span><br><span class="line"> | <span class="type">Errno</span> <span class="keyword">of</span> <span class="built_in">int</span></span><br><span class="line"> | <span class="type">KillProcess</span></span><br><span class="line"> | <span class="type">KillThread</span></span><br><span class="line"> | <span class="type">Log</span></span><br><span class="line"> | <span class="type">Trace</span> <span class="keyword">of</span> <span class="built_in">int</span></span><br><span class="line"> | <span class="type">Trap</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* C 端的常量定义(seccomp_stubs.ml:58-73) *)</span></span><br><span class="line"><span class="keyword">module</span> <span class="type">Constants</span> = <span class="keyword">struct</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_allow = <span class="number">0x7fff0000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_kill_thread = <span class="number">0x00000000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_kill_process = <span class="number">0x00000000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_trap = <span class="number">0x00030000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_log = <span class="number">0x7ffc0000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_errno code = </span><br><span class="line"> <span class="type">Int32</span>.(logor <span class="number">0x00050000l</span> (logand (of_int code) <span class="number">0x0000ffffl</span>))</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 转换函数(seccomp.ml:60-75) *)</span></span><br><span class="line"><span class="keyword">let</span> action_to_uint32 = <span class="keyword">function</span></span><br><span class="line"> | <span class="type">Allow</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_allow</span><br><span class="line"> | <span class="type">KillThread</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_kill_thread</span><br><span class="line"> | <span class="type">KillProcess</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_kill_process</span><br><span class="line"> | <span class="type">Trap</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_trap</span><br><span class="line"> | <span class="type">Log</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_log</span><br><span class="line"> | <span class="type">Errno</span> code -&gt; int32_to_uint32 (<span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_errno code)</span><br><span class="line"> | <span class="type">Trace</span> code -&gt; int32_to_uint32 (<span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_trace code)</span><br></pre></td></tr></table></figure>
+<p>这种模式的优势:</p>
+<ul>
+<li>OCaml 端使用类型安全的变体</li>
+<li>C 端使用整数常量</li>
+<li>转换函数处理映射</li>
+<li>编译器帮助捕获类型错误</li>
+</ul>
+<h3 id="错误处理模式"><a class="header-anchor" href="#错误处理模式">¶</a>错误处理模式</h3>
+<p>C 那边通常用返回值表示错误。OCaml 使用 result 类型,例如在 seccomp 的 binding 中我这么做:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> error =</span><br><span class="line"> | <span class="type">Init_failed</span></span><br><span class="line"> | <span class="type">Rule_add_failed</span> <span class="keyword">of</span> <span class="built_in">int</span></span><br><span class="line"> | <span class="type">Load_failed</span> <span class="keyword">of</span> <span class="built_in">int</span></span><br><span class="line"> | <span class="type">Unknown_syscall</span> <span class="keyword">of</span> <span class="built_in">string</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> init default_action =</span><br><span class="line"> <span class="keyword">let</span> ctx = <span class="type">Stubs</span>.<span class="type">Functions</span>.seccomp_init (action_to_uint32 default_action) <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">if</span> <span class="type">Ctypes</span>.ptr_compare ctx <span class="type">Ctypes</span>.null = <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> <span class="type">Error</span> <span class="type">Init_failed</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="type">Ok</span> ctx</span><br></pre></td></tr></table></figure>
+<h2 id="完整示例"><a class="header-anchor" href="#完整示例">¶</a>完整示例</h2>
+<p>让我们来看看一个完整的 <code>rule_add_conditional</code> 函数的实现:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> rule_add_conditional ctx action syscall cmps =</span><br><span class="line"> <span class="keyword">let</span> n = <span class="type">Array</span>.length cmps <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">if</span> n = <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> rule_add ctx action syscall</span><br><span class="line"> <span class="keyword">else</span> (</span><br><span class="line"> <span class="comment">(* 1. 分配 C 数组 *)</span></span><br><span class="line"> <span class="keyword">let</span> cmp_array = <span class="type">Ctypes</span>.allocate_n <span class="type">Stubs</span>.<span class="type">Types</span>.scmp_arg_cmp ~count:n <span class="keyword">in</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">(* 2. 填充每个结构体 *)</span></span><br><span class="line"> <span class="type">Array</span>.iteri</span><br><span class="line"> (<span class="keyword">fun</span> i cmp -&gt;</span><br><span class="line"> <span class="type">Stubs</span>.<span class="type">Types</span>.set_arg_cmp_values</span><br><span class="line"> !@(cmp_array +@ i) <span class="comment">(* 解引用第 i 个元素 *)</span></span><br><span class="line"> cmp.arg</span><br><span class="line"> (cmp_op_to_int cmp.op)</span><br><span class="line"> cmp.datum_a</span><br><span class="line"> cmp.datum_b)</span><br><span class="line"> cmps;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">(* 3. 调用 C 函数 *)</span></span><br><span class="line"> <span class="keyword">let</span> result =</span><br><span class="line"> <span class="type">Stubs</span>.<span class="type">Functions</span>.seccomp_rule_add_array</span><br><span class="line"> ctx</span><br><span class="line"> (action_to_uint32 action)</span><br><span class="line"> syscall</span><br><span class="line"> (<span class="type">Unsigned</span>.<span class="type">UInt</span>.of_int n)</span><br><span class="line"> cmp_array</span><br><span class="line"> <span class="keyword">in</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">(* 4. 检查结果 *)</span></span><br><span class="line"> <span class="keyword">if</span> result &lt; <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> <span class="type">Error</span> (<span class="type">Rule_add_failed</span> result)</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="type">Ok</span> <span class="literal">()</span></span><br><span class="line"> )</span><br></pre></td></tr></table></figure>
+<ul>
+<li>数组分配: <code>allocate_n</code> 分配 <code>n</code> 个 <code>scmp_arg_cmp</code> 结构体的空间</li>
+<li>结构体填充:
+<ul>
+<li><code>cmp_array +@ i</code> 获取第 i 个元素的指针</li>
+<li><code>!@</code> 解引用得到结构体</li>
+<li><code>set_arg_cmp_values</code> 填充字段</li>
+</ul>
+</li>
+<li>函数调用: 传递数组和长度给 C 函数</li>
+<li>返回值: C 函数返回负值表示错误</li>
+</ul>
+<p>对应的 C API:</p>
+<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">seccomp_rule_add_array</span><span class="params">(scmp_filter_ctx ctx, <span class="type">uint32_t</span> action,</span></span><br><span class="line"><span class="params"> <span class="type">int</span> syscall, <span class="type">unsigned</span> <span class="type">int</span> arg_cnt,</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="keyword">struct</span> scmp_arg_cmp *arg_array)</span>;</span><br></pre></td></tr></table></figure>
+<h3 id="双层架构-Binding"><a class="header-anchor" href="#双层架构-Binding">¶</a>双层架构 Binding</h3>
+<p>seccomp 绑定用了清晰的三层架构:</p>
+<ul>
+<li><a target="_blank" rel="noopener" href="http://seccomp.ml">seccomp.ml</a> / seccomp.mli 是顶层 API 纯粹的OCaml 风格的类型和函数</li>
+<li>seccomp_stubs.ml 绑定层,放 CTypes 绑定,类型和函数定义</li>
+</ul>
+<h3 id="模块组织"><a class="header-anchor" href="#模块组织">¶</a>模块组织</h3>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* seccomp_stubs.ml 的模块组织 *)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">module</span> <span class="type">Types</span> = <span class="keyword">struct</span> <span class="comment">(* C 类型定义 *)</span></span><br><span class="line"> <span class="keyword">type</span> scmp_filter_ctx = <span class="built_in">unit</span> <span class="type">Ctypes</span>.ptr</span><br><span class="line"> <span class="keyword">val</span> scmp_filter_ctx : ...</span><br><span class="line"> <span class="keyword">val</span> scmp_arg_cmp : ...</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">module</span> <span class="type">Constants</span> = <span class="keyword">struct</span> <span class="comment">(* C 常量 *)</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_allow = ...</span><br><span class="line"> <span class="keyword">let</span> scmp_cmp_ne = ...</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">module</span> <span class="type">Functions</span> = <span class="keyword">struct</span> <span class="comment">(* C 函数绑定 *)</span></span><br><span class="line"> <span class="keyword">let</span> seccomp_init = ...</span><br><span class="line"> <span class="keyword">let</span> seccomp_load = ...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>
+<p>类型,常量,函数分离。</p>
+<h2 id="常见问题"><a class="header-anchor" href="#常见问题">¶</a>常见问题</h2>
+<h3 id="1-内存泄漏"><a class="header-anchor" href="#1-内存泄漏">¶</a>1. 内存泄漏</h3>
+<p>C 分配的内存需要手动释放:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 始终提供 release 函数 *)</span></span><br><span class="line"><span class="keyword">let</span> release ctx = <span class="type">Stubs</span>.<span class="type">Functions</span>.seccomp_release ctx</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 使用 Fun.protect 确保清理 *)</span></span><br><span class="line"><span class="keyword">let</span> with_filter default_action f =</span><br><span class="line"> <span class="keyword">match</span> init default_action <span class="keyword">with</span></span><br><span class="line"> | <span class="type">Error</span> e -&gt; <span class="type">Error</span> e</span><br><span class="line"> | <span class="type">Ok</span> ctx -&gt;</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> <span class="keyword">let</span> result = f ctx <span class="keyword">in</span></span><br><span class="line"> release ctx;</span><br><span class="line"> result</span><br><span class="line"> <span class="keyword">with</span> e -&gt;</span><br><span class="line"> release ctx;</span><br><span class="line"> raise e</span><br></pre></td></tr></table></figure>
+<h3 id="2-类型不匹配"><a class="header-anchor" href="#2-类型不匹配">¶</a>2. 类型不匹配</h3>
+<p>OCaml 和 C 的类型表示不同:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 使用 Unsigned 模块的转换函数 *)</span></span><br><span class="line"><span class="keyword">let</span> int32_to_uint32 n = <span class="type">Unsigned</span>.<span class="type">UInt32</span>.of_int32 n</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 显式类型转换 *)</span></span><br><span class="line"><span class="keyword">let</span> c_uint = <span class="type">Unsigned</span>.<span class="type">UInt</span>.of_int <span class="number">42</span></span><br></pre></td></tr></table></figure>
+<h3 id="3-空指针检查"><a class="header-anchor" href="#3-空指针检查">¶</a>3. 空指针检查</h3>
+<p>C 函数可能返回 NULL:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 始终检查返回的指针 *)</span></span><br><span class="line"><span class="keyword">if</span> <span class="type">Ctypes</span>.ptr_compare ctx <span class="type">Ctypes</span>.null = <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> <span class="type">Error</span> <span class="type">Init_failed</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> <span class="type">Ok</span> ctx</span><br></pre></td></tr></table></figure>
+<h3 id="4-字符串所有权"><a class="header-anchor" href="#4-字符串所有权">¶</a>4. 字符串所有权</h3>
+<p>C 返回的字符串生命周期不确定:</p>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">(* 如果 C 返回需要释放的字符串,使用 string 并复制 *)</span></span><br><span class="line"><span class="keyword">let</span> result = <span class="type">Bytes</span>.of_string c_string <span class="keyword">in</span></span><br><span class="line"><span class="comment">(* 释放 C 字符串 *)</span></span><br><span class="line"><span class="type">C</span>.free c_string_ptr;</span><br><span class="line"><span class="type">Bytes</span>.to_string result</span><br></pre></td></tr></table></figure>
+<h2 id="调试技巧"><a class="header-anchor" href="#调试技巧">¶</a>调试技巧</h2>
+<h3 id="1-检查库加载"><a class="header-anchor" href="#1-检查库加载">¶</a>1. 检查库加载</h3>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="literal">()</span> =</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> <span class="keyword">let</span> _ = <span class="type">Dl</span>.dlopen ~filename:<span class="string">&quot;libseccomp.so.2&quot;</span> ~flags:[ <span class="type">Dl</span>.<span class="type">RTLD_NOW</span> ] <span class="keyword">in</span></span><br><span class="line"> print_endline <span class="string">&quot;Library loaded successfully&quot;</span></span><br><span class="line"> <span class="keyword">with</span></span><br><span class="line"> | <span class="type">Dl</span>.<span class="type">DL_error</span> msg -&gt;</span><br><span class="line"> <span class="type">Printf</span>.eprintf <span class="string">&quot;Failed to load library: %s\n&quot;</span> msg</span><br></pre></td></tr></table></figure>
+<h3 id="2-检查函数绑定"><a class="header-anchor" href="#2-检查函数绑定">¶</a>2. 检查函数绑定</h3>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="literal">()</span> =</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> <span class="keyword">let</span> _ = <span class="type">Foreign</span>.foreign ~from:libseccomp <span class="string">&quot;seccomp_init&quot;</span> </span><br><span class="line"> (<span class="type">Ctypes</span>.uint32_t @-&gt; returning <span class="type">Ctypes</span>.ptr <span class="type">Ctypes</span>.void) <span class="keyword">in</span></span><br><span class="line"> print_endline <span class="string">&quot;Function bound successfully&quot;</span></span><br><span class="line"> <span class="keyword">with</span></span><br><span class="line"> | _ -&gt;</span><br><span class="line"> print_endline <span class="string">&quot;Function binding failed&quot;</span></span><br></pre></td></tr></table></figure>
+<h3 id="3-检查结构体大小"><a class="header-anchor" href="#3-检查结构体大小">¶</a>3. 检查结构体大小</h3>
+<figure class="highlight ocaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="literal">()</span> =</span><br><span class="line"> <span class="keyword">let</span> s = structure <span class="string">&quot;scmp_arg_cmp&quot;</span> <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _ = field s <span class="string">&quot;arg&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> seal s;</span><br><span class="line"> <span class="type">Printf</span>.printf <span class="string">&quot;Struct size: %d\n&quot;</span> (sizeof s)</span><br></pre></td></tr></table></figure>
+<h2 id="参考资源"><a class="header-anchor" href="#参考资源">¶</a>参考资源</h2>
+<ul>
+<li>CTypes 官方文档: <a target="_blank" rel="noopener" href="https://github.com/yallop/ocaml-ctypes">https://github.com/yallop/ocaml-ctypes</a></li>
+<li>CTypes 教程: <a target="_blank" rel="noopener" href="https://github.com/yallop/ocaml-ctypes/wiki/ctypes-tutorial">https://github.com/yallop/ocaml-ctypes/wiki/ctypes-tutorial</a></li>
+<li>CTypes Wiki: <a target="_blank" rel="noopener" href="https://github.com/yallop/ocaml-ctypes/wiki">https://github.com/yallop/ocaml-ctypes/wiki</a></li>
+<li>libseccomp 文档: <a target="_blank" rel="noopener" href="https://man7.org/linux/man-pages/man3/seccomp.3.html">https://man7.org/linux/man-pages/man3/seccomp.3.html</a></li>
+<li>bonding 项目: <a target="_blank" rel="noopener" href="https://github.com/muqiuhan/bonding">https://github.com/muqiuhan/bonding</a></li>
+</ul>
+
+</div>
+
+<script>
+ window.onload = detectors();
+</script>
+ <div class="post-footer">
+ <div class="h-line-primary"></div>
+ <nav class="post-nav">
+ <div class="prev-item">
+
+ </div>
+ <div class="next-item">
+
+ <div class="icon arrow-right"></div>
+ <div class="post-link">
+ <a href="/2026/03/21/%E5%B0%8F%E8%B0%88-NestJS-Zod/">Next</a>
+ </div>
+
+ </div>
+ </nav>
+</div>
+
+
+ <div class="post-comment">
+
+
+
+
+
+
+
+</div>
+
+
+</article>
+ </div>
+ </div>
+
+ <div class="footer">
+ <div class="flex-container">
+ <div class="footer-text">
+
+
+ 韩暮秋 |
+
+
+ 希望路过的人可以添点柴火让这里暖和点
+
+ </div>
+ </div>
+</div>
+
+
+ </div>
+
+ <script src="/js/mermaid-zoom.js"></script>
+
+ </body>
+</html>
diff --git a/archives/2026/03/index.html b/archives/2026/03/index.html
index 93699f4d..09eb5047 100644
--- a/archives/2026/03/index.html
+++ b/archives/2026/03/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2026/03/22/%E5%9C%A8-OCaml-%E4%B8%AD%E9%80%9A%E8%BF%87-CTypes-%E7%BB%91%E5%AE%9A-libseccomp/">
+
+ 在 OCaml 中通过 CTypes 绑定 libseccomp
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 22, 2026</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2026/03/21/%E5%B0%8F%E8%B0%88-NestJS-Zod/">
小谈 NestJS Zod
diff --git a/archives/2026/index.html b/archives/2026/index.html
index a6800f6c..ab28d2fc 100644
--- a/archives/2026/index.html
+++ b/archives/2026/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2026/03/22/%E5%9C%A8-OCaml-%E4%B8%AD%E9%80%9A%E8%BF%87-CTypes-%E7%BB%91%E5%AE%9A-libseccomp/">
+
+ 在 OCaml 中通过 CTypes 绑定 libseccomp
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 22, 2026</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2026/03/21/%E5%B0%8F%E8%B0%88-NestJS-Zod/">
小谈 NestJS Zod
diff --git a/archives/index.html b/archives/index.html
index 1d5489da..82e0edb9 100644
--- a/archives/index.html
+++ b/archives/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2026/03/22/%E5%9C%A8-OCaml-%E4%B8%AD%E9%80%9A%E8%BF%87-CTypes-%E7%BB%91%E5%AE%9A-libseccomp/">
+
+ 在 OCaml 中通过 CTypes 绑定 libseccomp
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 22, 2026</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2026/03/21/%E5%B0%8F%E8%B0%88-NestJS-Zod/">
小谈 NestJS Zod
@@ -322,24 +340,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2025/12/28/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%85%AB%E6%97%A5/">
-
- 二〇二五年十二月二十八日
-
- </a>
- </div>
-
- <span class="post-date">Dec 28, 2025</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/10/index.html b/archives/page/10/index.html
index 457d1bc7..609d4b3b 100644
--- a/archives/page/10/index.html
+++ b/archives/page/10/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/06/06/NET-AOT-%E4%B8%8B%E7%9A%84-F-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%BA%93%E9%80%89%E6%8B%A9/">
+
+ .NET AOT 下的 F# 命令行参数解析库选择
+
+ </a>
+ </div>
+
+ <span class="post-date">Jun 6, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/05/20/Rescript-genType-import-%E9%97%AE%E9%A2%98/">
Rescript @genType import 问题
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/01/25/%E6%80%A5%E8%AF%8A%E4%B8%8E%E7%81%BE%E9%9A%BE%E5%8C%BB%E5%AD%A6-%E7%83%A7%E4%BC%A4/">
-
- 急诊与灾难医学--烧伤
-
- </a>
- </div>
-
- <span class="post-date">Jan 25, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/11/index.html b/archives/page/11/index.html
index 11622402..3160d770 100644
--- a/archives/page/11/index.html
+++ b/archives/page/11/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/01/25/%E6%80%A5%E8%AF%8A%E4%B8%8E%E7%81%BE%E9%9A%BE%E5%8C%BB%E5%AD%A6-%E7%83%A7%E4%BC%A4/">
+
+ 急诊与灾难医学--烧伤
+
+ </a>
+ </div>
+
+ <span class="post-date">Jan 25, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/01/23/G-Machine/">
G-Machine
@@ -322,24 +340,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/11/21/Adolescent-Onset-of-Acute-Heart-Failure/">
-
- Adolescent Onset of Acute Heart Failure
-
- </a>
- </div>
-
- <span class="post-date">Nov 21, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/12/index.html b/archives/page/12/index.html
index 5c2c5340..a6d2c2bd 100644
--- a/archives/page/12/index.html
+++ b/archives/page/12/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/11/21/Adolescent-Onset-of-Acute-Heart-Failure/">
+
+ Adolescent Onset of Acute Heart Failure
+
+ </a>
+ </div>
+
+ <span class="post-date">Nov 21, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/11/16/Fever-of-Unknown-Origin/">
Fever of Unknown Origin
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/10/12/%E8%BF%87%E5%BA%A6%E7%96%B2%E5%8A%B3%E7%9A%84%E5%85%AD%E7%A7%8D%E8%A1%A8%E7%8E%B0/">
-
- 过度疲劳的六种表现
-
- </a>
- </div>
-
- <span class="post-date">Oct 12, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/13/index.html b/archives/page/13/index.html
index 30460345..072925e4 100644
--- a/archives/page/13/index.html
+++ b/archives/page/13/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/10/12/%E8%BF%87%E5%BA%A6%E7%96%B2%E5%8A%B3%E7%9A%84%E5%85%AD%E7%A7%8D%E8%A1%A8%E7%8E%B0/">
+
+ 过度疲劳的六种表现
+
+ </a>
+ </div>
+
+ <span class="post-date">Oct 12, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/10/05/v8%E4%B8%AD%E7%9A%84Number-toString/">
v8中的Number.toString()
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/07/21/WiscKey-Separating-Keys-from-Values-in-SSD-conscious-Storage/">
-
- WiscKey: Separating Keys from Values in SSD-conscious Storage
-
- </a>
- </div>
-
- <span class="post-date">Jul 21, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/14/index.html b/archives/page/14/index.html
index b748ece4..1f4dddb7 100644
--- a/archives/page/14/index.html
+++ b/archives/page/14/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/07/21/WiscKey-Separating-Keys-from-Values-in-SSD-conscious-Storage/">
+
+ WiscKey: Separating Keys from Values in SSD-conscious Storage
+
+ </a>
+ </div>
+
+ <span class="post-date">Jul 21, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/06/29/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%85%AD%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B9%9D%E6%97%A5/">
二〇二三年六月二十九日
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/05/01/Rust-%E8%99%9A%E8%A1%A8%E5%B8%83%E5%B1%80%E8%A7%84%E5%88%99%E4%BB%8B%E7%BB%8D/">
-
- Rust 虚表布局规则介绍
-
- </a>
- </div>
-
- <span class="post-date">May 1, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/15/index.html b/archives/page/15/index.html
index 4e2d5225..ce814993 100644
--- a/archives/page/15/index.html
+++ b/archives/page/15/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/05/01/Rust-%E8%99%9A%E8%A1%A8%E5%B8%83%E5%B1%80%E8%A7%84%E5%88%99%E4%BB%8B%E7%BB%8D/">
+
+ Rust 虚表布局规则介绍
+
+ </a>
+ </div>
+
+ <span class="post-date">May 1, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/04/30/TypeScript-With-Rust-Errors-No-Try-Catch-Heresy/">
TypeScript With Rust Errors, No Try Catch, Heresy
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/03/01/%E7%BA%AF%E7%89%9B%E5%A5%B6%E5%8F%AF%E4%BB%A5%E5%8A%A0%E7%82%B9%E5%95%A5/">
-
- 纯牛奶可以加点啥
-
- </a>
- </div>
-
- <span class="post-date">Mar 1, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/16/index.html b/archives/page/16/index.html
index 6e2d5b28..999466e3 100644
--- a/archives/page/16/index.html
+++ b/archives/page/16/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/03/01/%E7%BA%AF%E7%89%9B%E5%A5%B6%E5%8F%AF%E4%BB%A5%E5%8A%A0%E7%82%B9%E5%95%A5/">
+
+ 纯牛奶可以加点啥
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 1, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/02/26/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%85%AD%E6%97%A5/">
二零二三年二月二十六日
@@ -322,24 +340,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2022/10/01/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E5%8D%81%E6%9C%88%E4%B8%80%E6%97%A5/">
-
- 二零二二年十月一日
-
- </a>
- </div>
-
- <span class="post-date">Oct 1, 2022</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/17/index.html b/archives/page/17/index.html
index 276c58f0..b274ed6e 100644
--- a/archives/page/17/index.html
+++ b/archives/page/17/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2022/10/01/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E5%8D%81%E6%9C%88%E4%B8%80%E6%97%A5/">
+
+ 二零二二年十月一日
+
+ </a>
+ </div>
+
+ <span class="post-date">Oct 1, 2022</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2022/09/22/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%97%A5/">
二零二二年九月二十二日
@@ -324,24 +342,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2020/12/09/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%B9%9D%E6%97%A5/">
-
- 二零二零年十二月九日
-
- </a>
- </div>
-
- <span class="post-date">Dec 9, 2020</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/18/index.html b/archives/page/18/index.html
index 3ea24473..5ef3da80 100644
--- a/archives/page/18/index.html
+++ b/archives/page/18/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2020/12/09/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%B9%9D%E6%97%A5/">
+
+ 二零二零年十二月九日
+
+ </a>
+ </div>
+
+ <span class="post-date">Dec 9, 2020</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2020/12/08/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E5%85%AB%E6%97%A5/">
二零二零年十二月八日
diff --git a/archives/page/2/index.html b/archives/page/2/index.html
index 8b7b2dfd..11217048 100644
--- a/archives/page/2/index.html
+++ b/archives/page/2/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2025/12/28/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%85%AB%E6%97%A5/">
+
+ 二〇二五年十二月二十八日
+
+ </a>
+ </div>
+
+ <span class="post-date">Dec 28, 2025</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2025/12/27/%E6%84%8F%E5%90%91%E6%80%A7%E6%B2%BB%E7%96%97%E5%8E%9F%E5%88%99/">
意向性治疗原则
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2025/11/08/%E5%90%8E%E6%91%87%E7%9A%84-Graphic-EQ-APO-Presets/">
-
- 后摇的 Graphic EQ APO Presets
-
- </a>
- </div>
-
- <span class="post-date">Nov 8, 2025</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/3/index.html b/archives/page/3/index.html
index 2ac71430..6df4df96 100644
--- a/archives/page/3/index.html
+++ b/archives/page/3/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2025/11/08/%E5%90%8E%E6%91%87%E7%9A%84-Graphic-EQ-APO-Presets/">
+
+ 后摇的 Graphic EQ APO Presets
+
+ </a>
+ </div>
+
+ <span class="post-date">Nov 8, 2025</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2025/11/07/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E4%B8%80%E6%9C%88%E4%B8%83%E6%97%A5/">
二〇二五年十一月七日
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2025/09/24/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%BA%8C%E5%8D%81%E5%9B%9B%E6%97%A5/">
-
- 二〇二五年九月二十四日
-
- </a>
- </div>
-
- <span class="post-date">Sep 24, 2025</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/4/index.html b/archives/page/4/index.html
index 09dcb168..d805a8c6 100644
--- a/archives/page/4/index.html
+++ b/archives/page/4/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2025/09/24/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%BA%8C%E5%8D%81%E5%9B%9B%E6%97%A5/">
+
+ 二〇二五年九月二十四日
+
+ </a>
+ </div>
+
+ <span class="post-date">Sep 24, 2025</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2025/09/09/database-fk-design-in-clinical-medicine/">
在临床医疗领域中的数据库外键设计取舍
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2025/06/10/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%85%AD%E6%9C%88%E5%8D%81%E6%97%A5/">
-
- 二〇二五年六月十日
-
- </a>
- </div>
-
- <span class="post-date">Jun 10, 2025</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/5/index.html b/archives/page/5/index.html
index 4d4ac35b..6ddd0648 100644
--- a/archives/page/5/index.html
+++ b/archives/page/5/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2025/06/10/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%85%AD%E6%9C%88%E5%8D%81%E6%97%A5/">
+
+ 二〇二五年六月十日
+
+ </a>
+ </div>
+
+ <span class="post-date">Jun 10, 2025</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2025/05/27/vertical-slicing-practice/">
Vertical slicing 在开发中的实践探索
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2025/03/30/F-%E9%87%8D%E8%BD%BD%E8%A7%A3%E6%9E%90%E9%97%AE%E9%A2%98/">
-
- F# 重载解析问题
-
- </a>
- </div>
-
- <span class="post-date">Mar 30, 2025</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/6/index.html b/archives/page/6/index.html
index 11c07bcd..47c0af07 100644
--- a/archives/page/6/index.html
+++ b/archives/page/6/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2025/03/30/F-%E9%87%8D%E8%BD%BD%E8%A7%A3%E6%9E%90%E9%97%AE%E9%A2%98/">
+
+ F# 重载解析问题
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 30, 2025</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2025/03/30/libdecor-gtk-WARNING-Failed-to-initialize-GTK/">
libdecor-gtk-WARNING Failed to initialize GTK in VSCode on Wayland
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2025/02/19/Repository-pattern-in-Typescript/">
-
- Repository pattern in Typescript
-
- </a>
- </div>
-
- <span class="post-date">Feb 19, 2025</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/7/index.html b/archives/page/7/index.html
index b92a33d0..017d06b5 100644
--- a/archives/page/7/index.html
+++ b/archives/page/7/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2025/02/19/Repository-pattern-in-Typescript/">
+
+ Repository pattern in Typescript
+
+ </a>
+ </div>
+
+ <span class="post-date">Feb 19, 2025</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2025/02/18/Prisma-%E5%85%B3%E7%B3%BB%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84-Self-relations/">
Prisma 关系型数据库的 Self-relations
@@ -322,24 +340,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/10/18/10-Tips-for-Productive-FSharp-Scripting/">
-
- 10 Tips for Productive FSharp Scripting
-
- </a>
- </div>
-
- <span class="post-date">Oct 18, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/8/index.html b/archives/page/8/index.html
index d79d29db..f7423f24 100644
--- a/archives/page/8/index.html
+++ b/archives/page/8/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/10/18/10-Tips-for-Productive-FSharp-Scripting/">
+
+ 10 Tips for Productive FSharp Scripting
+
+ </a>
+ </div>
+
+ <span class="post-date">Oct 18, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/10/18/Building-custom-fibers-library-in-FSharp/">
Building custom fibers library in FSharp
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/09/07/%E7%BB%99%E5%AD%A9%E5%AD%90%E4%BB%AC%E7%9A%84%E8%AF%9D/">
-
- 什么玩意儿都是
-
- </a>
- </div>
-
- <span class="post-date">Sep 7, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/9/index.html b/archives/page/9/index.html
index e7d19ee2..45f09fbb 100644
--- a/archives/page/9/index.html
+++ b/archives/page/9/index.html
@@ -165,6 +165,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/09/07/%E7%BB%99%E5%AD%A9%E5%AD%90%E4%BB%AC%E7%9A%84%E8%AF%9D/">
+
+ 什么玩意儿都是
+
+ </a>
+ </div>
+
+ <span class="post-date">Sep 7, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">
二〇二四年九月一日
@@ -320,24 +338,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/06/06/NET-AOT-%E4%B8%8B%E7%9A%84-F-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%BA%93%E9%80%89%E6%8B%A9/">
-
- .NET AOT 下的 F# 命令行参数解析库选择
-
- </a>
- </div>
-
- <span class="post-date">Jun 6, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/search.xml b/search.xml
index fe5473fd..e135aa5e 100644
--- a/search.xml
+++ b/search.xml
@@ -1102,6 +1102,40 @@ await prisma.$transaction(async tx =&gt; &#123;
</tags>
</entry>
<entry>
+ <title>OCaml Core.Int.pow 的实现</title>
+ <url>/2023/10/21/OCaml-Core-Int-pow-%E7%9A%84%E5%AE%9E%E7%8E%B0/</url>
+ <content><![CDATA[<p>Core内部直接引用的Base的实现, Base的实现在 <a href="https://github.com/janestreet/base/blob/494a0876168d24cda695cbb9a3d86ad8d1eb97d8/src/int_math.ml#L11-L18">src/int_math.ml</a> 中:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> int_pow base exponent =</span><br><span class="line"> <span class="keyword">if</span> exponent &lt; <span class="number">0</span> <span class="keyword">then</span> negative_exponent <span class="literal">()</span>;</span><br><span class="line"> <span class="keyword">if</span> abs base &gt; <span class="number">1</span></span><br><span class="line"> &amp;&amp; (exponent &gt; <span class="number">63</span></span><br><span class="line"> || abs base &gt; <span class="type">Pow_overflow_bounds</span>.int_positive_overflow_bounds.(exponent))</span><br><span class="line"> <span class="keyword">then</span> overflow <span class="literal">()</span>;</span><br><span class="line"> int_math_int_pow base exponent</span><br><span class="line">;;</span><br></pre></td></tr></table></figure>
+<p>其中 <code>int_math_int_pow()</code> 由 C 实现:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">external</span> int_math_int_pow : <span class="built_in">int</span> -&gt; <span class="built_in">int</span> -&gt; <span class="built_in">int</span> = <span class="string">&quot;Base_int_math_int_pow_stub&quot;</span> [@@noalloc]</span><br></pre></td></tr></table></figure>
+<p>其实现在 <a href="https://github.com/janestreet/base/blob/494a0876168d24cda695cbb9a3d86ad8d1eb97d8/src/int_math_stubs.c#L56-L92">src/int_math_stubs.c</a> 中:</p>
+<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int64_t</span> <span class="title function_">int_pow</span><span class="params">(<span class="type">int64_t</span> base, <span class="type">int64_t</span> exponent)</span> &#123;</span><br><span class="line"> <span class="type">int64_t</span> ret = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int64_t</span> mul[<span class="number">4</span>];</span><br><span class="line"> mul[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> mul[<span class="number">1</span>] = base;</span><br><span class="line"> mul[<span class="number">3</span>] = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (exponent != <span class="number">0</span>) &#123;</span><br><span class="line"> mul[<span class="number">1</span>] *= mul[<span class="number">3</span>];</span><br><span class="line"> mul[<span class="number">2</span>] = mul[<span class="number">1</span>] * mul[<span class="number">1</span>];</span><br><span class="line"> mul[<span class="number">3</span>] = mul[<span class="number">2</span>] * mul[<span class="number">1</span>];</span><br><span class="line"> ret *= mul[exponent &amp; <span class="number">3</span>];</span><br><span class="line"> exponent &gt;&gt;= <span class="number">2</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
+<p>这是一个四分快速幂的实现,它是二分快速幂的一种变种。二分快速幂将指数分为两部分,然后递归地计算每一部分的结果。<br>
+而四分快速幂将指数分为四部分,然后递归地计算每一部分的结果。<br>
+这里通过将指数右移2位(相当于除以4)和使用位与操作来实现,进一步减少了乘法次数。</p>
+<p>主要步骤:</p>
+<ul>
+<li>初始化返回值ret为1,和一个包含4个元素的数组mul, mul[0]和mul[3]被初始化为1, mul[1]被初始化为基数</li>
+<li>当指数不为0时,执行循环, 在每次循环中,首先更新mul数组的值
+<blockquote>
+<p>mul[1]是基数和mul[3]的乘积,mul[2]是mul[1]的平方,mul[3]是mul[2]和基数的乘积</p>
+</blockquote>
+</li>
+<li>然后,将ret乘以mul数组中的一个元素, 这个元素的索引是指数和3的位与运算的结果
+<blockquote>
+<p>这样做的目的是为了选择正确的乘数,因为指数被分解为4的倍数</p>
+</blockquote>
+</li>
+<li>最后,将指数右移2位,相当于将指数除以4</li>
+<li>当指数变为0时,循环结束,返回ret</li>
+</ul>
+<p>这个实现的优点是它可以在对数时间内计算出幂运算,而且每次循环只需要4次乘法。这比标准的二分快速幂算法需要的乘法次数更少。</p>
+]]></content>
+ <tags>
+ <tag>Technique</tag>
+ </tags>
+ </entry>
+ <entry>
<title>OCaml News 2024-1</title>
<url>/2024/01/08/OCaml-News-2024-1/</url>
<content><![CDATA[<h2 id="News"><a class="header-anchor" href="#News">¶</a>News</h2>
@@ -1165,40 +1199,6 @@ await prisma.$transaction(async tx =&gt; &#123;
</tags>
</entry>
<entry>
- <title>OCaml Core.Int.pow 的实现</title>
- <url>/2023/10/21/OCaml-Core-Int-pow-%E7%9A%84%E5%AE%9E%E7%8E%B0/</url>
- <content><![CDATA[<p>Core内部直接引用的Base的实现, Base的实现在 <a href="https://github.com/janestreet/base/blob/494a0876168d24cda695cbb9a3d86ad8d1eb97d8/src/int_math.ml#L11-L18">src/int_math.ml</a> 中:</p>
-<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> int_pow base exponent =</span><br><span class="line"> <span class="keyword">if</span> exponent &lt; <span class="number">0</span> <span class="keyword">then</span> negative_exponent <span class="literal">()</span>;</span><br><span class="line"> <span class="keyword">if</span> abs base &gt; <span class="number">1</span></span><br><span class="line"> &amp;&amp; (exponent &gt; <span class="number">63</span></span><br><span class="line"> || abs base &gt; <span class="type">Pow_overflow_bounds</span>.int_positive_overflow_bounds.(exponent))</span><br><span class="line"> <span class="keyword">then</span> overflow <span class="literal">()</span>;</span><br><span class="line"> int_math_int_pow base exponent</span><br><span class="line">;;</span><br></pre></td></tr></table></figure>
-<p>其中 <code>int_math_int_pow()</code> 由 C 实现:</p>
-<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">external</span> int_math_int_pow : <span class="built_in">int</span> -&gt; <span class="built_in">int</span> -&gt; <span class="built_in">int</span> = <span class="string">&quot;Base_int_math_int_pow_stub&quot;</span> [@@noalloc]</span><br></pre></td></tr></table></figure>
-<p>其实现在 <a href="https://github.com/janestreet/base/blob/494a0876168d24cda695cbb9a3d86ad8d1eb97d8/src/int_math_stubs.c#L56-L92">src/int_math_stubs.c</a> 中:</p>
-<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int64_t</span> <span class="title function_">int_pow</span><span class="params">(<span class="type">int64_t</span> base, <span class="type">int64_t</span> exponent)</span> &#123;</span><br><span class="line"> <span class="type">int64_t</span> ret = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int64_t</span> mul[<span class="number">4</span>];</span><br><span class="line"> mul[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> mul[<span class="number">1</span>] = base;</span><br><span class="line"> mul[<span class="number">3</span>] = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (exponent != <span class="number">0</span>) &#123;</span><br><span class="line"> mul[<span class="number">1</span>] *= mul[<span class="number">3</span>];</span><br><span class="line"> mul[<span class="number">2</span>] = mul[<span class="number">1</span>] * mul[<span class="number">1</span>];</span><br><span class="line"> mul[<span class="number">3</span>] = mul[<span class="number">2</span>] * mul[<span class="number">1</span>];</span><br><span class="line"> ret *= mul[exponent &amp; <span class="number">3</span>];</span><br><span class="line"> exponent &gt;&gt;= <span class="number">2</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
-<p>这是一个四分快速幂的实现,它是二分快速幂的一种变种。二分快速幂将指数分为两部分,然后递归地计算每一部分的结果。<br>
-而四分快速幂将指数分为四部分,然后递归地计算每一部分的结果。<br>
-这里通过将指数右移2位(相当于除以4)和使用位与操作来实现,进一步减少了乘法次数。</p>
-<p>主要步骤:</p>
-<ul>
-<li>初始化返回值ret为1,和一个包含4个元素的数组mul, mul[0]和mul[3]被初始化为1, mul[1]被初始化为基数</li>
-<li>当指数不为0时,执行循环, 在每次循环中,首先更新mul数组的值
-<blockquote>
-<p>mul[1]是基数和mul[3]的乘积,mul[2]是mul[1]的平方,mul[3]是mul[2]和基数的乘积</p>
-</blockquote>
-</li>
-<li>然后,将ret乘以mul数组中的一个元素, 这个元素的索引是指数和3的位与运算的结果
-<blockquote>
-<p>这样做的目的是为了选择正确的乘数,因为指数被分解为4的倍数</p>
-</blockquote>
-</li>
-<li>最后,将指数右移2位,相当于将指数除以4</li>
-<li>当指数变为0时,循环结束,返回ret</li>
-</ul>
-<p>这个实现的优点是它可以在对数时间内计算出幂运算,而且每次循环只需要4次乘法。这比标准的二分快速幂算法需要的乘法次数更少。</p>
-]]></content>
- <tags>
- <tag>Technique</tag>
- </tags>
- </entry>
- <entry>
<title>OCaml News 2024-2</title>
<url>/2024/01/18/OCaml-News-2024-2/</url>
<content><![CDATA[<h2 id="News"><a class="header-anchor" href="#News">¶</a>News</h2>
@@ -2133,33 +2133,6 @@ A relation table (also sometimes called a <em>JOIN</em>, <em>link</em> or <em>pi
</tags>
</entry>
<entry>
- <title>Rescript and React Native</title>
- <url>/2024/01/22/Rescript-React-Native/</url>
- <content><![CDATA[<p>Rescript 对 React Native 的支持还算可以,但如今的 RN 已经是 Expo 的 RN 了,所以仍然有些落后,在这个 template 中,我测试了 rescript + rescript-react-native + expo + nativewind 的组合,可以跑起来:</p>
-<ul>
-<li><a href="https://github.com/muqiuhan/rescript-react-native-expo-nativewind-template">muqiuhan/rescript-react-native-expo-nativewind-template</a></li>
-</ul>
-<p>其中有两个问题,一是 <a href="https://github.com/rescript-bindings/expo">rescript-expo</a> 对 rescript v11 的兼容性,我通过简单的注释让其通过编译:</p>
-<ul>
-<li><a href="https://github.com/muqiuhan/expo/commit/918e9578f2a991450821760352c2445c4866f0d8">[Fix]: DocumentPicker type options</a></li>
-</ul>
-<p>二是对于 nativewind 的支持,编写的 binding 非常丑陋:</p>
-<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">type default_style = &#123;</span><br><span class="line"> className?: string,</span><br><span class="line"> children?: React.element,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@module(&quot;nativewind&quot;)</span><br><span class="line">external styled: React.component&lt;&#x27;a&gt; =&gt; React.component&lt;default_style&gt; = &quot;styled&quot;</span><br><span class="line"></span><br><span class="line">module StyledText = &#123;</span><br><span class="line"> let make = styled(ReactNative.Text.make)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">module StyledView = &#123;</span><br><span class="line"> let make = styled(ReactNative.View.make)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">module StyledImage = &#123;</span><br><span class="line"> type props = &#123;</span><br><span class="line"> source?: string,</span><br><span class="line"> ...default_style,</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> @module(&quot;nativewind&quot;)</span><br><span class="line"> external styled: React.component&lt;&#x27;a&gt; =&gt; React.component&lt;props&gt; = &quot;styled&quot;</span><br><span class="line"></span><br><span class="line"> let make: React.component&lt;props&gt; = styled(ReactNative.Image.make)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
-<p>而这似乎并没有好的解决方案,理想中的实现应该是:</p>
-<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">module Styled = (Component: &#123;</span><br><span class="line"> type params // or type params = &#x27;a</span><br><span class="line"> let make: React.component&lt;props&gt;</span><br><span class="line">&#125;) =&gt; &#123;</span><br><span class="line"> type props = &#123;</span><br><span class="line"> ...Component.props, // This can&#x27;t work as it is not defined as record</span><br><span class="line"> className?: string</span><br><span class="line"> &#125;</span><br><span class="line"> @module(&quot;nativewind&quot;)</span><br><span class="line"> external styled: React.component&lt;Component.props&gt; =&gt; React.component&lt;props&gt; = &quot;styled&quot;</span><br><span class="line"></span><br><span class="line"> let make: React.component&lt;props&gt; = styled(Component.make)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
-<p>或者退一步:</p>
-<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">type styledProps = &#123;</span><br><span class="line"> className?: string,</span><br><span class="line"> // ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@module(&quot;nativewind&quot;)</span><br><span class="line"> external styled: React.component&lt;&#x27;a&gt; =&gt; React.component&lt;&#x27;b&gt; = &quot;styled&quot;</span><br><span class="line"></span><br><span class="line">module StyledText = &#123;</span><br><span class="line"> type props = &#123;</span><br><span class="line"> ...ReactNative.Text.props,</span><br><span class="line"> ...styledProps</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> let make: React.component&lt;props&gt; = styled(ReactNative.Text.make)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
-<p>但这在当前的 rescript 中,根本无法实现。</p>
-<p>详细的讨论看这个帖子:</p>
-<ul>
-<li><a href="https://forum.rescript-lang.org/t/how-to-use-nativewind-in-rescript-react-native/5005">How to use Nativewind in Rescript React Native</a></li>
-</ul>
-]]></content>
- <tags>
- <tag>Technique</tag>
- </tags>
- </entry>
- <entry>
<title>Repository pattern in Typescript</title>
<url>/2025/02/19/Repository-pattern-in-Typescript/</url>
<content><![CDATA[<blockquote>
@@ -2199,6 +2172,33 @@ A relation table (also sometimes called a <em>JOIN</em>, <em>link</em> or <em>pi
</tags>
</entry>
<entry>
+ <title>Rescript and React Native</title>
+ <url>/2024/01/22/Rescript-React-Native/</url>
+ <content><![CDATA[<p>Rescript 对 React Native 的支持还算可以,但如今的 RN 已经是 Expo 的 RN 了,所以仍然有些落后,在这个 template 中,我测试了 rescript + rescript-react-native + expo + nativewind 的组合,可以跑起来:</p>
+<ul>
+<li><a href="https://github.com/muqiuhan/rescript-react-native-expo-nativewind-template">muqiuhan/rescript-react-native-expo-nativewind-template</a></li>
+</ul>
+<p>其中有两个问题,一是 <a href="https://github.com/rescript-bindings/expo">rescript-expo</a> 对 rescript v11 的兼容性,我通过简单的注释让其通过编译:</p>
+<ul>
+<li><a href="https://github.com/muqiuhan/expo/commit/918e9578f2a991450821760352c2445c4866f0d8">[Fix]: DocumentPicker type options</a></li>
+</ul>
+<p>二是对于 nativewind 的支持,编写的 binding 非常丑陋:</p>
+<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">type default_style = &#123;</span><br><span class="line"> className?: string,</span><br><span class="line"> children?: React.element,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@module(&quot;nativewind&quot;)</span><br><span class="line">external styled: React.component&lt;&#x27;a&gt; =&gt; React.component&lt;default_style&gt; = &quot;styled&quot;</span><br><span class="line"></span><br><span class="line">module StyledText = &#123;</span><br><span class="line"> let make = styled(ReactNative.Text.make)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">module StyledView = &#123;</span><br><span class="line"> let make = styled(ReactNative.View.make)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">module StyledImage = &#123;</span><br><span class="line"> type props = &#123;</span><br><span class="line"> source?: string,</span><br><span class="line"> ...default_style,</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> @module(&quot;nativewind&quot;)</span><br><span class="line"> external styled: React.component&lt;&#x27;a&gt; =&gt; React.component&lt;props&gt; = &quot;styled&quot;</span><br><span class="line"></span><br><span class="line"> let make: React.component&lt;props&gt; = styled(ReactNative.Image.make)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
+<p>而这似乎并没有好的解决方案,理想中的实现应该是:</p>
+<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">module Styled = (Component: &#123;</span><br><span class="line"> type params // or type params = &#x27;a</span><br><span class="line"> let make: React.component&lt;props&gt;</span><br><span class="line">&#125;) =&gt; &#123;</span><br><span class="line"> type props = &#123;</span><br><span class="line"> ...Component.props, // This can&#x27;t work as it is not defined as record</span><br><span class="line"> className?: string</span><br><span class="line"> &#125;</span><br><span class="line"> @module(&quot;nativewind&quot;)</span><br><span class="line"> external styled: React.component&lt;Component.props&gt; =&gt; React.component&lt;props&gt; = &quot;styled&quot;</span><br><span class="line"></span><br><span class="line"> let make: React.component&lt;props&gt; = styled(Component.make)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
+<p>或者退一步:</p>
+<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">type styledProps = &#123;</span><br><span class="line"> className?: string,</span><br><span class="line"> // ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@module(&quot;nativewind&quot;)</span><br><span class="line"> external styled: React.component&lt;&#x27;a&gt; =&gt; React.component&lt;&#x27;b&gt; = &quot;styled&quot;</span><br><span class="line"></span><br><span class="line">module StyledText = &#123;</span><br><span class="line"> type props = &#123;</span><br><span class="line"> ...ReactNative.Text.props,</span><br><span class="line"> ...styledProps</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> let make: React.component&lt;props&gt; = styled(ReactNative.Text.make)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
+<p>但这在当前的 rescript 中,根本无法实现。</p>
+<p>详细的讨论看这个帖子:</p>
+<ul>
+<li><a href="https://forum.rescript-lang.org/t/how-to-use-nativewind-in-rescript-react-native/5005">How to use Nativewind in Rescript React Native</a></li>
+</ul>
+]]></content>
+ <tags>
+ <tag>Technique</tag>
+ </tags>
+ </entry>
+ <entry>
<title>Rescript @genType import 问题</title>
<url>/2024/05/20/Rescript-genType-import-%E9%97%AE%E9%A2%98/</url>
<content><![CDATA[<p>Rescript 11 之后,<code>@genType</code> 被合并进编译器,无需任何依赖就能使用,当在 Rescript 中 <code>@genType</code> 了使用某些 Rescript built-in 的基本类型时,可能会生成有问题的 <code>import</code> 相关代码,例如:</p>
@@ -4377,6 +4377,24 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的�
</categories>
</entry>
<entry>
+ <title>二〇二五年十月三十日</title>
+ <url>/2025/10/30/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E6%9C%88%E4%B8%89%E5%8D%81%E6%97%A5/</url>
+ <content><![CDATA[<p>那是一个无云、风也很小的傍晚。天光从浅到深慢慢往你这边退,像有人慢慢把你从白昼里抱出来。街角的花店门口站着一只猫,它看着路过的车,耳朵偶尔动一下。屋檐下有风铃,轻轻的,叮的一声,时间像也跟着响了一下。</p>
+<p>花店里里有一支黄玫瑰,花瓣像被烫过,从橙黄到金黄,层层叠叠,在傍晚的空气里慢慢散开一层暖色。它有甜味,甜里还带着一点苦,味道朦胧的像你在窗下读书,书页翻过时有阳光落下来,像你走过老城的石板路,脚下的石头被晒得很暖。</p>
+<p>我记得那张粗糙的柜台,记得老板的手掌上有花叶的汁液。记得那种 “被允许站在这里” 的轻松。它没有要你改变什么,也没有给你安排一个更好的未来。它只是让你看见,一个傍晚可以用来想事,一条路可以一直往前延伸而不追问终点是什么。</p>
+<p>从那以后,我偶尔还会闻到那种味道。公交车拐弯的时候,风从窗缝里钻进来,地铁门打开的时候,站台上一阵匆忙的脚步后,忽然静下来的那一刻。我不知道它为什么会出现,也不刻意寻找。它像朋友一样,来了就坐一会儿,走了也不会留下地址。</p>
+<p>然后我遇见了你。</p>
+<p>起初我们并肩走在一条街上,街灯刚亮,影子从脚边拖到墙根。你说话的时候,声音不急也不慢。我听见了你的过去,但不是像在听故事那样去比较谁更动人,我只是听着你。你给我看了你的手心,那上面有被太阳晒过后的浅色纹理,也有你习惯把指甲剪得很短的认真。我心里有一种安静,它不是从谁那里借来的,而是像从很远的地方自己走来的。</p>
+<p>我知道你不是她,她是一种很亮的影子,只能在黑暗里存在。而你会在这里,在光亮里也在风里,在你的沉默里也在你的笑声里。时间不会让你跟她合在一起,时间只会让你在我旁边,成为一个可以站着的人,一个愿意陪我走过同一条街、同一种气味、同一种阳光的人。</p>
+<p>于是我想给你那朵黄玫瑰的记忆。不是因为我想要你变成她的样子,而是因为我在你身上看见了自己喜欢的那种安静和从容。我想让你在今后的某一天,当公交门打开,站台忽然安静下来,当你走在一条熟悉的路上,风从树梢里穿过,当你坐在窗边,光从书页上滑过去的时候,能闻到那种味道,能想起那一刻的我们,我们在傍晚的风里站着,既不躲藏也不假装,我们把时间握在手里,像握着一朵温柔得让人不需要理由的花。</p>
+<p>如果你愿意,我可以每天傍晚经过花店门口,我们可以把那种味道带回家,用一只透明的瓶子装起来,不需要解释它来自哪里,也不需要把它和任何过去比较。它只会在你需要的时候出现,像一个老朋友,提醒你,不是每一次都要变成别人,不是每一次都需要证明你已经忘记。你可以是现在的你,我可以是现在的我。我们可以在同一个黄昏里站一会儿,看看天空从浅到深,看看风从我们旁边走过,看看影子在我们的脚边拉长。</p>
+<p>黄玫瑰的味道不会要求我们变成谁,它只会给我们一点轻的力气,让我们在时间里往前走走。我把这份力气给你,也给我自己,然后我们就接着往前走,带着这种不喧哗的光。</p>
+]]></content>
+ <categories>
+ <category>gallery</category>
+ </categories>
+ </entry>
+ <entry>
<title>二〇二五年十月十九日</title>
<url>/2025/10/19/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E6%9C%88%E5%8D%81%E4%B9%9D%E6%97%A5/</url>
<content><![CDATA[<p>第二十个除夕。</p>
@@ -4415,24 +4433,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的�
</categories>
</entry>
<entry>
- <title>二〇二五年十月三十日</title>
- <url>/2025/10/30/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E6%9C%88%E4%B8%89%E5%8D%81%E6%97%A5/</url>
- <content><![CDATA[<p>那是一个无云、风也很小的傍晚。天光从浅到深慢慢往你这边退,像有人慢慢把你从白昼里抱出来。街角的花店门口站着一只猫,它看着路过的车,耳朵偶尔动一下。屋檐下有风铃,轻轻的,叮的一声,时间像也跟着响了一下。</p>
-<p>花店里里有一支黄玫瑰,花瓣像被烫过,从橙黄到金黄,层层叠叠,在傍晚的空气里慢慢散开一层暖色。它有甜味,甜里还带着一点苦,味道朦胧的像你在窗下读书,书页翻过时有阳光落下来,像你走过老城的石板路,脚下的石头被晒得很暖。</p>
-<p>我记得那张粗糙的柜台,记得老板的手掌上有花叶的汁液。记得那种 “被允许站在这里” 的轻松。它没有要你改变什么,也没有给你安排一个更好的未来。它只是让你看见,一个傍晚可以用来想事,一条路可以一直往前延伸而不追问终点是什么。</p>
-<p>从那以后,我偶尔还会闻到那种味道。公交车拐弯的时候,风从窗缝里钻进来,地铁门打开的时候,站台上一阵匆忙的脚步后,忽然静下来的那一刻。我不知道它为什么会出现,也不刻意寻找。它像朋友一样,来了就坐一会儿,走了也不会留下地址。</p>
-<p>然后我遇见了你。</p>
-<p>起初我们并肩走在一条街上,街灯刚亮,影子从脚边拖到墙根。你说话的时候,声音不急也不慢。我听见了你的过去,但不是像在听故事那样去比较谁更动人,我只是听着你。你给我看了你的手心,那上面有被太阳晒过后的浅色纹理,也有你习惯把指甲剪得很短的认真。我心里有一种安静,它不是从谁那里借来的,而是像从很远的地方自己走来的。</p>
-<p>我知道你不是她,她是一种很亮的影子,只能在黑暗里存在。而你会在这里,在光亮里也在风里,在你的沉默里也在你的笑声里。时间不会让你跟她合在一起,时间只会让你在我旁边,成为一个可以站着的人,一个愿意陪我走过同一条街、同一种气味、同一种阳光的人。</p>
-<p>于是我想给你那朵黄玫瑰的记忆。不是因为我想要你变成她的样子,而是因为我在你身上看见了自己喜欢的那种安静和从容。我想让你在今后的某一天,当公交门打开,站台忽然安静下来,当你走在一条熟悉的路上,风从树梢里穿过,当你坐在窗边,光从书页上滑过去的时候,能闻到那种味道,能想起那一刻的我们,我们在傍晚的风里站着,既不躲藏也不假装,我们把时间握在手里,像握着一朵温柔得让人不需要理由的花。</p>
-<p>如果你愿意,我可以每天傍晚经过花店门口,我们可以把那种味道带回家,用一只透明的瓶子装起来,不需要解释它来自哪里,也不需要把它和任何过去比较。它只会在你需要的时候出现,像一个老朋友,提醒你,不是每一次都要变成别人,不是每一次都需要证明你已经忘记。你可以是现在的你,我可以是现在的我。我们可以在同一个黄昏里站一会儿,看看天空从浅到深,看看风从我们旁边走过,看看影子在我们的脚边拉长。</p>
-<p>黄玫瑰的味道不会要求我们变成谁,它只会给我们一点轻的力气,让我们在时间里往前走走。我把这份力气给你,也给我自己,然后我们就接着往前走,带着这种不喧哗的光。</p>
-]]></content>
- <categories>
- <category>gallery</category>
- </categories>
- </entry>
- <entry>
<title>二〇二四年一月二十三日</title>
<url>/2024/01/23/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B8%80%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B8%89%E6%97%A5/</url>
<content><![CDATA[<p>有人在桥上并肩走过,孩子们举着风车大笑着跑开,热乎乎的米糕新鲜出炉,香气混着夜风传去老远。</p>
@@ -4512,6 +4512,29 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的�
</categories>
</entry>
<entry>
+ <title>二〇二四年二月七日</title>
+ <url>/2024/02/07/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%B8%83%E6%97%A5/</url>
+ <content><![CDATA[<p>瀑布的水逆流而上,<br>
+蒲公英种子从远处飘回,聚成伞的模样,<br>
+太阳从西边升起,落向东方。</p>
+<p>子弹退回枪膛,<br>
+运动员回到起跑线上,<br>
+我交回录取通知书,忘了十年寒窗。</p>
+<p>厨房里飘来饭菜的香,<br>
+你把我的卷子签好名字,<br>
+关掉电视,帮我把书包背上。</p>
+<p>你还在我身旁。</p>
+<hr>
+<p>功利性主义总会引导我们思考结局,可生活本质上就是一场旅途。</p>
+<p>别再去胡思乱想了,好好欣赏生活中的一切吧,去感受一朵鲜花的盛开,一束阳光的倾泻,一湖清水的静谧。</p>
+<p>奶奶说,炉膛的柴火若是爆裂,那是有贵客要登门了。我总是不信。直到有一次,舅公伴着柴火的爆裂声,出现在家门口,我才将信将疑。</p>
+<p>所以,让我们在下雨天烧柴火灶,在跳跃的火苗里,等待一种不确定的相逢,然后煮茶饮酒,自由一生 。</p>
+]]></content>
+ <categories>
+ <category>gallery</category>
+ </categories>
+ </entry>
+ <entry>
<title>二〇二四年五月七日</title>
<url>/2024/05/07/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%BA%94%E6%9C%88%E4%B8%83%E6%97%A5/</url>
<content><![CDATA[<p>我在写信<br>
@@ -4537,29 +4560,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的�
</categories>
</entry>
<entry>
- <title>二〇二四年二月七日</title>
- <url>/2024/02/07/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%B8%83%E6%97%A5/</url>
- <content><![CDATA[<p>瀑布的水逆流而上,<br>
-蒲公英种子从远处飘回,聚成伞的模样,<br>
-太阳从西边升起,落向东方。</p>
-<p>子弹退回枪膛,<br>
-运动员回到起跑线上,<br>
-我交回录取通知书,忘了十年寒窗。</p>
-<p>厨房里飘来饭菜的香,<br>
-你把我的卷子签好名字,<br>
-关掉电视,帮我把书包背上。</p>
-<p>你还在我身旁。</p>
-<hr>
-<p>功利性主义总会引导我们思考结局,可生活本质上就是一场旅途。</p>
-<p>别再去胡思乱想了,好好欣赏生活中的一切吧,去感受一朵鲜花的盛开,一束阳光的倾泻,一湖清水的静谧。</p>
-<p>奶奶说,炉膛的柴火若是爆裂,那是有贵客要登门了。我总是不信。直到有一次,舅公伴着柴火的爆裂声,出现在家门口,我才将信将疑。</p>
-<p>所以,让我们在下雨天烧柴火灶,在跳跃的火苗里,等待一种不确定的相逢,然后煮茶饮酒,自由一生 。</p>
-]]></content>
- <categories>
- <category>gallery</category>
- </categories>
- </entry>
- <entry>
<title>二〇二四年五月十六日</title>
<url>/2024/05/16/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%BA%94%E6%9C%88%E5%8D%81%E5%85%AD%E6%97%A5/</url>
<content><![CDATA[<p>无眼耳鼻舌身意,无色声香味触法,四下皆空,但万物有实,活在真实世界,而非活在一系列参照物之中。</p>
@@ -4737,15 +4737,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的�
</categories>
</entry>
<entry>
- <title>二零二三年二月一日</title>
- <url>/2023/02/01/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%B8%80%E6%97%A5/</url>
- <content><![CDATA[<p>我想在六点和日出打招呼,我想去菜市场看看卖蔬菜的阿婆,我想花一上午去准备一餐中午饭,我想下午能在阳台捧着一盏热茶慢慢看书,慢点好,再慢点,慢到能透过阳台栏杆的缝隙看看马路上车水马龙映衬下的老人家提着新鲜的一荤一素慢慢蹒跚走回家,看看贼几把大的夕阳在冒着热气的水杯中慢慢融化。</p>
-]]></content>
- <categories>
- <category>gallery</category>
- </categories>
- </entry>
- <entry>
<title>二零二三年二月二十二日</title>
<url>/2023/02/22/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%97%A5/</url>
<content><![CDATA[<p>这个小城市还有很多老人家,她们跟不上这个时代的脚步,要有人带她们跟上这个时代,再目送她们去下一个时代,不只是老人家,还有我们的未来。</p>
@@ -4766,9 +4757,9 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的�
</categories>
</entry>
<entry>
- <title>二零二三年二月二十六日</title>
- <url>/2023/02/26/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%85%AD%E6%97%A5/</url>
- <content><![CDATA[<p>翻滚的热汤接过焰火的能量把周围的蒸汽撕开一道裂缝,透过缝隙我见到一对爱人的时空交织在未来,他们幸运的错开孤独,人们举起手中的酒杯,祝因果与命运缠绵之人幸福</p>
+ <title>二零二三年二月一日</title>
+ <url>/2023/02/01/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%B8%80%E6%97%A5/</url>
+ <content><![CDATA[<p>我想在六点和日出打招呼,我想去菜市场看看卖蔬菜的阿婆,我想花一上午去准备一餐中午饭,我想下午能在阳台捧着一盏热茶慢慢看书,慢点好,再慢点,慢到能透过阳台栏杆的缝隙看看马路上车水马龙映衬下的老人家提着新鲜的一荤一素慢慢蹒跚走回家,看看贼几把大的夕阳在冒着热气的水杯中慢慢融化。</p>
]]></content>
<categories>
<category>gallery</category>
@@ -4796,6 +4787,15 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的�
</categories>
</entry>
<entry>
+ <title>二零二三年二月二十六日</title>
+ <url>/2023/02/26/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%85%AD%E6%97%A5/</url>
+ <content><![CDATA[<p>翻滚的热汤接过焰火的能量把周围的蒸汽撕开一道裂缝,透过缝隙我见到一对爱人的时空交织在未来,他们幸运的错开孤独,人们举起手中的酒杯,祝因果与命运缠绵之人幸福</p>
+]]></content>
+ <categories>
+ <category>gallery</category>
+ </categories>
+ </entry>
+ <entry>
<title>二零二三年八月二十一日</title>
<url>/2023/08/21/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%85%AB%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B8%80%E6%97%A5/</url>
<content><![CDATA[<p>他看了一眼远处的尸体,指挥着镰刀将远处的杂草割干净,到处都是鲜花,春天来了,漫山遍野。</p>
@@ -4960,18 +4960,18 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的�
</categories>
</entry>
<entry>
- <title>二零二零年十二月二十六日</title>
- <url>/2020/12/26/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%85%AD%E6%97%A5/</url>
- <content><![CDATA[<p>我被包裹在吊床中,两颗香樟树支撑着它,仲夏的热浪砸在粗壮的香樟树上,叶子一屈,抱住了热浪,可随即而来的是不知从哪浇来的阳光,叶子承受不住这样的力量,被砸的孔洞百出,砸的香气落满树下的人间。</p>
+ <title>二零二零年十二月八日</title>
+ <url>/2020/12/08/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E5%85%AB%E6%97%A5/</url>
+ <content><![CDATA[<p>它们勇敢的站在烈日下, 像人类一样无所畏惧, 却又恐惧的躲在黑暗中, 像当初的它们一样无处可逃。</p>
]]></content>
<categories>
<category>gallery</category>
</categories>
</entry>
<entry>
- <title>二零二零年十二月八日</title>
- <url>/2020/12/08/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E5%85%AB%E6%97%A5/</url>
- <content><![CDATA[<p>它们勇敢的站在烈日下, 像人类一样无所畏惧, 却又恐惧的躲在黑暗中, 像当初的它们一样无处可逃。</p>
+ <title>二零二零年十二月二十六日</title>
+ <url>/2020/12/26/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E9%9B%B6%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%85%AD%E6%97%A5/</url>
+ <content><![CDATA[<p>我被包裹在吊床中,两颗香樟树支撑着它,仲夏的热浪砸在粗壮的香樟树上,叶子一屈,抱住了热浪,可随即而来的是不知从哪浇来的阳光,叶子承受不住这样的力量,被砸的孔洞百出,砸的香气落满树下的人间。</p>
]]></content>
<categories>
<category>gallery</category>
@@ -5407,6 +5407,185 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的�
</tags>
</entry>
<entry>
+ <title>在 OCaml 中通过 CTypes 绑定 libseccomp</title>
+ <url>/2026/03/22/%E5%9C%A8-OCaml-%E4%B8%AD%E9%80%9A%E8%BF%87-CTypes-%E7%BB%91%E5%AE%9A-libseccomp/</url>
+ <content><![CDATA[<p>CTypes 允许我们用纯 OCaml 代码绑定 C lib 而不用编写任何 C 代码。这篇文章是以 <a href="https://github.com/muqiuhan/bonding">bonding</a> 项目中的 <code>libseccomp</code> 绑定为例写的。</p>
+<p>CTypes 的核心概念是建立 OCaml 类型与 C 类型的双向映射,ctypes 中有如下基本类型:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">open</span> <span class="type">Ctypes</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* C 类型与 OCaml 类型的对应关系 *)</span></span><br><span class="line"><span class="keyword">let</span> c_int : <span class="built_in">int</span> typ = <span class="built_in">int</span> <span class="comment">(* int *)</span></span><br><span class="line"><span class="keyword">let</span> c_uint : <span class="type">Unsigned</span>.<span class="type">UInt</span>.t typ = uint <span class="comment">(* unsigned int *)</span></span><br><span class="line"><span class="keyword">let</span> c_long : <span class="type">Signed</span>.<span class="type">Long</span>.t typ = long <span class="comment">(* long *)</span></span><br><span class="line"><span class="keyword">let</span> c_int64 : <span class="type">Unsigned</span>.<span class="type">UInt64</span>.t typ = uint64_t <span class="comment">(* int64_t / uint64_t *)</span></span><br><span class="line"><span class="keyword">let</span> c_float : <span class="built_in">float</span> typ = <span class="built_in">float</span> <span class="comment">(* float *)</span></span><br><span class="line"><span class="keyword">let</span> c_double : <span class="built_in">float</span> typ = double <span class="comment">(* double *)</span></span><br><span class="line"><span class="keyword">let</span> c_char : <span class="built_in">char</span> typ = <span class="built_in">char</span> <span class="comment">(* char *)</span></span><br><span class="line"><span class="keyword">let</span> c_void : <span class="built_in">unit</span> typ = void <span class="comment">(* void *)</span></span><br></pre></td></tr></table></figure>
+<h2 id="指针"><a class="header-anchor" href="#指针">¶</a>指针</h2>
+<p>对于 C 的指针,CTypes 提供了多种指针表示:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 通用指针:void* *)</span></span><br><span class="line"><span class="keyword">let</span> void_ptr : <span class="built_in">unit</span> ptr typ = ptr void</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 类型化指针:int* *)</span></span><br><span class="line"><span class="keyword">let</span> int_ptr : <span class="built_in">int</span> ptr typ = ptr <span class="built_in">int</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 不透明指针:用于表示我们不知道内部结构的 C 结构体 *)</span></span><br><span class="line"><span class="keyword">let</span> scmp_filter_ctx : <span class="built_in">unit</span> ptr typ = ptr void</span><br></pre></td></tr></table></figure>
+<p>bonding 中的 seccomp_stubs.ml 中是这么写的:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> scmp_filter_ctx = <span class="built_in">unit</span> ptr</span><br><span class="line"><span class="keyword">let</span> scmp_filter_ctx : scmp_filter_ctx typ = ptr void</span><br></pre></td></tr></table></figure>
+<p>这里 <code>scmp_filter_ctx</code> 是 libseccomp 的核心类型,它是一个不透明指针。C 库不暴露其内部结构,我们只能通过库函数操作它。使用 <code>ptr void</code> 是表示不透明指针的标准做法。</p>
+<h2 id="结构体"><a class="header-anchor" href="#结构体">¶</a>结构体</h2>
+<p>对于结构体,ctypes 通过 <code>structure</code> 和 <code>field</code> 函数定义:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 定义 C 结构体:</span></span><br><span class="line"><span class="comment"> struct scmp_arg_cmp &#123;</span></span><br><span class="line"><span class="comment"> unsigned int arg;</span></span><br><span class="line"><span class="comment"> unsigned int op;</span></span><br><span class="line"><span class="comment"> uint64_t datum_a;</span></span><br><span class="line"><span class="comment"> uint64_t datum_b;</span></span><br><span class="line"><span class="comment"> &#125;;</span></span><br><span class="line"><span class="comment">*)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> scmp_arg_cmp : <span class="built_in">unit</span> structure typ =</span><br><span class="line"> <span class="keyword">let</span> s = structure <span class="string">&quot;scmp_arg_cmp&quot;</span> <span class="keyword">in</span> <span class="comment">(* 1. 创建结构体模板 *)</span></span><br><span class="line"> <span class="keyword">let</span> _arg = field s <span class="string">&quot;arg&quot;</span> uint <span class="keyword">in</span> <span class="comment">(* 2. 添加字段 *)</span></span><br><span class="line"> <span class="keyword">let</span> _op = field s <span class="string">&quot;op&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _datum_a = field s <span class="string">&quot;datum_a&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _datum_b = field s <span class="string">&quot;datum_b&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> seal s; <span class="comment">(* 3. 封印结构体 *)</span></span><br><span class="line"> s <span class="comment">(* 4. 返回类型描述符 *)</span></span><br></pre></td></tr></table></figure>
+<p>注意:</p>
+<ul>
+<li><code>structure &quot;name&quot;</code> 创建一个未完成的类型描述符</li>
+<li><code>field s &quot;fieldname&quot; typ</code> 添加字段,返回字段访问器</li>
+<li><code>seal s</code> 完成结构体定义,之后不可再添加字段</li>
+<li>返回的 <code>s</code> 可以作为类型使用</li>
+</ul>
+<p>例如:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> scmp_arg_cmp : <span class="built_in">unit</span> structure typ =</span><br><span class="line"> <span class="keyword">let</span> s = structure <span class="string">&quot;scmp_arg_cmp&quot;</span> <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _arg = field s <span class="string">&quot;arg&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _op = field s <span class="string">&quot;op&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _datum_a = field s <span class="string">&quot;datum_a&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _datum_b = field s <span class="string">&quot;datum_b&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> seal s;</span><br><span class="line"> s</span><br></pre></td></tr></table></figure>
+<p>定义结构体后,可以通过字段访问器读写值:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 写入字段值 *)</span></span><br><span class="line"><span class="keyword">let</span> set_arg_cmp_values struct_ptr arg_val op_val datum_a_val datum_b_val =</span><br><span class="line"> <span class="keyword">let</span> s = structure <span class="string">&quot;scmp_arg_cmp&quot;</span> <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> arg = field s <span class="string">&quot;arg&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> op = field s <span class="string">&quot;op&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> datum_a = field s <span class="string">&quot;datum_a&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> datum_b = field s <span class="string">&quot;datum_b&quot;</span> uint64_t <span class="keyword">in</span></span><br><span class="line"> seal s;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">(* 使用 setf 设置字段值 *)</span></span><br><span class="line"> setf struct_ptr arg (<span class="type">Unsigned</span>.<span class="type">UInt</span>.of_int arg_val);</span><br><span class="line"> setf struct_ptr op (<span class="type">Unsigned</span>.<span class="type">UInt</span>.of_int op_val);</span><br><span class="line"> setf struct_ptr datum_a (<span class="type">Unsigned</span>.<span class="type">UInt64</span>.of_int64 datum_a_val);</span><br><span class="line"> setf struct_ptr datum_b (<span class="type">Unsigned</span>.<span class="type">UInt64</span>.of_int64 datum_b_val)</span><br></pre></td></tr></table></figure>
+<p>这里展示了如何创建辅助函数来操作结构体。注意:</p>
+<ul>
+<li>每次都需要重新定义结构体和字段访问器</li>
+<li><code>setf</code> 的参数顺序:结构体指针、字段访问器、值</li>
+<li>OCaml 的无符号类型需要显式转换</li>
+</ul>
+<h2 id="动态库"><a class="header-anchor" href="#动态库">¶</a>动态库</h2>
+<p>使用 CTypes 绑定 C 库只需要:加载库、然后绑定函数。</p>
+<p>首先,加载共享库:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> libseccomp =</span><br><span class="line"> <span class="keyword">let</span> paths = [ <span class="string">&quot;libseccomp.so.2&quot;</span>; <span class="string">&quot;libseccomp.so.1&quot;</span>; <span class="string">&quot;libseccomp.so&quot;</span> ] <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">rec</span> try_paths = <span class="keyword">function</span></span><br><span class="line"> | <span class="literal">[]</span> -&gt;</span><br><span class="line"> <span class="type">Dl</span>.dlopen ~filename:<span class="string">&quot;libseccomp.so.2&quot;</span> ~flags:[ <span class="type">Dl</span>.<span class="type">RTLD_NOW</span> ]</span><br><span class="line"> | path :: rest -&gt; (</span><br><span class="line"> <span class="keyword">try</span> <span class="type">Dl</span>.dlopen ~filename:path ~flags:[ <span class="type">Dl</span>.<span class="type">RTLD_NOW</span> ] <span class="keyword">with</span></span><br><span class="line"> | _ -&gt;</span><br><span class="line"> try_paths rest)</span><br><span class="line"> <span class="keyword">in</span></span><br><span class="line"> try_paths paths</span><br></pre></td></tr></table></figure>
+<p>这段代码用了一个提升动态库加载稳定性的小技巧:</p>
+<ul>
+<li>尝试多个可能的库文件名(不同系统可能版本不同)</li>
+<li>依次尝试,直到成功</li>
+<li>如果全部失败,使用默认路径(触发异常)</li>
+</ul>
+<p>注意 <code>Dl.dlopen</code> 的参数:</p>
+<ul>
+<li><code>filename</code>: 库文件名(可以是绝对路径或库文件名)</li>
+<li><code>flags</code>: 加载标志
+<ul>
+<li><code>RTLD_NOW</code>: 立即解析所有符号</li>
+<li><code>RTLD_LAZY</code>: 延迟解析符号</li>
+</ul>
+</li>
+</ul>
+<p>加载完成后使用 <code>Foreign.foreign</code> 绑定到函数,基本语法:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> function_name = </span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:library</span><br><span class="line"> <span class="string">&quot;c_function_name&quot;</span></span><br><span class="line"> (arg1_type @-&gt; arg2_type @-&gt; ... @-&gt; returning return_type)</span><br></pre></td></tr></table></figure>
+<p>单参数函数:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_init =</span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:libseccomp</span><br><span class="line"> <span class="string">&quot;seccomp_init&quot;</span></span><br><span class="line"> (uint32_t @-&gt; returning scmp_filter_ctx)</span><br></pre></td></tr></table></figure>
+<p>对应的 C 函数原型:</p>
+<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">scmp_filter_ctx <span class="title function_">seccomp_init</span><span class="params">(<span class="type">uint32_t</span> def_action)</span>;</span><br></pre></td></tr></table></figure>
+<p><code>Foreign.foreign</code> 的参数:</p>
+<ul>
+<li><code>~from:libseccomp</code>: 指定函数所在的库</li>
+<li><code>&quot;seccomp_init&quot;</code>: C 函数名</li>
+<li><code>uint32_t @-&gt; returning scmp_filter_ctx</code>: 函数签名
+<ul>
+<li><code>@-&gt;</code> 分隔参数</li>
+<li><code>returning</code> 标记返回类型</li>
+</ul>
+</li>
+</ul>
+<p>对于多参数函数:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_rule_add =</span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:libseccomp</span><br><span class="line"> <span class="string">&quot;seccomp_rule_add&quot;</span></span><br><span class="line"> (scmp_filter_ctx @-&gt; uint32_t @-&gt; <span class="built_in">int</span> @-&gt; uint @-&gt; returning <span class="built_in">int</span>)</span><br></pre></td></tr></table></figure>
+<p><strong>C 函数原型</strong>:</p>
+<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">seccomp_rule_add</span><span class="params">(scmp_filter_ctx ctx, <span class="type">uint32_t</span> action, </span></span><br><span class="line"><span class="params"> <span class="type">int</span> syscall, <span class="type">unsigned</span> <span class="type">int</span> arg_cnt)</span>;</span><br></pre></td></tr></table></figure>
+<p>对于无参数函数:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_arch_native =</span><br><span class="line"> <span class="type">Foreign</span>.foreign </span><br><span class="line"> ~from:libseccomp </span><br><span class="line"> <span class="string">&quot;seccomp_arch_native&quot;</span> </span><br><span class="line"> (void @-&gt; returning uint32_t)</span><br></pre></td></tr></table></figure>
+<p>对于指针参数:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_load =</span><br><span class="line"> <span class="type">Foreign</span>.foreign </span><br><span class="line"> ~from:libseccomp </span><br><span class="line"> <span class="string">&quot;seccomp_load&quot;</span> </span><br><span class="line"> (scmp_filter_ctx @-&gt; returning <span class="built_in">int</span>)</span><br></pre></td></tr></table></figure>
+<p><strong>C 函数原型</strong>:</p>
+<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">seccomp_load</span><span class="params">(scmp_filter_ctx ctx)</span>;</span><br></pre></td></tr></table></figure>
+<p>对于字符串参数:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_syscall_resolve_name =</span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:libseccomp</span><br><span class="line"> <span class="string">&quot;seccomp_syscall_resolve_name&quot;</span></span><br><span class="line"> (<span class="built_in">string</span> @-&gt; returning <span class="built_in">int</span>)</span><br></pre></td></tr></table></figure>
+<p>这里 CTypes 会自动处理 OCaml 字符串与 C 字符串的转换。</p>
+<p>对于返回 NULL 的情况:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> seccomp_syscall_resolve_num_arch =</span><br><span class="line"> <span class="type">Foreign</span>.foreign</span><br><span class="line"> ~from:libseccomp</span><br><span class="line"> <span class="string">&quot;seccomp_syscall_resolve_num_arch&quot;</span></span><br><span class="line"> (uint32_t @-&gt; <span class="built_in">int</span> @-&gt; returning string_opt)</span><br></pre></td></tr></table></figure>
+<p><code>string_opt</code> 处理可能返回 NULL 的字符串指针。</p>
+<h2 id="内存管理"><a class="header-anchor" href="#内存管理">¶</a>内存管理</h2>
+<p>C 需要手动管理内存,CTypes 提供了相应的工具。</p>
+<p>分配单个值:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 分配并初始化单个值 *)</span></span><br><span class="line"><span class="keyword">let</span> ptr = allocate <span class="built_in">int</span> <span class="number">42</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 读取值 *)</span></span><br><span class="line"><span class="keyword">let</span> <span class="keyword">value</span> = !@ ptr</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 写入值 *)</span></span><br><span class="line">ptr &lt;-@ <span class="number">100</span></span><br></pre></td></tr></table></figure>
+<p>分配数组:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 分配数组(未初始化) *)</span></span><br><span class="line"><span class="keyword">let</span> arr = allocate_n <span class="built_in">int</span> ~count:<span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 分配并初始化数组 *)</span></span><br><span class="line"><span class="keyword">let</span> arr = allocate_n <span class="built_in">int</span> ~count:<span class="number">5</span> ~initial:<span class="number">0</span></span><br></pre></td></tr></table></figure>
+<p>例如:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> cmp_array = <span class="type">Ctypes</span>.allocate_n <span class="type">Stubs</span>.<span class="type">Types</span>.scmp_arg_cmp ~count:n <span class="keyword">in</span></span><br></pre></td></tr></table></figure>
+<h3 id="指针算术"><a class="header-anchor" href="#指针算术">¶</a>指针算术</h3>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = allocate_n <span class="built_in">int</span> ~count:<span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 访问第 i 个元素 *)</span></span><br><span class="line"><span class="keyword">let</span> elem_ptr = arr +@ i</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 读写元素 *)</span></span><br><span class="line"><span class="keyword">let</span> <span class="keyword">value</span> = !@ elem_ptr</span><br><span class="line">elem_ptr &lt;-@ <span class="number">42</span></span><br></pre></td></tr></table></figure>
+<p>例如:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="type">Stubs</span>.<span class="type">Types</span>.set_arg_cmp_values</span><br><span class="line"> !@(cmp_array +@ i) <span class="comment">(* 第 i 个结构体的指针 *)</span></span><br><span class="line"> cmp.arg</span><br><span class="line"> (cmp_op_to_int cmp.op)</span><br><span class="line"> cmp.datum_a</span><br><span class="line"> cmp.datum_b</span><br></pre></td></tr></table></figure>
+<p><code>!@</code> 运算符解引用指针,<code>+@</code> 进行指针算术。</p>
+<h3 id="指针转换"><a class="header-anchor" href="#指针转换">¶</a>指针转换</h3>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 类型化指针转 void 指针 *)</span></span><br><span class="line"><span class="keyword">let</span> void_p = to_voidp int_ptr</span><br><span class="line"></span><br><span class="line"><span class="comment">(* void 指针转类型化指针 *)</span></span><br><span class="line"><span class="keyword">let</span> int_p = from_voidp <span class="built_in">int</span> void_p</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 获取空指针 *)</span></span><br><span class="line"><span class="keyword">let</span> null_ptr : <span class="built_in">unit</span> ptr = null</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 检查是否为空指针 *)</span></span><br><span class="line"><span class="keyword">if</span> is_null ptr <span class="keyword">then</span> ...</span><br></pre></td></tr></table></figure>
+<p><strong>实例解析(<a href="http://seccomp.ml:96">seccomp.ml:96</a>)</strong>:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="type">Ctypes</span>.ptr_compare ctx <span class="type">Ctypes</span>.null = <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> <span class="type">Error</span> <span class="type">Init_failed</span></span><br></pre></td></tr></table></figure>
+<p>检查 <code>seccomp_init</code> 是否返回了 NULL(失败)。</p>
+<h2 id="一些模式"><a class="header-anchor" href="#一些模式">¶</a>一些模式</h2>
+<h3 id="类型视图(Type-Views)"><a class="header-anchor" href="#类型视图(Type-Views)">¶</a>类型视图(Type Views)</h3>
+<p>当 C 类型与 OCaml 类型不能直接对应时,使用 <code>view</code> 创建自定义转换:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 将 C int 转换为 OCaml bool *)</span></span><br><span class="line"><span class="keyword">let</span> c_bool = view</span><br><span class="line"> ~read:(<span class="keyword">fun</span> i -&gt; i &lt;&gt; <span class="number">0</span>)</span><br><span class="line"> ~write:(<span class="keyword">fun</span> b -&gt; <span class="keyword">if</span> b <span class="keyword">then</span> <span class="number">1</span> <span class="keyword">else</span> <span class="number">0</span>)</span><br><span class="line"> <span class="built_in">int</span></span><br></pre></td></tr></table></figure>
+<h3 id="枚举类型映射"><a class="header-anchor" href="#枚举类型映射">¶</a>枚举类型映射</h3>
+<p>在 seccomp 实现中,使用 OCaml 变体和转换函数映射 C 枚举:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* OCaml 端的类型定义(seccomp.ml:18-25) *)</span></span><br><span class="line"><span class="keyword">type</span> action =</span><br><span class="line"> | <span class="type">Allow</span></span><br><span class="line"> | <span class="type">Errno</span> <span class="keyword">of</span> <span class="built_in">int</span></span><br><span class="line"> | <span class="type">KillProcess</span></span><br><span class="line"> | <span class="type">KillThread</span></span><br><span class="line"> | <span class="type">Log</span></span><br><span class="line"> | <span class="type">Trace</span> <span class="keyword">of</span> <span class="built_in">int</span></span><br><span class="line"> | <span class="type">Trap</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* C 端的常量定义(seccomp_stubs.ml:58-73) *)</span></span><br><span class="line"><span class="keyword">module</span> <span class="type">Constants</span> = <span class="keyword">struct</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_allow = <span class="number">0x7fff0000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_kill_thread = <span class="number">0x00000000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_kill_process = <span class="number">0x00000000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_trap = <span class="number">0x00030000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_log = <span class="number">0x7ffc0000l</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_errno code = </span><br><span class="line"> <span class="type">Int32</span>.(logor <span class="number">0x00050000l</span> (logand (of_int code) <span class="number">0x0000ffffl</span>))</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">(* 转换函数(seccomp.ml:60-75) *)</span></span><br><span class="line"><span class="keyword">let</span> action_to_uint32 = <span class="keyword">function</span></span><br><span class="line"> | <span class="type">Allow</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_allow</span><br><span class="line"> | <span class="type">KillThread</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_kill_thread</span><br><span class="line"> | <span class="type">KillProcess</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_kill_process</span><br><span class="line"> | <span class="type">Trap</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_trap</span><br><span class="line"> | <span class="type">Log</span> -&gt; int32_to_uint32 <span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_log</span><br><span class="line"> | <span class="type">Errno</span> code -&gt; int32_to_uint32 (<span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_errno code)</span><br><span class="line"> | <span class="type">Trace</span> code -&gt; int32_to_uint32 (<span class="type">Stubs</span>.<span class="type">Constants</span>.scmp_act_trace code)</span><br></pre></td></tr></table></figure>
+<p>这种模式的优势:</p>
+<ul>
+<li>OCaml 端使用类型安全的变体</li>
+<li>C 端使用整数常量</li>
+<li>转换函数处理映射</li>
+<li>编译器帮助捕获类型错误</li>
+</ul>
+<h3 id="错误处理模式"><a class="header-anchor" href="#错误处理模式">¶</a>错误处理模式</h3>
+<p>C 那边通常用返回值表示错误。OCaml 使用 result 类型,例如在 seccomp 的 binding 中我这么做:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> error =</span><br><span class="line"> | <span class="type">Init_failed</span></span><br><span class="line"> | <span class="type">Rule_add_failed</span> <span class="keyword">of</span> <span class="built_in">int</span></span><br><span class="line"> | <span class="type">Load_failed</span> <span class="keyword">of</span> <span class="built_in">int</span></span><br><span class="line"> | <span class="type">Unknown_syscall</span> <span class="keyword">of</span> <span class="built_in">string</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> init default_action =</span><br><span class="line"> <span class="keyword">let</span> ctx = <span class="type">Stubs</span>.<span class="type">Functions</span>.seccomp_init (action_to_uint32 default_action) <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">if</span> <span class="type">Ctypes</span>.ptr_compare ctx <span class="type">Ctypes</span>.null = <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> <span class="type">Error</span> <span class="type">Init_failed</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="type">Ok</span> ctx</span><br></pre></td></tr></table></figure>
+<h2 id="完整示例"><a class="header-anchor" href="#完整示例">¶</a>完整示例</h2>
+<p>让我们来看看一个完整的 <code>rule_add_conditional</code> 函数的实现:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> rule_add_conditional ctx action syscall cmps =</span><br><span class="line"> <span class="keyword">let</span> n = <span class="type">Array</span>.length cmps <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">if</span> n = <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> rule_add ctx action syscall</span><br><span class="line"> <span class="keyword">else</span> (</span><br><span class="line"> <span class="comment">(* 1. 分配 C 数组 *)</span></span><br><span class="line"> <span class="keyword">let</span> cmp_array = <span class="type">Ctypes</span>.allocate_n <span class="type">Stubs</span>.<span class="type">Types</span>.scmp_arg_cmp ~count:n <span class="keyword">in</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">(* 2. 填充每个结构体 *)</span></span><br><span class="line"> <span class="type">Array</span>.iteri</span><br><span class="line"> (<span class="keyword">fun</span> i cmp -&gt;</span><br><span class="line"> <span class="type">Stubs</span>.<span class="type">Types</span>.set_arg_cmp_values</span><br><span class="line"> !@(cmp_array +@ i) <span class="comment">(* 解引用第 i 个元素 *)</span></span><br><span class="line"> cmp.arg</span><br><span class="line"> (cmp_op_to_int cmp.op)</span><br><span class="line"> cmp.datum_a</span><br><span class="line"> cmp.datum_b)</span><br><span class="line"> cmps;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">(* 3. 调用 C 函数 *)</span></span><br><span class="line"> <span class="keyword">let</span> result =</span><br><span class="line"> <span class="type">Stubs</span>.<span class="type">Functions</span>.seccomp_rule_add_array</span><br><span class="line"> ctx</span><br><span class="line"> (action_to_uint32 action)</span><br><span class="line"> syscall</span><br><span class="line"> (<span class="type">Unsigned</span>.<span class="type">UInt</span>.of_int n)</span><br><span class="line"> cmp_array</span><br><span class="line"> <span class="keyword">in</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">(* 4. 检查结果 *)</span></span><br><span class="line"> <span class="keyword">if</span> result &lt; <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> <span class="type">Error</span> (<span class="type">Rule_add_failed</span> result)</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="type">Ok</span> <span class="literal">()</span></span><br><span class="line"> )</span><br></pre></td></tr></table></figure>
+<ul>
+<li>数组分配: <code>allocate_n</code> 分配 <code>n</code> 个 <code>scmp_arg_cmp</code> 结构体的空间</li>
+<li>结构体填充:
+<ul>
+<li><code>cmp_array +@ i</code> 获取第 i 个元素的指针</li>
+<li><code>!@</code> 解引用得到结构体</li>
+<li><code>set_arg_cmp_values</code> 填充字段</li>
+</ul>
+</li>
+<li>函数调用: 传递数组和长度给 C 函数</li>
+<li>返回值: C 函数返回负值表示错误</li>
+</ul>
+<p>对应的 C API:</p>
+<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">seccomp_rule_add_array</span><span class="params">(scmp_filter_ctx ctx, <span class="type">uint32_t</span> action,</span></span><br><span class="line"><span class="params"> <span class="type">int</span> syscall, <span class="type">unsigned</span> <span class="type">int</span> arg_cnt,</span></span><br><span class="line"><span class="params"> <span class="type">const</span> <span class="keyword">struct</span> scmp_arg_cmp *arg_array)</span>;</span><br></pre></td></tr></table></figure>
+<h3 id="双层架构-Binding"><a class="header-anchor" href="#双层架构-Binding">¶</a>双层架构 Binding</h3>
+<p>seccomp 绑定用了清晰的三层架构:</p>
+<ul>
+<li><a href="http://seccomp.ml">seccomp.ml</a> / seccomp.mli 是顶层 API 纯粹的OCaml 风格的类型和函数</li>
+<li>seccomp_stubs.ml 绑定层,放 CTypes 绑定,类型和函数定义</li>
+</ul>
+<h3 id="模块组织"><a class="header-anchor" href="#模块组织">¶</a>模块组织</h3>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* seccomp_stubs.ml 的模块组织 *)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">module</span> <span class="type">Types</span> = <span class="keyword">struct</span> <span class="comment">(* C 类型定义 *)</span></span><br><span class="line"> <span class="keyword">type</span> scmp_filter_ctx = <span class="built_in">unit</span> <span class="type">Ctypes</span>.ptr</span><br><span class="line"> <span class="keyword">val</span> scmp_filter_ctx : ...</span><br><span class="line"> <span class="keyword">val</span> scmp_arg_cmp : ...</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">module</span> <span class="type">Constants</span> = <span class="keyword">struct</span> <span class="comment">(* C 常量 *)</span></span><br><span class="line"> <span class="keyword">let</span> scmp_act_allow = ...</span><br><span class="line"> <span class="keyword">let</span> scmp_cmp_ne = ...</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">module</span> <span class="type">Functions</span> = <span class="keyword">struct</span> <span class="comment">(* C 函数绑定 *)</span></span><br><span class="line"> <span class="keyword">let</span> seccomp_init = ...</span><br><span class="line"> <span class="keyword">let</span> seccomp_load = ...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>
+<p>类型,常量,函数分离。</p>
+<h2 id="常见问题"><a class="header-anchor" href="#常见问题">¶</a>常见问题</h2>
+<h3 id="1-内存泄漏"><a class="header-anchor" href="#1-内存泄漏">¶</a>1. 内存泄漏</h3>
+<p>C 分配的内存需要手动释放:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 始终提供 release 函数 *)</span></span><br><span class="line"><span class="keyword">let</span> release ctx = <span class="type">Stubs</span>.<span class="type">Functions</span>.seccomp_release ctx</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 使用 Fun.protect 确保清理 *)</span></span><br><span class="line"><span class="keyword">let</span> with_filter default_action f =</span><br><span class="line"> <span class="keyword">match</span> init default_action <span class="keyword">with</span></span><br><span class="line"> | <span class="type">Error</span> e -&gt; <span class="type">Error</span> e</span><br><span class="line"> | <span class="type">Ok</span> ctx -&gt;</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> <span class="keyword">let</span> result = f ctx <span class="keyword">in</span></span><br><span class="line"> release ctx;</span><br><span class="line"> result</span><br><span class="line"> <span class="keyword">with</span> e -&gt;</span><br><span class="line"> release ctx;</span><br><span class="line"> raise e</span><br></pre></td></tr></table></figure>
+<h3 id="2-类型不匹配"><a class="header-anchor" href="#2-类型不匹配">¶</a>2. 类型不匹配</h3>
+<p>OCaml 和 C 的类型表示不同:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 使用 Unsigned 模块的转换函数 *)</span></span><br><span class="line"><span class="keyword">let</span> int32_to_uint32 n = <span class="type">Unsigned</span>.<span class="type">UInt32</span>.of_int32 n</span><br><span class="line"></span><br><span class="line"><span class="comment">(* 显式类型转换 *)</span></span><br><span class="line"><span class="keyword">let</span> c_uint = <span class="type">Unsigned</span>.<span class="type">UInt</span>.of_int <span class="number">42</span></span><br></pre></td></tr></table></figure>
+<h3 id="3-空指针检查"><a class="header-anchor" href="#3-空指针检查">¶</a>3. 空指针检查</h3>
+<p>C 函数可能返回 NULL:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 始终检查返回的指针 *)</span></span><br><span class="line"><span class="keyword">if</span> <span class="type">Ctypes</span>.ptr_compare ctx <span class="type">Ctypes</span>.null = <span class="number">0</span> <span class="keyword">then</span></span><br><span class="line"> <span class="type">Error</span> <span class="type">Init_failed</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> <span class="type">Ok</span> ctx</span><br></pre></td></tr></table></figure>
+<h3 id="4-字符串所有权"><a class="header-anchor" href="#4-字符串所有权">¶</a>4. 字符串所有权</h3>
+<p>C 返回的字符串生命周期不确定:</p>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="comment">(* 如果 C 返回需要释放的字符串,使用 string 并复制 *)</span></span><br><span class="line"><span class="keyword">let</span> result = <span class="type">Bytes</span>.of_string c_string <span class="keyword">in</span></span><br><span class="line"><span class="comment">(* 释放 C 字符串 *)</span></span><br><span class="line"><span class="type">C</span>.free c_string_ptr;</span><br><span class="line"><span class="type">Bytes</span>.to_string result</span><br></pre></td></tr></table></figure>
+<h2 id="调试技巧"><a class="header-anchor" href="#调试技巧">¶</a>调试技巧</h2>
+<h3 id="1-检查库加载"><a class="header-anchor" href="#1-检查库加载">¶</a>1. 检查库加载</h3>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="literal">()</span> =</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> <span class="keyword">let</span> _ = <span class="type">Dl</span>.dlopen ~filename:<span class="string">&quot;libseccomp.so.2&quot;</span> ~flags:[ <span class="type">Dl</span>.<span class="type">RTLD_NOW</span> ] <span class="keyword">in</span></span><br><span class="line"> print_endline <span class="string">&quot;Library loaded successfully&quot;</span></span><br><span class="line"> <span class="keyword">with</span></span><br><span class="line"> | <span class="type">Dl</span>.<span class="type">DL_error</span> msg -&gt;</span><br><span class="line"> <span class="type">Printf</span>.eprintf <span class="string">&quot;Failed to load library: %s\n&quot;</span> msg</span><br></pre></td></tr></table></figure>
+<h3 id="2-检查函数绑定"><a class="header-anchor" href="#2-检查函数绑定">¶</a>2. 检查函数绑定</h3>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="literal">()</span> =</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> <span class="keyword">let</span> _ = <span class="type">Foreign</span>.foreign ~from:libseccomp <span class="string">&quot;seccomp_init&quot;</span> </span><br><span class="line"> (<span class="type">Ctypes</span>.uint32_t @-&gt; returning <span class="type">Ctypes</span>.ptr <span class="type">Ctypes</span>.void) <span class="keyword">in</span></span><br><span class="line"> print_endline <span class="string">&quot;Function bound successfully&quot;</span></span><br><span class="line"> <span class="keyword">with</span></span><br><span class="line"> | _ -&gt;</span><br><span class="line"> print_endline <span class="string">&quot;Function binding failed&quot;</span></span><br></pre></td></tr></table></figure>
+<h3 id="3-检查结构体大小"><a class="header-anchor" href="#3-检查结构体大小">¶</a>3. 检查结构体大小</h3>
+<figure class="highlight ocaml"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="literal">()</span> =</span><br><span class="line"> <span class="keyword">let</span> s = structure <span class="string">&quot;scmp_arg_cmp&quot;</span> <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">let</span> _ = field s <span class="string">&quot;arg&quot;</span> uint <span class="keyword">in</span></span><br><span class="line"> seal s;</span><br><span class="line"> <span class="type">Printf</span>.printf <span class="string">&quot;Struct size: %d\n&quot;</span> (sizeof s)</span><br></pre></td></tr></table></figure>
+<h2 id="参考资源"><a class="header-anchor" href="#参考资源">¶</a>参考资源</h2>
+<ul>
+<li>CTypes 官方文档: <a href="https://github.com/yallop/ocaml-ctypes">https://github.com/yallop/ocaml-ctypes</a></li>
+<li>CTypes 教程: <a href="https://github.com/yallop/ocaml-ctypes/wiki/ctypes-tutorial">https://github.com/yallop/ocaml-ctypes/wiki/ctypes-tutorial</a></li>
+<li>CTypes Wiki: <a href="https://github.com/yallop/ocaml-ctypes/wiki">https://github.com/yallop/ocaml-ctypes/wiki</a></li>
+<li>libseccomp 文档: <a href="https://man7.org/linux/man-pages/man3/seccomp.3.html">https://man7.org/linux/man-pages/man3/seccomp.3.html</a></li>
+<li>bonding 项目: <a href="https://github.com/muqiuhan/bonding">https://github.com/muqiuhan/bonding</a></li>
+</ul>
+]]></content>
+ <tags>
+ <tag>Technique</tag>
+ </tags>
+ </entry>
+ <entry>
<title>在F#中处理复杂依赖注入的实践指南</title>
<url>/2025/03/31/%E5%9C%A8F-%E4%B8%AD%E5%A4%84%E7%90%86%E5%A4%8D%E6%9D%82%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E7%9A%84%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/</url>
<content><![CDATA[<h2 id="一、传统手艺:Partial-Application"><a class="header-anchor" href="#一、传统手艺:Partial-Application">¶</a>一、传统手艺:Partial Application</h2>
diff --git a/tags/Technique/index.html b/tags/Technique/index.html
index a5b83828..f39ecfae 100644
--- a/tags/Technique/index.html
+++ b/tags/Technique/index.html
@@ -172,6 +172,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2026/03/22/%E5%9C%A8-OCaml-%E4%B8%AD%E9%80%9A%E8%BF%87-CTypes-%E7%BB%91%E5%AE%9A-libseccomp/">
+
+ 在 OCaml 中通过 CTypes 绑定 libseccomp
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 22, 2026</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2026/03/21/%E5%B0%8F%E8%B0%88-NestJS-Zod/">
小谈 NestJS Zod
@@ -329,24 +347,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2025/05/27/vertical-slicing-practice/">
-
- Vertical slicing 在开发中的实践探索
-
- </a>
- </div>
-
- <span class="post-date">May 27, 2025</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/tags/Technique/page/2/index.html b/tags/Technique/page/2/index.html
index d2488b16..8ad67e54 100644
--- a/tags/Technique/page/2/index.html
+++ b/tags/Technique/page/2/index.html
@@ -172,6 +172,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2025/05/27/vertical-slicing-practice/">
+
+ Vertical slicing 在开发中的实践探索
+
+ </a>
+ </div>
+
+ <span class="post-date">May 27, 2025</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2025/05/08/Multiplayer-Collaborative-Systems-tips/">
多人协作系统中的实现策略 (CRDT,锁等实现方案)
@@ -327,24 +345,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2025/03/20/TDD-%E5%92%8C-DDD-%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B0%8F%E6%83%B3%E6%B3%95/">
-
- TDD 和 DDD 的一些小想法
-
- </a>
- </div>
-
- <span class="post-date">Mar 20, 2025</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/tags/Technique/page/3/index.html b/tags/Technique/page/3/index.html
index 8b84ad4b..d2f2d7fc 100644
--- a/tags/Technique/page/3/index.html
+++ b/tags/Technique/page/3/index.html
@@ -172,6 +172,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2025/03/20/TDD-%E5%92%8C-DDD-%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B0%8F%E6%83%B3%E6%B3%95/">
+
+ TDD 和 DDD 的一些小想法
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 20, 2025</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2025/03/19/%E9%A2%86%E5%9F%9F%E9%A9%B1%E5%8A%A8%E8%AE%BE%E8%AE%A1%E4%B8%AD%E8%81%9A%E5%90%88%E6%A0%B9%E6%8C%81%E4%B9%85%E5%8C%96%E5%92%8C%E4%BA%8B%E4%BB%B6%E5%8F%91%E5%B8%83%E5%8F%AF%E8%83%BD%E5%AF%BC%E8%87%B4%E6%95%B0%E6%8D%AE%E4%B8%8D%E4%B8%80%E8%87%B4%E9%97%AE%E9%A2%98/">
领域驱动设计中聚合根持久化和事件发布可能导致数据不一致问题
@@ -329,24 +347,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/10/18/Building-custom-fibers-library-in-FSharp/">
-
- Building custom fibers library in FSharp
-
- </a>
- </div>
-
- <span class="post-date">Oct 18, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/tags/Technique/page/4/index.html b/tags/Technique/page/4/index.html
index 72d9f8cf..e544446b 100644
--- a/tags/Technique/page/4/index.html
+++ b/tags/Technique/page/4/index.html
@@ -172,6 +172,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/10/18/Building-custom-fibers-library-in-FSharp/">
+
+ Building custom fibers library in FSharp
+
+ </a>
+ </div>
+
+ <span class="post-date">Oct 18, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/10/18/Dealing-with-complex-dependency-injection-in-FSharp/">
Dealing with complex dependency injection in FSharp
@@ -327,24 +345,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/05/20/Rescript-genType-import-%E9%97%AE%E9%A2%98/">
-
- Rescript @genType import 问题
-
- </a>
- </div>
-
- <span class="post-date">May 20, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/tags/Technique/page/5/index.html b/tags/Technique/page/5/index.html
index 6bdbd84a..0492a6d7 100644
--- a/tags/Technique/page/5/index.html
+++ b/tags/Technique/page/5/index.html
@@ -172,6 +172,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/05/20/Rescript-genType-import-%E9%97%AE%E9%A2%98/">
+
+ Rescript @genType import 问题
+
+ </a>
+ </div>
+
+ <span class="post-date">May 20, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/04/03/shadow-cljs-proxy/">
shadow-cljs 怎么设置代理
@@ -329,24 +347,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/08/15/tick-thread%E5%9C%A8Multicore-OCaml%E4%B8%AD%E7%9A%84%E4%BD%9C%E7%94%A8/">
-
- tick thread在Multicore OCaml中的作用
-
- </a>
- </div>
-
- <span class="post-date">Aug 15, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/tags/Technique/page/6/index.html b/tags/Technique/page/6/index.html
index 80dcf780..5c1250e8 100644
--- a/tags/Technique/page/6/index.html
+++ b/tags/Technique/page/6/index.html
@@ -172,6 +172,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/08/15/tick-thread%E5%9C%A8Multicore-OCaml%E4%B8%AD%E7%9A%84%E4%BD%9C%E7%94%A8/">
+
+ tick thread在Multicore OCaml中的作用
+
+ </a>
+ </div>
+
+ <span class="post-date">Aug 15, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/08/15/poll-error-attribute-in-OCaml/">
使用 [@poll error] 实现线程安全的数据结构