[{"content":" 我们无法用制造问题的同一思维层次来解决问题。1 \u0026mdash; “阿尔伯特·爱因斯坦”\n一致性挑战 我第一次接触 X Macros，是在排查一个越界访问问题的时候。\n下面是一段简化后的示例代码：\n// a.h typedef enum { SYS_OK, SYS_ERR_TIMEOUT, SYS_ERR_BUSY, SYS_ERR_INVALID_ARG, SYS_ERR_NOT_FOUND } SysState; // a.c const char *state_desc[] = { [SYS_OK] = \u0026#34;System OK\u0026#34;, [SYS_ERR_TIMEOUT] = \u0026#34;Timeout\u0026#34;, [SYS_ERR_BUSY] = \u0026#34;System Busy\u0026#34;, [SYS_ERR_INVALID_ARG] = \u0026#34;Invalid Argument\u0026#34; // 这里没有 SYS_ERR_NOT_FOUND }; int main() { printf(\u0026#34;State = %s\\n\u0026#34;, state_desc[SYS_ERR_NOT_FOUND]); } 问题显而易见：枚举中新增了SYS_ERR_NOT_FOUND，但字符串数组没有同步更新。\n程序执行到 state_desc[SYS_ERR_NOT_FOUND]时会越过数组边界，最终导致崩溃。\n只需要在字符串数组里加一行就可以防止访问越界。但是这样的修复并不能防止同类的问题再次发生。原因在于，这里实际上维护了两份必须保持一致的数据：\n一份是枚举定义 一份是字符串表 只要这两份数据分散在不同位置，并且依赖人工同步维护，就很容易再次出现类似问题。\n这里需要一种能够防止同类问题再次发生的方案。\nX Macros 在查找解决方案的过程中，我看到了 Randy Meyers 发表在 Dr. Dobb\u0026rsquo;s Journal 上的一篇文章 The New C: X Macros 2。文章介绍了一种利用 C 预处理器实现简单代码生成的技巧。\n这项技巧通常被称为 X Macros 。\n它的基本思路是维护一份统一的数据列表，然后通过不同的宏定义方式生成所需要的代码结构。\n我们可以对前面的例子做一个简单改造, 来展示 X Macros 的用法。\n首先定义单一数据源：\n#define SYS_STATE_LIST \\ X(SYS_OK, \u0026#34;System OK\u0026#34;) \\ X(SYS_ERR_TIMEOUT, \u0026#34;Timeout\u0026#34;) \\ X(SYS_ERR_BUSY, \u0026#34;System Busy\u0026#34;) \\ X(SYS_ERR_INVALID_ARG, \u0026#34;Invalid Argument\u0026#34;) \\ X(SYS_ERR_NOT_FOUND, \u0026#34;Not Found\u0026#34;) 这里创建了一个包含所有信息的“主列表”，每个条目都包裹在一个占位宏 X() 中 。X()就是这项技巧名称的由来，并没有什么特殊含义，当然也可以用其他名字。\n接下来，我们定义宏 X ，利用这份数据生成枚举类型：\n#define X(name, desc) name, typedef enum { SYS_STATE_LIST } SysState; #undef X 然后，重新定义宏 X ，让同一份数据展开成字符串描述数组：\n#define X(name, desc) [name] = desc, const char *state_desc[] = { SYS_STATE_LIST }; #undef X 对于熟悉 Python 或 Java 等动态语言的开发者，上述例子可能让您联想到反射机制。可以看到，X Macros 把分散的信息收拢到单一的数据表中，通过对宏 X 的多次定义和撤销 (#undef)，我们可以将上述数据“注入”到不同的模板中 。不论你添加还是删除数据， 甚至前后移动都不会带来不一致的问题。\n这项技巧适用于许多需要维护数据一致性的场合：\n错误码，状态，事件类型及描述的管理 命令，协议定义，解析代码的生成 序列化与反序列化 配置数据初始化与读写代码的生成 进阶提示 X Macros 带来的好处并不是没有代价的。宏的写法会略微降低代码的直观可读性，可调试性。\n提高可读性：可以参考 Andrew Lucas 的建议，把宏 X 作为参数传递给数据列表，以提高代码的可读性3。\n大数据量：如果数据量足够大，可能会碰到编译器行长度限制。 Randy Meyers在文章中已经给出了方案 —— 把数据列表定义在一个独立的 .def 或者 .h 文件中, 在需要展开的地方用 #include 包含进来。\n预编译检查：可以通过 gcc -E 查看预编译结果。如果你用 IDE 结合语言服务器的话， 就更容易了。\n团队沟通：这项技巧并非人人皆知，最好在代码中加上必要注释，并向团队成员说明其用途。\n适度使用：与其他的宏技巧一样，应当谨慎使用，避免滥用。\n总结 X Macros 本质上是一种基于 C/C++ 预处理器的简单元编程技巧。在缺乏反射或代码生成机制的情况下，这种方法为 C/C++ 提供了一种轻量而实用的工程技巧。\n它通过维护一份单一数据源（Single Source of Truth），再利用宏展开生成不同的代码结构，从而避免多处重复定义带来的同步问题。\n它的历史可以溯源到上世纪60年代的汇编语言编程，传承至今， 在需要维护大量一致性数据的场景中，它仍然有不可替代的价值, 是每一个C/C++工程师的必备工具。\n溯源的时候，发现这个：https://www.quora.com/Einstein-said-that-you-cannot-solve-a-problem-from-the-same-level-of-consciousness-that-created-it-What-did-he-mean-with-it-Can-you-use-a-concrete-example\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nRandy Meyers. \u0026ldquo;The New C: X macros\u0026rdquo;, Dr.Dobb\u0026rsquo;s 2001, 可访问链接：https://jacobfilipp.com/DrDobbs/articles/CUJ/2001/0105/meyers/meyers.htm\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nAndrew Lucas. \u0026ldquo;Reduce C-language coding errors with X macros\u0026rdquo;, Embedded.com, 2013.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","permalink":"https://victorhge.github.io/zh/posts/xmacro/","summary":"\u003cblockquote\u003e\n\u003cp\u003e我们无法用制造问题的同一思维层次来解决问题。\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\n\u0026mdash; “阿尔伯特·爱因斯坦”\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一致性挑战\"\u003e一致性挑战\u003c/h2\u003e\n\u003cp\u003e我第一次接触 X Macros，是在排查一个越界访问问题的时候。\u003c/p\u003e\n\u003cp\u003e下面是一段简化后的示例代码：\u003c/p\u003e","title":"X Macros"},{"content":"最近在折腾 GitHub Pages ，搭了个小博客。几乎没费什么力气，选一套模板，配几行参数， 一个网站就建好了。既然已经折腾起来，我索性把博客做成了中英文两个版本。于是每一篇文章，我都先用中文写，再用 AI 工具帮忙翻译成英文。如今大模型翻译的效果已经相当不错，大多数时候只要稍作修改，就可以发布。\n不过每次校对译文的时候，我心里总有些说不清的别扭。句子没有错，意思也很准确，可读起来总像隔着一层东西。\n直到有一天，我写了一篇稍长一点的文章，大模型译文带来的奇怪感觉愈发强烈。我就把几篇文章的中英文仔细对照着看，一瞬间惊得我头皮发麻，直冒冷汗，就像照镜子时，发现镜中人不是自己一样惊悚。\n我想起了古人的那句感叹： 侯门一入深如海，从此萧郎是路人。\n问题不在翻译，而在我写的中文。\n这些年我待过的几家公司都是外企。邮件是英文，文档是英文，代码和注释也是英文。这么多年，我几乎没有正经用中文写过什么东西。\n中文当然也在用，但多是在聊天、发消息、日常沟通。\n再看看我最近写的几篇中文博文，和大模型直译的英文一对比，几乎是一字一句直接对应，表面是中文的字词，骨架却是英文的结构，几乎可以直接对译。\n我这才明白，原来这些年在日复一日的工作，不知不觉养成了一种习惯，一旦进入“写作” 的状态，心中所想会自动从英文里绕一圈，再落回中文。我哪里是在用中文写作，不过是用汉字给英文的行文逻辑贴了层皮，读起来像是蹩脚的英文翻译。\n这种情况，应该不止我才有。\n很多双语使用者的书表达面，几乎完全依赖英文。中文逐渐退回到口表达里，很少再承担复杂、深度的表达任务。\n久而久之，不常用的语言能力就会开始退化。直到某一天需要认真写一段中文时，才发现已经没那么顺手了。\n这就是典型的“用进废退”。\n所幸心血来潮开始折腾这个博客，让我惊醒并发现这个问题。解决这个问题似乎只有一条路 ——重新把中文写起来。这个博客也让我开始走上了这条路。\n在写下这些文字的时候，我开始审视脑中跳出的每一个句子：这句话可不可以直译为英文？\n","permalink":"https://victorhge.github.io/zh/posts/translationese/","summary":"\u003cp\u003e最近在折腾 GitHub Pages ，搭了个小博客。几乎没费什么力气，选一套模板，配几行参数，\n一个网站就建好了。既然已经折腾起来，我索性把博客做成了中英文两个版本。于是每一篇文章，我都先用中文写，再用 AI 工具帮忙翻译成英文。如今大模型翻译的效果已经相当不错，大多数时候只要稍作修改，就可以发布。\u003c/p\u003e","title":"中文还是英文？"},{"content":"Emacs 拥有大量默认快捷键绑定，不可避免地会与系统快捷键发生冲突，对于 Emacs 新手来说，如何解决这些冲突是一项重大挑战。\n其中最常见的一个是命令 set-mark-command ，默认键绑定是C-SPC(Ctrl+Space) 1。在 Windows 系统中， Ctrl+Space 默认用于切换中英文输入法，这个冲突会导致在 Windows 下的 Emacs 无法使用 Ctrl+Space 进行标记设置。\n要解决这个问题，可以选择使用 set-mark-command 的备用快捷键 C-@ ，或者禁用 Windows 的 Ctrl+Space 功能。此外，由于空格键左右手操作同样方便，还有“第三选择: 把 Ctrl+Space 一分为二，一侧的 Ctrl+Space 留给 Windows，另一侧释放出来供 Emacs 或其他应用程序使用。这正是我采用的方案，因为这样我就可以保留用两个大拇指操作的习惯。\n在最新的 Windows 11 下可以这么做：\n找到 Settings \u0026gt; Time \u0026amp; Language \u0026gt; Language \u0026amp; region \u0026gt; Options \u0026gt; Microsoft Pinyin \u0026gt; Keys , 取消勾选 Ctrl + Space ，保留勾选 Shift 。\n打开 PowerToys (没有的话先安装）， 在 Keyboard Manager 功能中，添加一个快捷键重映射：将 Ctrl(Left) + Space 映射为 Shift 或者 Win(Left) + Space 。\n此前的 Windows 版本存在一个 UI 无法更改相关设置的缺陷2，可以通过修改注册表解决：\n找到 HKEY_CURRENT_USER\\Control Panel\\Input Method\\Hot Keys\\00000010 条目， 将其中的 Key Modifiers 修改为 02 80 00 00 , 或者 02 40 00 00 3。 如果希望将更改应用于所有新用户，还需要在 HKEY_USERS\\.DEFAULT\\Control Panel\\Input Method\\Hot Keys\\00000010 中进行同样的修改。 修改完成后，重启电脑即可生效。\n似乎只有 Ctrl+Space 才能“劈开”， 其他的键天然的只对一只手来说顺手，比如Ctrl+a 就已经隐含着 Right Ctrl+a 。\n脚注 Emacs 有独特的键位的缩写习惯，用 C-SPC 代表 Ctrl+Space 。本文暂且保留常用习惯。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhttps://superuser.com/questions/327479/ctrl-space-always-toggles-chinese-ime-windows-7\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n02 指Ctrl, 80 指左侧，40指右侧https://learn.microsoft.com/en-us/windows/win32/tsf/tf-mod--constants\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","permalink":"https://victorhge.github.io/zh/posts/ctrl_space/","summary":"\u003cp\u003eEmacs 拥有大量默认快捷键绑定，不可避免地会与系统快捷键发生冲突，对于 Emacs 新手来说，如何解决这些冲突是一项重大挑战。\u003c/p\u003e\n\u003cp\u003e其中最常见的一个是命令 \u003ccode\u003eset-mark-command\u003c/code\u003e ，默认键绑定是\u003ccode\u003eC-SPC(Ctrl+Space)\u003c/code\u003e \u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e。在 Windows 系统中， \u003ccode\u003eCtrl+Space\u003c/code\u003e 默认用于切换中英文输入法，这个冲突会导致在 Windows 下的 Emacs 无法使用 \u003ccode\u003eCtrl+Space\u003c/code\u003e 进行标记设置。\u003c/p\u003e","title":"劈开 Ctrl+Space"},{"content":" 这篇修改过翻译腔的问题。原文放在后面，留作纪念。\n修订版 这是我所有计算机上的必备配置，全是因为 Emacs。\nEmacs 最早是 TECO 编辑器的宏程序(Editing MACroS)。当年开发者用的键盘，Ctrl 键在空格键两侧1，大拇指就能按，所以 Emacs 最常用的命令都是用 Ctrl 修饰。不知为何，后来的个人电脑标准键盘把 Ctrl 移到了最边上，在这些键盘上使用 Emacs 就显得不太顺手。\n我的解决办法是把 Ctrl 和 Alt 互换，让 Ctrl 回到大拇指能按到的位置。这样就可以用大拇指来操作 Emacs 最常用的命令，既轻松又高效。\n具体方法：\nWindows 用 SharpKeys 改键位； Linux (GNOME 桌面)用 GNOME Tweak Tool直接设置2。 这是一个“一旦用上就再也回不去”的设置。它还有一个“隐藏功能”——让试图操作我键盘的人抓狂。\n原版 这是我所有所用计算机上的必备配置，全是因为 Emacs。\nEmacs 最初是为 TECO 编辑器开发的宏程序(Editing MACroS)。当时的开发者所用键盘布局将 Ctrl 键放在空格键两侧1，便于大拇指操作。不知什么原因，微型机普及后的标准键盘却将 Ctrl 键移至最外侧，只能靠小拇指操作。这种设计容易导致小拇指疲劳。\n因此，我选择交换 Ctrl 和 Alt 键，让 Ctrl 键重回大拇指位置，既缓解小拇指负担，又显著提升效率。\n我的具体方法是：\nWindows：通过 SharpKeys 实现键位重映射。 Linux (GNOME 桌面)：GNOME Tweak Tool可轻松完成设置2。 这是一个“一旦用上就回不去”的配置。它还有一个“隐藏功能”——让其他尝试操作我键盘的人抓狂。\n脚注： Knight Keyboard http://xahlee.info/kbd/knight_keyboard.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nGNOME Treak Tool https://askubuntu.com/questions/885045/how-to-swap-ctrl-and-alt-keys-in-ubuntu-16-04/885047\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","permalink":"https://victorhge.github.io/zh/posts/swap_ctrl_alt/","summary":"\u003cfigure\u003e\n    \u003cimg loading=\"lazy\" src=\"/ox-hugo/lisp-machine-keyboard-2.jpg\"/\u003e \n\u003c/figure\u003e\n\n\u003cblockquote\u003e\n\u003cp\u003e这篇修改过\u003cstrong\u003e翻译腔\u003c/strong\u003e的问题。原文放在后面，留作纪念。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"修订版\"\u003e修订版\u003c/h2\u003e\n\u003cp\u003e这是我所有计算机上的必备配置，全是因为 Emacs。\u003c/p\u003e\n\u003cp\u003eEmacs 最早是 TECO 编辑器的宏程序(\u003ca href=\"https://blog.djmnet.org/2008/08/05/origin-of-emacs/\"\u003eEditing MACroS\u003c/a\u003e)。当年开发者用的键盘，Ctrl 键在空格键两侧\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e，大拇指就能按，所以 Emacs 最常用的命令都是用 Ctrl 修饰。不知为何，后来的个人电脑标准键盘把 Ctrl 移到了最边上，在这些键盘上使用 Emacs 就显得不太顺手。\u003c/p\u003e","title":"交换 Ctrl 和 Alt 键"},{"content":"今天考察了一下各个大语言模型对我的印象。下面是截图。\n豆包 - 用的最久 豆包真敢想像，不知道盗了谁的图。\n小爱 - 手机自带 小爱太糊弄了，直接一张网红脸。\n千问 - 最近使用 千问有一个故作惊讶的表情。\nChatGpt - 元老 ChatGpt开始妄图要我的照片。我一再强调可以随机生成才开始干活。\nGemini - 上班用的多 Gemini生成的图细节丰富，可是偏离的主题，还犟得很，纠正不过来。 ","permalink":"https://victorhge.github.io/zh/posts/selfie/","summary":"\u003cp\u003e今天考察了一下各个大语言模型对我的印象。下面是截图。\u003c/p\u003e\n\u003ch2 id=\"豆包-用的最久\"\u003e豆包 - 用的最久\u003c/h2\u003e\n\u003cp\u003e豆包真敢想像，不知道盗了谁的图。\u003cimg loading=\"lazy\" src=\"/ox-hugo/selfie_doubao.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"小爱-手机自带\"\u003e小爱 - 手机自带\u003c/h2\u003e\n\u003cp\u003e小爱太糊弄了，直接一张网红脸。\u003cimg loading=\"lazy\" src=\"/ox-hugo/selfie_mi.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"千问-最近使用\"\u003e千问 - 最近使用\u003c/h2\u003e\n\u003cp\u003e千问有一个故作惊讶的表情。\u003cimg loading=\"lazy\" src=\"/ox-hugo/selfie_qwen.jpg\"\u003e\u003c/p\u003e","title":"  \"我的自拍照”\n  "}]