diff --git a/404.html b/404.html new file mode 100644 index 0000000..f8414f0 --- /dev/null +++ b/404.html @@ -0,0 +1,3 @@ + +404 Not Found +

404 Not Found

diff --git a/BingSiteAuth.xml b/BingSiteAuth.xml new file mode 100644 index 0000000..97f0152 --- /dev/null +++ b/BingSiteAuth.xml @@ -0,0 +1,4 @@ + + + A975401FAA4E9AC54E399A80B7205BB2 + diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..db46bba --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +blog.pg999w.top diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..0a74207 --- /dev/null +++ b/about/index.html @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

I'm a CS graduate student of SJTU. My undergraduate degree was completed at NWPU.

+

About this blog

+

See this post.

+

More info about me

+

GitHub +Wikipedia

+

Friends

+

dykai https://blog.ykai.top/

+

Kehan https://blog.kehan.xyz/

+

zzsqwq https://blog.zzsqwq.cn/

+

Jiali https://anlarry.github.io/

+

Macromogic https://macromogic.xyz

+

haotian https://thehaotian.com/

+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/acm-error-set/index.html b/acm-error-set/index.html new file mode 100644 index 0000000..cc15d5d --- /dev/null +++ b/acm-error-set/index.html @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + pg999w's blog - ACM 错误集 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ ACM 错误集 +

+ +
+ +
+

以下是平时做题时造成不能一遍AC的原因。

+

1. Codeforces Round #510 (Div. 2) Problem B

+
+

⚠ 样例错误

+
+

把位操作&|弄反。

+

2. PAT (Advanced Level) Practice 1001

+

数字 1000 写成 100。

+

3. PAT (Advanced Level) Practice 1002

+

没有注意到题中 non-zero 的条件。

+

4. PAT (Advanced Level) Practice 1003

+

忘记小顶堆应该使用 priority_queue<T, vector<T>, greater<>>

+

5. PAT (Advanced Level) Practice 1022

+

没有输出数字的前导零。

+

printf("%013lf", num);的等价方法:

+
cout << setfill('0') << setw(13) << num;
+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+ACM 错误集Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/baoyan/index.html b/baoyan/index.html new file mode 100644 index 0000000..d22a250 --- /dev/null +++ b/baoyan/index.html @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + pg999w's blog - 保研经历总结 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 保研经历总结 +

+ +
+ +
+

2020年保研算是告一段落了。因为基地里面要组织给学弟学妹的分享活动,借此机会正好来写一篇总结。

+

0 基本情况

+
    +
  • 综合成绩:第 1 名
  • +
  • 学分积:9%
  • +
  • 六级:549
  • +
  • CSP 成绩:累计前 0.4%
  • +
+

我的课程成绩也不算太差,但是由于强者实在是太多,学分积只能排到专业 9%。但现在来看大一大二我做对了两件事,一件就是参加了很多比赛,最后综合成绩拿到了专业第一名;另一件就是大一乘着高中时候打 OI 的老本还在,CSP 考了一个还算不错的成绩。

+

1 准备阶段

+

大三下开始之后就逐渐在关注保研相关的事情了。在 3 月到 6 月底的这段时间里,基本上就做了两件事:联系导师和海投学校。一般是报名的时候联系该学校的老师。学校的报名信息完全参考CSSummerCamp,报名的时候需要准备特别多的材料,很多需要复印或者教务处盖章,第一次报名的时候非常痛苦,准备了很长时间。但是后面的材料大多可以复用,所以到最后五分钟就能完成一个学校的报名。

+

报名的痛苦是一次性的,但是联系导师所耗费的精力却不会逐渐降为零。整个大三下学期,我在寻找导师的时候都会面临着灵魂拷问:我是想搞学术还是搞工程?我想做哪个方向?希望 push 的老师还是放任的老师?要不要读博?这些问题在联系导师阶段也许并没有直接影响,但是这些迟早需要解决的问题确实造成了极大的焦虑。

+

进入五月份,各个学校相继开奖,下面是我的失败史:

+
    +
  • 清北:想都没想过。
  • +
  • 南京大学:同时报了计算机科学与技术系和软件学院,全部未入营。
  • +
  • 哈工大深圳校区:未入营。
  • +
  • 复旦大学:未入营。
  • +
  • 哈工大海量数据计算研究中心:这相当于是实验室的单独初筛,简历就没有通过。从此我对哈工大失去兴趣,再不尝试。
  • +
+

入营的学校包括上交软院,北航计院,人大信院,同济软院,中山计院。其中人大,北航和同济都没有发邮件通知,而是首先在官网上通知的。这几个学校的消息都是身边的几位同学告诉我的。(在此感谢他们!)

+

既然已经拿到了华五和北航的入营,那同济和中山大学我就不考虑了,当时就直接拒掉了。

+

2 面试

+

七月初是各个学校集中举行夏令营考核的时候。但是运气非常好的是,我入营的三个学校考核时间刚好是首尾相接,再得益于疫情期间的线上面试,我得以完整参加三个夏令营。

+
    +
  1. +

    人大信院

    +

    人大的笔试可以用 CSP 成绩来抵,因为有比较高的 CSP 成绩,所以我不仅能免笔试,在最后笔试面试合起来也能拿到一个比较高的名次。

    +

    由于免去了笔试,其他人笔试的时候我在给老师发邮件,看看能不能先确定一个老师。人大的数据库比较强,我当时就表示想做数据库相关的方向。老师人很好,详细的介绍了他们实验室各个老师具体的研究方向。

    +

    笔试第二天面试。先是一个自我介绍,然后抽一套题,我抽到的题先让描述 KMP 算法,然后描述 RAID。然后换一个老师问了数据库的问题,关于可串行化的。这些问题都在本科专业课学习的范围之内。最后让我用英文介绍了我操作系统课上在 miniOS 上做的工作。

    +
  2. +
  3. +

    北航计院

    +

    北航的笔试可以用 CSP 成绩来抵,因为有比较高的 CSP 成绩,所以我不仅能免笔试,在最后笔试面试合起来也能拿到一个比较高的名次。

    +

    由于免去了笔试,其他人笔试的时候我在给老师发邮件,看看能不能先确定一个老师。找了一个高性能计算方向的老师。老师当时加了微信,但是没有给承诺。

    +

    笔试第二天面试。先是北航传统的政治题,我答的不好。然后英文自我介绍。然后高数/线代二选一抽题,我选线代。问了怎么旋转一个向量,以及行列式的物理意义。最后问了你有什么优点。

    +
  4. +
  5. +

    上交软院

    +

    在家中面试完北航我就赶赴上海参加线下的夏令营了。北航和人大的面试基本就是二十分钟左右,我面完感觉挺稳的,而交大这边报的 IPADS 实验室好像有点过于强了,所以交大这边也不太报希望,就当是交流学习了。没想到后面的考核有充分的时间准备,就显得不是那么困难。感觉自己又有戏了。

    +

    交大软院很扣门,食宿路费都自行解决,不报销。幸好有个交大的朋友慷慨地借了我一张饭卡,极大的缓解了伙食的问题。(在此对这位同学表示非常的感谢!)上海的气候实在算不上宜人,当时连续多日高温天气,我除了最后一天完成去外滩逛了一逛,便再没有去别的地方游玩了。

    +

    上交软院的考核较为固定,网上有非常好的资料,这里就不赘述了。

    +
  6. +
+

北航的优营在我还在上海时就出了。北航要求当晚就联系导师签一个文件。因为加了微信,我立马联系了之前那个老师,很快就把文件给签好了。

+

回家后获得了人大的优营。联系了一个导师,表示自己有名额并欢迎我加入。

+

此时 7 月还没有结束。为了避免我后面太无聊,我又去找了实习。但是企业面试实在是比高校面试耗时长多了,还要面很多轮,最后八月底我才拿到实习 offer。实习一周之后上交才把 offer 发下来。

+

3 尾声

+

暑假把 offer 拿到,剩下的事就比较简单了。由于我比较佛,9 月预推免就不再尝试别的学校了。交大这边确定下来后,礼貌地给北航和人大的老师发了鸽信。然后就等学校确定保研名额,9 月 28 日填系统了。

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+保研经历总结Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/c-p0709/index.html b/c-p0709/index.html new file mode 100644 index 0000000..c568cf7 --- /dev/null +++ b/c-p0709/index.html @@ -0,0 +1,400 @@ + + + + + + + + + + + + + + + pg999w's blog - 从 C++ 的错误处理说起 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 从 C++ 的错误处理说起 +

+ +
+ +
+

错误处理是一个非常重要的软件工程问题。对软件中出现的非致命错误的不当处理,是几乎所有的灾难性系统故障的诱因。1 编程语言往往需要提供一些用于错误处理的语言设施,这些设施反过来会影响项目中错误处理的方式。不同的语言错误处理方式不同。例如 Java 采用基于 try-throw-catch 语法的异常机制,而 Go 语言则选择手动检测函数返回的 error 对象。一个令人惊讶的事实是,C++ 到现在还没有一个被广泛接受的错误处理方式。

+ +

尽管 C++ 当中引入了基于 try-throw-catch 的异常(Exception)处理机制,而且将其应用到了标准库当中,但这种处理方式遭到了广泛的批评。Linus Torvalds 曾说 C++ 的异常处理「从根本上坏掉了2」。一些重要的 C++ 风格指南,如《谷歌 C++ 风格指南3》、《联合打击战斗机飞行器 C++ 代码规范(JSF++)4》,都禁止使用 C++ 异常。其中制定 JSF++ 的 C++ 专家还包括了 C++ 创始人 Bjarne Stroustrup。除此之外,有调查显示 52% 的 C++ 开发者都至少有一部分项目完全禁用异常。

+

这导致了非常严重的问题。由于异常在 C++ 标准库中广泛的使用,禁用异常的 C++ 已经不是标准的 ISO C++ 而成为了一种方言。5这与标准委员会的使命,当然也与广大程序员的切身利益相违背。基于此,微软的 C++ 专家 Herb Sutter,同时也是 C++ 标准委员会的召集人,在他撰写的 C++ 标准提案 P0709 当中提出了一种解决方案。这份长达 65 页的标准提案并不晦涩难懂,相反,它深入浅出地探讨了异常处理的方方面面,并针对存在的问题给出了令人信服的解决方案。即使这个提案距离真正进入标准还遥遥无期,但阅读这份文档仍可令人受益匪浅。本文将以这份文档为蓝本,讨论一些错误处理方面的重要问题。虽然 Sutter 是在 C++ 的语境下讨论这些问题的,但本文有意弱化了 C++ 的语境,并加入了与其他语言的对比。

+

C++ 的错误处理出了什么问题?

+

为何在其他语言(Java、Python 等)中广泛应用的异常机制在 C++ 之中如此臭名昭著?因为现有的异常实际上和 C++ 的核心价值不符。主要分两点原因:

+
    +
  • 现有的异常不符合零开销抽象。异常不仅会引入多余的运行时开销,而且还会使编译产生的二进制文件急剧增大。不同的环境下增大的比例不同,Sutter 收集到的资料报告了 +15%,+16%,+38% 甚至 +52% 的文件体积增大。
  • +
  • 现有的异常处理是非确定性的。由于异常对象储存在堆上,而且用到了 RTTI 特性,无论是运行的时间,还是运行的内存占用,都不能得到保证。
  • +
+

以上的原因使得异常处理在高实时性,低内存的应用场景下变得不可接受。为了解决这个问题,标准库中引入了「error_code6」作为一种替代方案。但这个方案并没有解决所有问题,反而把问题搞复杂了。新加入标准库的组件如 <filesystem> 陷入了需要同时支持两种错误处理方案的尴尬境地。标准库的两个方案都有缺陷,人们开始寻求标准之外基于库的解决方案,ExpectedOutcome 就是这样的尝试。但它们最后达成的效果,只能用 xkcd 927 来描述:

+

xkcd 927

+

重构「错误处理」

+

前面提到的已有的对于 C++ 错误处理的尝试并非徒劳无功,它们为后来的设计者提供了经验和教训。在充分理解现有方案的优点和弊端之后,Herb Sutter 准备提供一个新的大一统方案,解决之前提出的所有问题。这当然有陷入 xkcd 927 的「N+1 种标准」危险,但解决方案也是有的:只要新的方案能够至少比一种现有的方案绝对更优,亦即,在完全涵盖某种方案的优点的前提下,克服其一部分缺点,就可以完全取而代之,至少不会使现有的方案增加。

+

当然仅仅避免「N+1」的问题当然还不够。为了提出一个真正能满足各种需求的方案,Sutter 对错误处理做了认真的分析。在 P0709 中包含了许多重要的见解。

+
+

注意在本文中「错误」和「异常」的用法区别。「错误(error)」是一个通用的软件工程概念,泛指程序编写时和运行时出现的各种异常情况,而「异常(exception)」是一个特定的 C++ 概念。

+
+

逻辑错误和内存错误应特殊处理

+

逻辑错误也就是 bug,是程序员由于考虑不周产生的错误。广受诟病的 ArgumentNullException 以及对负数开平方根,数组下标溢出错误等,都是属于这一类型。Sutter 指出,这些错误总是会导致状态损坏,这种编写时的错误绝不可能在运行时得到恢复,所以程序无需向上层代码报告错误,而是应该特殊专门处理(比如直接崩溃掉)。这一部分问题有望在尚未合并的 C++ 特性 Contracts 中得到解决。

+

而对于内存分配错误(allocation failure),基于以下的原因,Sutter 认为也不应该抛出异常。

+
    +
  • 根本没有人能真正正确的处理内存异常。 Sutter 与 Clow 在 2019 年编写了一套测试框架 babb,对 Visual C++ 标准库的 STL 部分,Microsoft PowerPoint 的 File>Open 功能和 Microsoft Word7 做了测试。这些代码都宣称在编写时仔细考虑了内存错误,但是都没有通过测试。
  • +
  • 很少有人花心思编写代码来正确的处理内存异常。 尽管 VC++ STL 存在不能正确抛出 bad_alloc 的问题,但 2015-2019 年间总共只收到了 6 次有关的错误报告。由 bad_alloc 派生出的 bad_array_new_length 根本就没人使用
  • +
  • 由于各个平台的内存管理实现不同,根本不可能在所有平台上正确实现堆分配错误的抛出。
  • +
+

令人惊讶的是,将逻辑错误和内存错误移除出正常的异常处理机制之后,90% 的函数都可以变成 noexcept8。这将带来巨大影响。在一次内部会议上,微软的软件架构师 Pavel Curtis 曾提到,如果 STL 中引入了 contracts,并且 bad_alloc 能够立刻停止程序(fail fast),那么整个 Windows 代码都可以直接使用标准的 STL 库。

+

应当抛出相同类型、不同值的错误对象,而不是让错误类型属于某个继承树

+

历史确实是在曲折中前进的。在错误处理方面,工业界走过的一个巨大弯路便和异常(Exception)的继承层次结构有关。Java 和 C++ 都选择了把所有的异常都挂在一个以 Exception 类(C++ 中为 std::exception)为根的一个继承树上面。这样捕获异常时,便可以在层次结构中选择一个合适的基类进行捕获。更进一步地,语言还提供了「异常规范」,把一个函数可能产生的异常作为函数的签名标注出来,以期实现更严谨的错误处理。

+

然而这一切在被加入标准之后,终于逐渐被证明是弊大于利的。现在在网上存在大量关于 Java 异常规范的批评,ISO C++ 的第二个大版本 C++11 也赶紧弃用了异常规范。而且在 C++ 这种动态性很低的语言中,不仅是异常规范,基于继承的异常层次结构也有很大问题。注意到异常捕获通常有两类场景:

+
    +
  • +

    不关心异常类型的捕获这种被称为「distant catch」的捕获往往在最终的调用端代码中执行,被 Sutter 认为是「最有价值的(this most valuable)」。由于函数的封装性,在调用树中较远处的异常捕获并不知道也不关心9发生异常的具体类型。Sutter 注意到,由于抛出的异常类型无法在编译时确定,大量的高层代码最终只好编写 catch(...) 来捕获所有类型的异常。

    +
  • +
  • +

    关心异常类型的捕获这种捕获常发生在库代码中,这些代码需要了解底层逻辑,往往伴随着「重新抛出」等操作。对于这样的情况,基于继承的异常层次结构也不如使用 error_code 的方案。因为异常只能在继承结构中向上转换,难以实现 error_code 那样对错误的组合。另外,error_code 在传播,转换方面也比 exception 做得好。

    +
  • +
+

错误总是应该得到处理

+

Sutter 指出,任何允许错误被悄无声息地忽略掉的错误处理方案,都会导致程序健壮性和安全性长期的损失,同时影响语言的声誉。但错误应该得到处理,并不意味着程序员总是应该编写大量的处理代码(像 Go 那样)。错误可以被隐式的处理,例如通过某种机制自动向上级调用者传播。

+

通向解决之道

+

基于以上的见解,Herb Sutter 给出了最终的方案。throw 关键字被用来抛出 std::error 类型,为了和传统的 throw exception 的抛出方式做区分,throw error 的函数需要加上 throws 标记。std::error 作为值类型被使用,在 catch 语句中直接按值捕获。下面是提案中的示例。

+
int safe_divide(int i, int j) throws {
+  if (j == 0)
+    throw arithmetic_errc::divide_by_zero;
+  if (i == INT_MIN && j == -1)
+    throw arithmetic_errc::integer_divide_overflows;
+  if (i % j != 0)
+    throw arithmetic_errc::not_integer_division;
+  else return i / j;
+}
+
+double caller(double i, double j, double k) throws {
+  return i + safe_divide(j, k);
+}
+
+int caller2(int i, int j) noexcept {
+  try {
+    return safe_divide(i, j);
+  } catch(error e) {
+    if (e == arithmetic_errc::divide_by_zero)
+      return 0;
+    if (e == arithmetic_errc::not_integer_division)
+      return i / j; // ignore
+    if (e == arithmetic_errc::integer_divide_overflows)
+      return INT_MIN;
+    // Adding a new enum value “can” cause a compiler
+    // warning here, forcing an update of the code (see Note).
+  }
+}
+
+

同归却殊途

+

尽管 P0709 的方案足够有说服力,在 C++ 委员会全球会议中的投票中,这份提案的许多部分得到了没有反对票的支持。但将这样一个不小的语言改动合并到 C++ 标准之中仍旧是一件困难的事情。自 2018 年 5 月 2 日 P0709R0 提交之后,这份提案迄今已经修订了 5 个版本,然而这份提案仍然存在未解决问题。它不仅无法进入 C++20 的标准之中,甚至 C++23 都不一定能包含全新的错误处理机制。

+

然而正如前面所反复提到的那样,错误处理是一个非常通用的概念,经验和教训在各个语言之中都是通用的。现代语言如 Haskell,Rust 和 Swift 在错误处理方面都得出了一致的结论。借助 Fehler 库,上面的 C++ 示例代码在 Rust 中几乎具有相同的实现方式和语义:

+
#[throws(ArithmeticError)]
+fn safe_divide(i: i32, j: i32) -> i32 {
+    if j == 0 {
+        throw!(ArithmeticError::DivideByZero);
+    }
+    if i == i32::MIN && j == -1 {
+        throw!(ArithmeticError::IntegerDivideOverflows);
+    }
+    if i % j != 0 {
+        throw!(ArithmeticError::NotIntegerDivision);
+    }
+    i / j
+}
+
+#[throws(ArithmeticError)]
+fn caller(i: i32, j: i32, k: i32) -> i32 {
+    i + safe_divide(j, k)
+}
+
+fn caller2(i: i32, j: i32) -> i32 {
+    safe_divide(i, j).unwrap_or_else(|e| match e {
+        ArithmeticError::DivideByZero => 0,
+        ArithmeticError::NotIntegerDivision => i / j,
+        ArithmeticError::IntegerDivideOverflows => i32::MIN,
+    })
+}
+
+

虽然在设计思路上趋于相同,但 Rust 与 C++ 在语言层面的落实程度,却是大相径庭的。Rust 的错误处理机制自设计之初就选择了返回值类型这一正确的方向。在接下来的语言演进当中,新的设计想法几乎总是以库的形式首先出现,在经过了大量真实项目的实验之后进入语言本身。C++ 的 boost 库和 TS 库也部分承担了这样的功能,但 Rust 的强大包管理器无疑大幅度降低了试错成本,而依靠 Rust 强大的宏系统,第三方库具有对语法本身做扩展的能力。诚然,Rust 也会犯错,最初的 std::error::Error 提供的两个接口现在都已经分别在 1.33 和 1.42 被弃用,但这取代这两个的新接口已经被大量实践证明确实更优。比起 C++ 从 C++98 开始几十年的毫无进展,Rust 的错误处理的确在切实的进步。

+

C++ 作为一个有这悠久历史的语言,大量的基础代码库是其坚强生命力的重要来源。但大量的旧有代码也给语言本身戴上了沉重的枷锁。虽然从 C++11 开始,C++ 也成为了「现代」的 C++(Modern C++),但新兴的现代语言如 Rust,从发展的眼光来看,已经在许多方面超过了 C++。新项目在进行技术选型时,也应当首先对这些语言加以考虑。

+

结语

+

为了达成足够的可靠性,系统不可避免地会变复杂。10 软件工程的本质也许就是以人类的智慧去掌控人类无法真正理解的复杂系统。现代语言提供了有力的工具来简化错误处理,但程序员仍然需要学习大量的知识以高效地应用它们。这大概也是软件复杂度的体现吧。

+
1 +

“almost all (92%) of the catastrophic system failures are the result of incorrect handling of non-fatal errors explicitly signaled in software”, Simple Testing Can Prevent Most Critical Failures —— http://www.eecg.toronto.edu/~yuan/papers/failure_analysis_osdi14.pdf

+
+
2 +

“the whole C++ exception handling thing is fundamentally broken.” —— https://lkml.org/lkml/2004/1/20/20

+
+ + +
5 +

这两种方言的鸿沟可能比想象中的更大。例如,没有办法写出泛型代码,同时支持「启用异常」和「禁用异常」。

+
+
6 +

std::error_code 可以算作是错误码的面向对象实现。具体的用法在 Andrzej's C++ Blog 的文章 Your own error code 中有介绍。

+
+
7 +

对 Word 的测试是使用另外的测试框架进行的。

+
+
8 +

Duffy. "Safe native code". Joe Duffy’s Blog, 2015-12-19 —— http://joeduffyblog.com/2015/12/19/safe-native-code/

+
+
9 +

这里的「不关心」,是指不用编程的方式理解具体的错误内容,而是将其以统一的方式提供给人类,如输出日志。

+
+
10 +

C. A. R. Hoare. "The emperor's old clothes." ACM Turing award lectures. 2007. 1980.

+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+从 C++ 的错误处理说起Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/c-template-constraint/index.html b/c-template-constraint/index.html new file mode 100644 index 0000000..f634a17 --- /dev/null +++ b/c-template-constraint/index.html @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + pg999w's blog - C++ 每三年才解决一点点问题 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ C++ 每三年才解决一点点问题 +

+ +
+ +
+

或:怎样优雅地给 C++ 模板添加约束?

+ +

我们有一个基类 Base,一个子类 Derived: public Base,和一个无关的类 Other。我们希望实现一个类模版 M<T>,只能接受该类的派生类作为类型参数,即 M<Derived> 可以编译通过,而 M<Other> 则拒绝编译。应该怎么办?

+
+

以下代码皆依赖头文件 <type_traits>

+
+

实现

+

C++ 98

+

必须从头开始造轮子。此处略。

+

C++ 11

+
template<typename T,
+         typename =
+            typename std::enable_if<std::is_base_of<Base, T>::value>::type>
+struct C {};
+
+

C++ 14

+

C++14 支持 std::enable_if_t 了,这下代码变短了一点。

+
template<typename T,
+         typename = std::enable_if_t<std::is_base_of<Base, T>::value>>
+struct B {};
+
+

C++ 17

+

借助 std::is_base_of_v,我们终于可以在 80 个字符的宽度限制下把 template 声明写在一行里了。

+
template<typename T, typename = std::enable_if_t<std::is_base_of_v<Base, T>>>
+struct C {};
+
+

C++ 20

+

2020年3月24日 Clang 10.0 发布。终于我们有了全功能的 Concept。1 随后 GCC 10.0 也支持了 Concept。

+
template<typename T> requires std::is_base_of_v<Base, T>
+struct D {};
+
+

目前所有 C++ 编译器中只有 GCC 实现了 <concept> 头文件,在 GCC 10.0 中代码可以更加简化:

+
template<std::derived_from<Base> T>
+struct E {};
+
+

错误信息

+

C++ 11

+
+GCC Output +
❯ g++ -c a.cpp -std=c++2a
+a.cpp: 在函数‘int main()’中:
+a.cpp:36:12: 错误:no type named ‘type’ in ‘struct std::enable_if<false, void>’
+   36 |     A<Other> d1;
+      |            ^
+a.cpp:36:12: 错误:模板第 2 个参数无效
+
+
+

嗯?完全让人摸不着头脑的错误信息。用 Clang 试一试:

+
+Clang Output +
❯ clang++ a.cpp -std=c++2a
+a.cpp:11:57: error: failed requirement 'std::is_base_of<Base, Other>::value'; 'enable_if' cannot be used to disable this declaration
+template<typename T, typename = typename std::enable_if<std::is_base_of<Base, T>::value>::type>
+                                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+a.cpp:36:5: note: in instantiation of default argument for 'A<Other>' required here
+    A<Other> d1;
+    ^~~~~~~~
+1 error generated.
+
+
+

哦,这下看出来了,是 std::is_base_of<Base, Other>::value 不满足。

+

C++ 14

+
+GCC Output +
❯ g++ -c a.cpp -std=c++2a
+In file included from a.cpp:1:
+/usr/include/c++/10.2.0/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]’:
+a.cpp:36:12:   required from here
+/usr/include/c++/10.2.0/type_traits:2554:11: 错误:no type named ‘type’ in ‘struct std::enable_if<false, void>’
+ 2554 |     using enable_if_t = typename enable_if<_Cond, _Tp>::type;
+      |           ^~~~~~~~~~~
+a.cpp: 在函数‘int main()’中:
+a.cpp:36:12: 错误:模板第 2 个参数无效
+   36 |     B<Other> d1;
+      |            ^
+
+
+

std::is_base_of 还是被 GCC 吞了。同时 Clang 的错误信息也变糟糕了:

+
+Clang Output +
❯ clang++ a.cpp -std=c++2a
+In file included from a.cpp:1:
+/bin/../lib64/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../include/c++/10.2.0/type_traits:2554:44: error: no type named 'type' in 'std::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
+    using enable_if_t = typename enable_if<_Cond, _Tp>::type;
+                                           ^~~~~
+a.cpp:15:38: note: in instantiation of template type alias 'enable_if_t' requested here
+template<typename T, typename = std::enable_if_t<std::is_base_of<Base, T>::value>>
+                                     ^
+a.cpp:36:5: note: in instantiation of default argument for 'B<Other>' required here
+    B<Other> d1;
+    ^~~~~~~~
+1 error generated.
+
+
+

C++ 17

+

错误信息相较 C++ 14 的写法没有任何改进。换句话说,如果你在用 C++ 14 或 C++ 17 的写法,你现在能取得的最好的结果是,编译器告诉你,有一个 +std::is_base_of_v<Base, T> 无法满足约束。至于这个 T 是什么,自己猜去吧。

+

C++ 20

+

错误信息:

+
+GCC Output +
❯ g++ -c a.cpp -std=c++2a
+a.cpp: 在函数‘int main()’中:
+a.cpp:36:12: 错误:template constraint failure for ‘template<class T>  requires  is_base_of_v<Base, T> struct D’
+   36 |     D<Other> d1;
+      |            ^
+a.cpp:36:12: 附注:constraints not satisfied
+a.cpp:24:8:   required by the constraints of ‘template<class T>  requires  is_base_of_v<Base, T> struct D’
+a.cpp:23:36: 附注:the expression ‘is_base_of_v<Base, T> [with T = Other]’ evaluated to ‘false’
+   23 | template<typename T> requires std::is_base_of_v<Base, T>
+      |                               ~~~~~^~~~~~~~~~~~~~~~~~~~~
+
+
+

这次我们终于知道 T = Other 了。Clang 的错误信息更清楚一些:

+
+Clang Output +
❯ clang++ a.cpp -std=c++2a
+a.cpp:36:5: error: constraints not satisfied for class template 'D' [with T = Other]
+    D<Other> d1;
+    ^~~~~~~~
+a.cpp:23:31: note: because 'std::is_base_of_v<Base, Other>' evaluated to false
+template<typename T> requires std::is_base_of_v<Base, T>
+                              ^
+1 error generated.
+
+
+
1 +

GCC 从 6 开始支持一个叫做 Concept Lite 的 Concept 实验性版本,MSVC 从 VS2019 16.3 开始支持部分 Concept 的功能。

+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+C++ 每三年才解决一点点问题Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/chart.html b/chart.html new file mode 100644 index 0000000..ec71f93 --- /dev/null +++ b/chart.html @@ -0,0 +1,35 @@ + + + + + + + +
+ + + + diff --git a/constexpr-string/index.html b/constexpr-string/index.html new file mode 100644 index 0000000..45f087e --- /dev/null +++ b/constexpr-string/index.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + pg999w's blog - constexpr string + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ constexpr string +

+ +
+ +
+

按照 Andrzej's C++ blog 里这篇文章的思路,我实现了一个编译期的字符串拼接:

+
template<int N>
+class sstring {
+    char inner[N];
+
+    constexpr sstring() = default;
+
+public:
+    constexpr sstring(const char (&s)[N]) : inner{} {
+        for (int i = 0; i < N; ++i) {
+            inner[i] = s[i];
+        }
+    }
+
+    template<int M>
+    constexpr sstring(const sstring<M> &lhs, const sstring<N - M> &rhs) : inner{} {
+        for (int i = 0; i < M; ++i) {
+            inner[i] = lhs[i];
+        }
+        for (int i = 0; i < N - M; ++i) {
+            inner[i + N] = rhs[i];
+        }
+    }
+
+    constexpr char operator[](int i) const {
+        return inner[i];
+    }
+};
+
+template<int N>
+sstring(char (&s)[N]) -> sstring<N>;
+
+template<int M, int N>
+sstring(const sstring<M> &lhs, const sstring<N> &rhs) -> sstring<N + M>;
+
+template<int M, int N>
+constexpr auto operator+(const sstring<M> &lhs, const sstring<N> &rhs) {
+    return sstring(lhs, rhs);
+}
+
+constexpr sstring s {"123"};
+constexpr sstring q {"456"};
+constexpr sstring r {s + q};
+
+
    +
  • constexpr string 有什么用?这至少在初始化全局静态变量时有用。constexpr 静态变量不会存在烦人的初始化顺序问题。
  • +
  • 因为用到了 deducing guide,所以至少需要在 C++ 17 下编译。
  • +
  • std::string 将在 C++ 20 支持 constexpr,不过编译器全部普及这个特性可能还要等好几年。 +
      +
    • C++ 现在有一种「constexpr Everything」的倾向。这是为了更好的实现元编程。这是好事。
    • +
    +
  • +
  • constexpr 构造函数要求初始化每个每个子对象和非静态数据成员必须被初始化。奇怪的是,clang 可以通过未初始化的代码。
  • +
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+constexpr stringPeng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/csarpp-opt/index.html b/csarpp-opt/index.html new file mode 100644 index 0000000..e96ea07 --- /dev/null +++ b/csarpp-opt/index.html @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + pg999w's blog - Rust 的指针别名优化 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ Rust 的指针别名优化 +

+ +
+ +
+

本文研究了基于 Rust 具有的所有权语义的一些优化。

+

程序的机器级表示

+

采用下面这条指令可以让 Rust 编译器生成汇编代码文本:

+
rustc filename.rs --crate-type=lib --emit=asm -C opt-level=2
+
+

其中 --crate-type=lib 是为了以库的方式编译,这样我们就不必定义 main 函数。 +opt-level 设置编译级别。由于 Rust 具有较多的零开销抽象层,至少要开级别 2 的优化,编译器才会将诸如 into_iter 这样的函数调用优化掉,我们才能看到比较清晰的汇编代码。

+

指针别名

+

在 Rust 中,两个可变的引用(&mut T)不能指向同一块内存空间。这使得 Rust 编译器可以告诉 LLVM 后端某个指针不存在别名(noalias)。这可以开启一些 C 语言中不可行的优化。例如下面的代码:

+
pub fn add(a: &mut i64, b: &mut i64) {
+    *a += *b;
+    *a += *b;
+}
+
+

将会产生下面的编译输出:

+
; a in %rdi, b in %rsi
+ZN1a3add17h058f239ac4f807c2E:
+    movq    (%rsi), %rax
+    addq    %rax, %rax
+    addq    %rax, (%rdi)
+    retq
+
+

这相当于

+
pub fn add2(a: &mut i64, b: &mut i64) {
+    *a += 2 * *b;
+}
+
+

注意到如果 ab 能够指向相同的变量 c \(=x\),那么 add +中每一行都使 c 翻倍。add(a, b) 最终使 c 变为 \(4x\), +add2(a, b) 使 c 变为 \(2x\),那么这个优化会改变函数的行为。但是 Rust 可以保证可变引用是独占的,即 a != b,所以 Rust 可以做这种优化。1

+

不必要的内存引用

+

对于 CPU 而言,访问内存显然比访问寄存器更慢。考虑下面的 C 语言循环:

+
typedef struct {
+    long len;
+    double *data;
+} vec_rec, *vec_ptr;
+
+void combine3(vec_ptr v, double *dest) {
+    long length = v->len;
+    double *data = v->data;
+
+    *dest = 0;
+    for (long i = 0; i < length; i++) {
+        *dest += data[i];
+    }
+}
+
+

在循环中,dest 所指向的内存被频繁访问,造成程序低效。然而由于前述指针别名的问题,编译器无法无法保证 dest 不指向 v 中的某个元素,无法对此做优化。在 Rust 中,同样功能的代码

+
pub fn combine1(vec: &Vec<Data>, dest: &mut Data) {
+    *dest = 0;
+    for a in vec {
+        *dest += a;
+    }
+}
+
+

循环部分编译为如下汇编(优化级别 2):

+
; dest in %rsi
+.LBB1_8:
+	addsd	(%rax), %xmm0
+	addsd	8(%rax), %xmm0
+	addsd	16(%rax), %xmm0
+	addsd	24(%rax), %xmm0
+	addsd	32(%rax), %xmm0
+	addsd	40(%rax), %xmm0
+	addsd	48(%rax), %xmm0
+	addsd	56(%rax), %xmm0
+	addq	$64, %rax
+	cmpq	%rcx, %rax
+	jne	.LBB1_8
+.LBB1_9:
+	movsd	%xmm0, (%rsi)   ; write %xmm0 to *dest
+	retq
+
+
+

可以看到所求值被累加进了 %xmm0 寄存器,最后才被写入 %rsi 指向的内存。这样就获得了更佳的性能。2

+

我同时注意到,将 *dest += a 换为 *dest = *dest + a 后,编译器就不能做这个优化了。可见复合赋值语句不仅能让我们少打几个字符,还能帮助编译器优化。

+
+

Footnote

+
1 +

可惜的是,由于 LLVM 的一些 Bug,目前在 rustc 1.33.0-nightly (790f4c566 2018-12-19) 下,这个优化被默认关闭了。需要使用 rustc-Z mutable-noalias +选项开启这个优化。 一旦 LLVM 的 Bug 被修复,这个优化将重新被默认开启,相关的 Tracking issue +在这里

+
+
2 +

由于编译器做了循环展开,所以我们看到了 8 条 addsd 指令。

+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+Rust 的指针别名优化Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/custom.css b/custom.css new file mode 100644 index 0000000..32f9367 --- /dev/null +++ b/custom.css @@ -0,0 +1 @@ +.logo{font-family:"Noto Serif Display",serif;font-weight:400}#mobile-navbar .logo{font-size:1.4rem;font-family:"Source Serif 4",serif;font-optical-sizing:auto;font-weight:400}main .post{font-family:"Noto Serif SC",serif;font-weight:400}@media screen and (max-width: 800px){main .post{font-weight:500}}main .post .post-content h1{font-size:24px}main .post h1,main .post h2,main .post h3,main .post h4,main .post h5,main .post h6,main .post h1.post__title{font-family:"Noto Sans SC",sans-serif;font-weight:320}@media screen and (max-width: 800px){main .post h1,main .post h2,main .post h3,main .post h4,main .post h5,main .post h6,main .post h1.post__title{font-weight:350}}main .post .hashtag{color:gray;font-size:70%;background:none}main .post pre code{color:unset} \ No newline at end of file diff --git a/daocheng-journey/index.html b/daocheng-journey/index.html new file mode 100644 index 0000000..c7c1a72 --- /dev/null +++ b/daocheng-journey/index.html @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + pg999w's blog - 稻城游记 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 稻城游记 +

+ +
+ +
+
+Photo of Yading +
摄于2018年8月10日
+
+

零 游记

+

无聊的时候就会想着写一点东西。比如从亚丁村回稻城的两个小时的旅途中,我完成了这篇文章最初的构思。但是这篇文章经过了不短的写作过程。在回到成都之后,我想起来了,就写一段,写写停停,一直到开学,也没有完成最后让我满意的文章。

+

壹 亚丁村

+

前往稻城亚丁风景区的旅程几乎具有一种朝圣般的痛苦。从成都出发,我们先乘坐了两天的长途汽车,到达稻城县 +,又乘坐一个小时的车到香格里拉镇,再由镇上的游客中心乘一个小时的大巴,才到达亚丁村。全程耗费两天半,几乎全是令人眩晕的高海拔盘山公路。

+

如此与世隔绝,亚丁风景区的确与众不同。风景区的宣传语是“地球上最后一片净土”。虽然不一定是“最后一片”,但这里的确是“净土”。买门票进入后,景区里基本没有商业化的痕迹,除了宗教物品,所有的人造设施都只是为了满足游客通勤的基本需要。里面甚至连卖水的小贩都没有。为了应付从早上持续到下午的徒步游览,游客必须自带干粮。

+

景区里分“长线”和“短线”两条游览线路,短线当然较短,在自然风光外还有寺庙和佛教壁画。而长线则是纯粹的自然风光,在欣赏景色之余,游客的体力与耐力也在这条线路上受着挑战。这条线路处于深山,最后的路段只能步行登山,垂直距离大约六百米,往返水平距离超过十公里,我们就这样在海拔四千米以上的高原山地走了六个小时。一路上山,路况越来越差,地势越来越陡峭,比我们先出发的游客,陆续从山上下来,提示着剩下的路程:“还有两小时!” “还有一小时!” “最后半小时!” 入山后,天又下起了雨,加上山风料峭,我们实在是又饥又冷又累。

+

然而,一路走下来,我却有些失望。我觉得一路上的风景,值不上我的长途跋涉;拍摄的照片,也值不上全程增加的一台D700的配重。如前所述,亚丁风景区的美,是未经污染的秀美,却不是我所期盼的震撼人心的绝美。顶峰的几个海子,的确纯净无暇,可是它实在太小,缺少一种气度。远处的雪山,又在云雾缭绕之中朦朦胧胧,显现不出它的壮丽。山上的各种奇石的确令人眼前一亮,可如此种种,并不会让人有“无限风光在险峰”之感。最后只有安慰自己,这段旅程最大的意义,在于登上峰顶时的那句“We made it”,在于凯撒所讲的“我来,我见,我征服”。

+

贰 稻城县

+

虽然同样是高原,稻城毕竟要平坦得多,而且我从亚丁的山上下来后,各种高原上出现的身体不适奇迹般地消失了,所以稻城的游览显得惬意得多。每人花四十元钱,我们就得以骑电瓶车在县城方圆几公里的范围内自由游览,同时体验在乡间公路上以四十千米每时的速度飚车的感觉。

+

省道二一六上面的景色有一种稻城特有的干净。高原上的植被以灌木为主,基本没有东西遮挡视野,附近的山峦看起来相当光滑。整个视野有一种通透感,同时还显现出了丰富的层次。尽管不是在稻城最美的季节,我们的确见到了成都平原难以见到的景观。

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+稻城游记Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/dvorak-symphony-9/index.html b/dvorak-symphony-9/index.html new file mode 100644 index 0000000..2e57105 --- /dev/null +++ b/dvorak-symphony-9/index.html @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + pg999w's blog - 赏析:德沃夏克,第九交响曲“自新世界”,作品95号 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ 赏析:德沃夏克,第九交响曲“自新世界”,作品95号 +

+ +
+ +
+

第二乐章 Largo “广板”,D♭大调

+ +

我第一次接触到德沃夏克的第九交响曲是在一个动画连续剧里面,剧中人物用小号吹奏了广为人知的主题“念故乡”。第九交响曲作为浪漫主义时期的作品,虽然严守了交响曲四个乐章的体裁和曲式,但由于其主题突出,易于辨认,接受程度在普通大众当中相对较高。我也是由这首曲子逐渐接触到了更多的古典交响乐。下面简要对第二乐章进行评析。第二乐章速度标记为慢板,具有很高的抒情性,其曲式为复三部曲式,分为三个段落:

+
    +
  1. +

    呈示部

    +

    整个乐章在各声部低沉的齐鸣中开始,然后由英国管演秦“念故乡”的主题。第一遍完整的主题过后,转到弦乐声部演泰主题的变奏。最后回到木管声部的主题上来。

    +
  2. +
  3. +

    发展部

    +

    乐章的中段较为复杂,可分为前后两个部分。此时调性由D♭大调转为E大调,由长笛和双簧管的一连串短促的下行高音开始,然后加入了低音提琴的拨弦。低音部从未发出如此清晰的声音——制造了一种暗流涌动的情绪。在一连串较抒缓的长高音之后,进入后半部分,木管乐器发出了欢快的声音。回忆的旋律。随着织体的不断变厚,铜管乐器被加入了进来,然后是定音鼓——然而这升腾的旋律随着第二段的结束而戛然而止了。

    +
  4. +
  5. +

    再现部

    +

    梦醒时分——英国管再次回到呈示部的主题,但很快施律便每降了一个音阶,并转到了小提琴上面来。一种巨大的情绪每被压抑着,很快施律就到了难以继续的程度。终于在 小提琴主题的第二个乐句停了下来。在这里德沃夏克一连用了三个任意延长的休止符。情感终于决堤,大管和铜管乐器加入了合奏。整个第二乐章在弦乐声部的上行音当中结束。

    +
  6. +
+

纵观整个乐章,以弦乐器和木管乐器为主,在乐段衔接处引入了铜管合奏以及定音鼓。第二乐章的主题被冠以“念故乡”的标题之后单独编成曲目,使得其广为传颂。但最令我印象深刻的部分是再现部的那三个休止符。这一段小提琴的演奏给人一种极为强烈的情感色彩,情到至深处终于哽睁咽,这里的停顿给人的情感冲击是巨大的。正所谓“此时无声胜有声”。我想这种古典音乐所展现出的巨大的情感波澜,正是古典音乐的魅力之一吧。

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+赏析:德沃夏克,第九交响曲“自新世界”,作品95号Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/even.js b/even.js new file mode 100644 index 0000000..d26d8f4 --- /dev/null +++ b/even.js @@ -0,0 +1,81 @@ +function initMobile() { + var $mobileNav = document.getElementById("mobile-navbar"); + var $mobileNavIcon = document.querySelector(".mobile-navbar-icon"); + + var slideout = new Slideout({ + "panel": document.getElementById("mobile-panel"), + "menu": document.getElementById("mobile-menu"), + "padding": 180, + "tolerance": 70 + }); + slideout.disableTouch(); + + $mobileNavIcon.addEventListener("click", function() { + slideout.toggle(); + }); + + slideout.on("beforeopen", function () { + $mobileNav.classList.add("fixed-open"); + $mobileNavIcon.classList.add("icon-click"); + $mobileNavIcon.classList.remove("icon-out"); + }); + + slideout.on("beforeclose", function () { + $mobileNav.classList.remove("fixed-open"); + $mobileNavIcon.classList.add("icon-out"); + $mobileNavIcon.classList.remove("icon-click"); + }); + + document.getElementById("mobile-panel").addEventListener("touchend", function() { + slideout.isOpen() && $mobileNavIcon.click(); + }) +} +function initToc() { + var $toclink = document.querySelectorAll('.toc-link') + var $headerlink = document.querySelectorAll('.post-content h1 , .post-content h2') + var $tocLinkLis = document.querySelectorAll('.post-toc-content li') + + var searchActiveTocIndex = function (array, target) { + if (!array.length) { + return -1 + } + + target += 30 + for (let i = 0; i < array.length - 1; i++) { + if (target > array[i].offsetTop && target <= array[i + 1].offsetTop) return i + } + if (target > array[array.length - 1].offsetTop) return array.length - 1 + return -1 + } + + document.addEventListener("scroll", function() { + var scrollTop = document.body.scrollTop | document.documentElement.scrollTop + var activeTocIndex = searchActiveTocIndex($headerlink, scrollTop) + + $toclink.forEach(function (el) { + el.classList.remove('active') + }) + $tocLinkLis.forEach(function (el) { + el.classList.remove('has-active') + }) + + if ($toclink.length && activeTocIndex !== -1) { + $toclink[activeTocIndex].classList.add('active') + let ancestor = $toclink[activeTocIndex].parentNode + while (ancestor.tagName !== 'NAV') { + ancestor.classList.add('has-active') + ancestor = ancestor.parentNode.parentNode + } + } + }) +} + +if (document.readyState === "complete" || + (document.readyState !== "loading" && !document.documentElement.doScroll) +) { + initMobile(); + initToc(); +} else { + document.addEventListener("DOMContentLoaded", initMobile); + document.addEventListener("DOMContentLoaded", initToc); +} diff --git a/first-blog/index.html b/first-blog/index.html new file mode 100644 index 0000000..7ce1a8b --- /dev/null +++ b/first-blog/index.html @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + pg999w's blog - 关于这个博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 关于这个博客 +

+ +
+ +
+

除去第一个 Hello World 页面,这是第一篇文章。

+

我时常觉得应该写一点文章,但是由于时间不够,加上我的拖延症,直到高考完闲得慌的时候才搭建了这个静态博客。希望能写一些有用的文章,也能给互联网提供一些资料。 +这个博客由 Gutenberg 驱动。

+

Gutenberg 已经被重命名为 Zola

+

历史

+

充分说明我的拖延症

+
    +
  1. +

    2018年7月至11月。

    +

    因为懒得弄 Github Pages,该博客首先部署在了 Netlify 上。只在 https://fervent-rosalind-6ed4d2.netlify.com/ 上可见。应该不会被搜索引擎索引到。

    +
  2. +
  3. +

    2018年12月

    +

    因为写了第一篇具有一定质量的文章并打算发布,终于实现了 Travis CI 自动化 Github Pages 部署。留言、RSS 等基础功能也基本到位。

    +
  4. +
  5. +

    2018年12月18日

    +

    这个博客可以在谷歌上搜索到了。

    +
  6. +
  7. +

    2019年12月

    +

    增加了一个 Nanoblog 栏目,用于存放仓促完成,或对别人不那么有价值的文章。

    +
  8. +
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+关于这个博客Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/gh-issue-comments/index.html b/gh-issue-comments/index.html new file mode 100644 index 0000000..f12d9ee --- /dev/null +++ b/gh-issue-comments/index.html @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + pg999w's blog - 使用基于 Github issue 的留言系统 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ 使用基于 Github issue 的留言系统 +

+ +
+ +
+

流行的博客留言系统包括 Disqus 等,但是我并没有 Disqus 帐号,也并不想注册一个。考虑到该博客的受众应该都有 Github 帐号,采用基于 Github issue 的系统应该是合适的,而且还可以享受邮件提醒等功能。我选择了 utteranc.es 的方案。

+ +

在 Zola 下,直接将以下代码保存到 templates/page.html 即可。

+
{% extends "even/templates/page.html" %}
+
+{% block page_before_footer %}
+<script src="https://utteranc.es/client.js"
+        repo="peng1999/blog"
+        issue-term="pathname"
+        theme="github-light"
+        crossorigin="anonymous"
+        async>
+</script>
+{% endblock %}
+
+

给机器人授权后,会在文章第一次被评论后添加相应的 issue,也可以直接在 Github issue 下面评论。

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+使用基于 Github issue 的留言系统Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/google311e257059807e47.html b/google311e257059807e47.html new file mode 100644 index 0000000..eb77cf4 --- /dev/null +++ b/google311e257059807e47.html @@ -0,0 +1 @@ +google-site-verification: google311e257059807e47.html diff --git a/hello/index.html b/hello/index.html new file mode 100644 index 0000000..972d1b9 --- /dev/null +++ b/hello/index.html @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + pg999w's blog - Hello World + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + + + + +
+
+ + + +
+ + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..5fffe75 --- /dev/null +++ b/index.html @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+ +
+ +
+

+ 在 Typst 中使用 Latin Modern 家族 +

+ +
+ +
+

高德纳在开发 TeX 时,也设计了一套字体叫 Computer Modern,作为 TeX 的默认字体。然而当时字体是采用 METAFONT 制作的,和当今的字体标准 OpenType 并不兼容。Latin Modern 通过技术手段将 Computer Modern 转换到了 OpenType 格式,并且做了扩充和微调。所以我们在 Typst 中也可以调用 Latin Modern 字体。

+ +
+ +
+ +
+ +
+

+ 2023 个人年度电影 +

+ +
+ +
+

2022 年由于疫情和学业的各种原因,我观看的电影实在太少,以至于放弃了当年的年度电影评选。2023 年,在完全恢复了正常的生活秩序后,观影频次有所提高。本年我一共看了 40 部电影,其中有 9 部是动画片。

+ +
+ +
+ +
+ +
+

+ Rust 和 C++ 的对象生命管理 +

+ +
+ +
+

Rust 和 C++ 的对象都是值语义,都采用了 RAII 惯用法。所以他们需要处理类似的对象生命周期问题:需要专门的代码来处理对象的初始化,复制和析构。下面进行一个比较,我们能够看到两种语言之间内在的对称性。

+ +
+ +
+ +
+ +
+

+ 为 Typst 添加中文排版支持 +

+ +
+ +
+ +

Typst 是一个 2023 年初开源的一个排版软件。类似于 LaTeX,它通过纯文本编写源代码,然后通过编译器将源代码转换为排版好的 PDF 文件。虽然目前 Typst 的生态还不如 LaTeX,但是比起 LaTeX,它有一些明显的优势:

+ +
+ +
+ +
+ +
+

+ 在中国大陆境内编辑维基百科 +

+ +
+ +
+

由于 GFW 的存在,在中国境内使用维基百科必须要透过代理进行访问。然而为了避免恶意破坏,维基百科禁止匿名代理服务器修改词条。如果你的代理不幸处于封禁的 IP 段,那么将会得到类似于这样的警告:

+ +
+ +
+ +
+ + +
+
+ + + +
+ + + + + + + diff --git a/julia-cuda/index.html b/julia-cuda/index.html new file mode 100644 index 0000000..e80d2e0 --- /dev/null +++ b/julia-cuda/index.html @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + pg999w's blog - 用 Julia 编写 CUDA 程序 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 用 Julia 编写 CUDA 程序 +

+ +
+ +
+

CUDA 本身是一个 C 库,而 CUDA kernel 则需要使用扩展的 C/C++ 语法。但 CUDA.jl 让 Julia CUDA 编程成为可能。然而虽然 CUDA.jl 实现了绝大多数 CUDA 的功能,但其文档仍很不完善。本文补充了一些常见 CUDA 功能在 Julia 中的写法。本文假设读者预先具有 Julia,CUDA,以及 CUDA.jl 的基本知识。

+

Kernel

+

Julia 的基本语法本身与 C 类似,可以类似地编写 CUDA kernel 而无需指定 __device____global__ 关键字。而且其运行时编译的特性也使得函数自动成为泛型。然而需要注意 Julia 在部分数值处理的地方与 C 行为不同。例如,浮点数转换为整数时 Julia 不会自动取整,而是会在发生舍入时报错。假设 a::CuDeviceArray{Int, 1},那么下面的代码

+
a[idx] = c / d
+
+

很可能会报错。正确的写法是 a[idx] = trunc(c / d)a[idx] = c ÷ d

+

令人头疼的是 kernel 中的异常不会以正常的 Julia Exception 的形式抛出,所以没有具体发生错误的行号供参考,所以调试这类问题会比较麻烦。好在 kernel 里的代码只要不带 threadIdx() 这样的函数,就同样可以跑在 CPU 上。所以可以让代码在 CPU 上通过测试之后再去 GPU 上跑。

+

Julia 为 kernel 生成的类型信息,IR,PTX 代码可以通过 CUDA.coda_warntype 等函数找到。

+
+例子 +
julia> CUDA.code_warntype(add!, (CuDeviceVector{Int32, 1},CuDeviceVector{Int32, 1}))
+MethodInstance for add!(::CuDeviceVector{Int32, 1}, ::CuDeviceVector{Int32, 1})
+  from add!(a, b) in Main at /home/pgw/my/cuda_julia_test/main.jl:18
+Arguments
+  #self#::Core.Const(add!)
+  a::CuDeviceVector{Int32, 1}
+  b::CuDeviceVector{Int32, 1}
+Locals
+  i::Int64
+Body::Nothing
+1 ─ %1  = Main.threadIdx()::NamedTuple{(:x, :y, :z), Tuple{Int32, Int32, Int32}}
+│   %2  = Base.getproperty(%1, :x)::Int32
+│   %3  = Main.blockIdx()::NamedTuple{(:x, :y, :z), Tuple{Int32, Int32, Int32}}
+│   %4  = Base.getproperty(%3, :x)::Int32
+│   %5  = (%4 - 1)::Int64
+│   %6  = Main.blockDim()::NamedTuple{(:x, :y, :z), Tuple{Int32, Int32, Int32}}
+│   %7  = Base.getproperty(%6, :x)::Int32
+│   %8  = (%5 * %7)::Int64
+│         (i = %2 + %8)
+│   %10 = (i <= Main.SIZE)::Bool
+└──       goto #3 if not %10
+2 ─ %12 = Base.getindex(a, i)::Int32
+│   %13 = Base.getindex(b, i)::Int32
+│   %14 = (%12 + %13)::Int32
+└──       Base.setindex!(a, %14, i)
+3 ┄       return Main.nothing
+
+
+

获取设备信息

+

有的 CUDA API 没有对应的 Julia 函数封装,于是我们需要手动调用 CUDA.cu 开头的函数绑定。但是需要注意 CUDA.jl 使用的是 CUDA Driver API 而不是通常 CUDA 教程里使用的 Runtime API。例如,要查询设备信息应该使用 cuDeviceGetAttribute,可以像下面这样写函数进行封装:

+
function getMaxThreadsPerBlock()
+    value = Ref{Cint}()
+    CUDA.cuDeviceGetAttribute(value, CUDA.CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK, 0)
+    value[]
+end
+
+function getDeviceOverlap()
+    value = Ref{Cint}()
+    CUDA.cuDeviceGetAttribute(value, CUDA.CU_DEVICE_ATTRIBUTE_GPU_OVERLAP, 0)
+    value[] == 1
+end
+
+

Pinned Memory

+

在 CUDA C 中可以使用 cudaHostAlloc 来代替 malloc 申请 Host 内存,这样的内存复制到 GPU 的速度更快。在 CUDA.jl 中有Mem.HostBuffer 类型可以辅助实现这一功能,但封装的不是很彻底,我们需要自行写一点代码来进行封装。

+
buffertype(::Type{<:Array}) = Mem.HostBuffer
+buffertype(::Type{<:CuArray}) = Mem.DeviceBuffer
+pointertype(T::Type{<:Array}) = Ptr{eltype(T)}
+pointertype(T::Type{<:CuArray}) = CuPtr{eltype(T)}
+
+function allocarray(T::Type, size)
+    B = buffertype(T)
+    E = eltype(T)
+    P = pointertype(T)
+    buf = Mem.alloc(B, size * sizeof(E))
+    arr = unsafe_wrap(T, P(buf.ptr), size)
+    arr, buf
+end
+
+

使用的时候需要手动管理内存,使用 arr, buf = allocarray(Array{Int}, N) 来申请内存并创建 Array 数组,使用结束后使用 Mem.free(buf) 来释放内存。

+
# slower equivalent:
+# arr = Array{Int}(undef, 10)
+# arr_dev = CuArray{Int}(undef, 10)
+arr, buf = allocarray(Array{Int}, 10)
+arr_dev, buf_dev = allocarray(CuArray{Int}, 10)
+
+copyto!(arr_dev, 1, arr, 2, 4) # arr_dev[1:4] = arr[2:5]
+
+Mem.free(buf)
+Mem.free(buf_dev)
+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+用 Julia 编写 CUDA 程序Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/latex-math-formula/index.html b/latex-math-formula/index.html new file mode 100644 index 0000000..a935c35 --- /dev/null +++ b/latex-math-formula/index.html @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + pg999w's blog - \(\LaTeX\) 公式 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ \(\LaTeX\) 公式 +

+ +
+ +
+

\(\LaTeX\) 是一款非常优秀的文档准备系统,它强大的数学排版功能举世闻名。由于 Mathjax 的广泛采用,\(\LaTeX\) 数学公式也成为了 Web 技术上数学公式排版的事实标准。但 \(\LaTeX\) 的学习曲线陡峭,基本的命令难以轻松应对实际写作中遇到的复杂公式。本文选取并实现了 \(\textrm{The \TeX{}book}\) 第 18 章末尾提供的 20 个 Chanllenge。以期为想要深入学习 \(\LaTeX\) 公式排版的读者提供参考。

+ +

Knuth 在 The \(\TeX\)book 的附录中给出了全部习题的答案,但全部使用的是原始的 \(\TeX\) 命令,而本文则采用了适用于 \(\LaTeX\) 的命令。为提供最大兼容性,本文原则上只使用 \(\LaTeX\) 与 AMS 宏集提供的命令排版数学公式。一个例外是 commath 宏包提供的 \dif 命令。但即使不引用这个宏包,也可以轻易地通过定义 \DeclareMathOperator{\dif}{d\!} 来使用这个命令。

+

这篇文章是用 \(\LaTeX\) 写的,目前只有 PDF 版本。你也可以前往 Github 在线阅读

+

文档的源码在这里

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+\(\LaTeX\) 公式Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/latex-math/TeXbookFormula.pdf b/latex-math/TeXbookFormula.pdf new file mode 100644 index 0000000..a1ff1d0 Binary files /dev/null and b/latex-math/TeXbookFormula.pdf differ diff --git a/latex-math/TeXbookFormula.tex b/latex-math/TeXbookFormula.tex new file mode 100644 index 0000000..9f18e42 --- /dev/null +++ b/latex-math/TeXbookFormula.tex @@ -0,0 +1,426 @@ +\documentclass[UTF8,a4paper,scheme=plain,% +punct=quanjiao]{ctexart} + +\usepackage{tikz-cd} + +\usepackage{amsmath} +\usepackage{commath} +\usepackage[backend=biber,style=authoryear,% +bibstyle=trad-plain]{biblatex} +\usepackage{filecontents} +\begin{filecontents}{refs.bib} + @book{TeXbook, + title={The \TeX book}, + author={Donald Ervin Knuth}, + publisher={Addison-Wesley Professional}, + year={1984}, + month={1}, + } + @manual{MathMode, + title={Math Mode}, + author={Herbert Voß}, + year={2014}, + month={1}, + url={http://ctan.math.utah.edu/ctan/tex-archive/obsolete/info/math/voss/mathmode/Mathmode.pdf}, + } + @manual{AmSmath, + title={User's Guide for the \texttt{amsmath} package}, + author={American Mathematical Society and \LaTeX 3 Project}, + date={1999-12-13}, + url={http://mirrors.concertpass.com/tex-archive/macros/latex/required/amsmath/amsldoc.pdf}, + } + @techreport{ISO80000-2, + type={Standard}, + author={{ISO 80000-2:2009(E)}}, + year={2009}, + month={12}, + title={Quantities and units -- Part 2: Mathematical signs and symbols to be used in the natural sciences and technology}, + institution={International Organization for Standardization}, + url={https://www.iso.org/standard/31887.html} + } +\end{filecontents} +\addbibresource[location=local]{refs.bib} + +\usepackage{fancyvrb} +% 显示一个列子,并把对应的代码保存下来 +\newenvironment{exampleshow}[1] +{\makeatletter + \def\filename@es{\jobname-#1.aux} + \typeout{Writing file \filename@es} + \VerbatimOut{\filename@es}} +{\endVerbatimOut + \input{\filename@es} + \makeatother} + +% 显示对应例子的代码 +\newcommand{\examplecode}[1]{\VerbatimInput{\jobname-#1.aux}} + +\usepackage{enumitem} +\setlist[enumerate]{font=\bfseries} + +\usepackage{makeidx} +\makeindex + +\usepackage[skip=2ex plus 1ex]{parskip} + +\usepackage[unicode=true,pdfusetitle, + bookmarks=true,bookmarksnumbered=false,bookmarksopen=false, + breaklinks=false,pdfborder={0 0 0},pdfborderstyle={},backref=false,colorlinks=false] + {hyperref} + +%\setlength{\parskip}{4ex} + +\title{Math formula exercise} +\author{\textsc{Peng Guanwen}} +\newcommand*{\cmd}[1]{\texttt{\char`\\#1}} +\newcommand*{\bracite}[1]{[\cite{#1}]} +\renewcommand*{\indexname}{Unusual math command} + +\begin{document} + +\maketitle + +\begin{abstract} +\LaTeX 是一款非常优秀的文档准备系统,它强大的数学排版功能 +举世闻名。由于 Mathjax\footnote{\url{https://www.mathjax.org/}}的广泛采用,\LaTeX 数学公式也成为了 +Web 技术上数学公式排版的事实标准。但 \LaTeX 的学习曲线陡峭, +基本的命令难以轻松应对实际写作中遇到的复杂公式。本文选取 +并实现了 \citetitle{TeXbook}第18章末尾提供的20个Chanllenge。 +以期为想要深入学习 \LaTeX 公式排版的读者提供参考。 + +\citeauthor{TeXbook}在\citetitle{TeXbook}的附录中给出 +了全部习题的答案,但全部使用的是原始的 \TeX 命令,而本文则采用了 +适用于 \LaTeX 的命令。 +为提供最大兼容性,本文原则上只使用 \LaTeX 与 \AmS 宏集提供 +的命令排版数学公式。一个例外是 \texttt{commath} 宏包提供的 +\cmd{dif}命令。但即使不引用这个宏包,也可以轻易地通过定义 +\verb|\DeclareMathOperator{\dif}{d\!}| +来使用这个命令。 + +\end{abstract} + +\begin{enumerate}[label=Challenge \arabic*] +\item\label{i:textmathrm} + \begin{exampleshow}{nthroot} + \(n^\textrm{th}\) root + \end{exampleshow} + + \examplecode{nthroot} + + \cmd{textrm}命令与 \cmd{mathrm}命令都可以在数学模式 + 显示直的罗马体“th”。在本例中效果也是一样的。但根 + 据\citetitle{MathMode},\cmd{mathrm}是竖直字体的数学模式 + 而 \cmd{textrm}是“真正的”文本模式,在这个公式下应该选择后者。 + + \index{\cmd{textrm}} + +\item + \begin{exampleshow}{sts} + \(\mathbf{S}^{-1}\mathbf{TS}= + \mathbf{dg}(\omega_1,\dots,\omega_n)=\boldsymbol\Lambda\) + \end{exampleshow} + + \examplecode{sts} + + 与 \ref{i:textmathrm} 类似,本题中\(\mathbf{S}\)是粗体数学符号,所以 + 采用 \cmd{mathbf}而不是 \cmd{textbf}。 + + \index{\cmd{mathbf}} + + \LaTeX 下直接使用 \verb|\mathbf\Lambda| 不能得到正常 + 的\(\boldsymbol\Lambda \)粗体效果,我们采用 \AmS 宏集 + 的 \cmd{boldsymbol}命令完成。 + + \index{\cmd{boldsymbol}} + + \LaTeX 数学模式有两种省略号“\(\ldots\)”和“\(\cdots\)”,分别 + 用\cmd{cdots}和 \cmd{ldots}生成。\AmS 宏集提供 + 了 \cmd{dots}、\cmd{dotsi}、\cmd{dotsc}、\cmd{dotsb}、 + \cmd{dotsm}、 + \cmd{dotso}等命令,可以更方便灵活地使用这两种省略号。用法详 + 见 \citetitle[p14]{AmSmath}。 + + \index{\cmd{dots}} + +\item + \begin{exampleshow}{pr} + \(\Pr(m=n\mid m+n=3)\) + \end{exampleshow} + + \examplecode{pr} + + \cmd{mid}与\texttt{|}、\cmd{lvert}、\cmd{rvert}都是显示为\(|\)的同一 + 个字符。不同的是它们的语义不同,如 \cmd{mid}是一个关系符, + 而 \cmd{lvert}是一个左分隔符。这些语义能帮助\LaTeX 产生正确的空白。 + + \index{\cmd{vert}!vert@$\vert$} + \index{\cmd{vert}!\cmd{lvert}} + \index{\cmd{vert}!\cmd{rvert}} + \index{\cmd{mid}} + + \citetitle{TeXbook}认为这个式子可以与集合记号类比,在括号两侧添加窄空 + 格。但我认为\(\Pr\)还是应该被认为是一个函数,所以使用默认的空白方案。 + +\item + \begin{exampleshow}{sin18} + \(\sin18^\circ=\frac14 (\sqrt5-1)\) + \end{exampleshow} + + \examplecode{sin18} + + \cmd{frac}的参数如果只有一个字符,可以直接省略大括号,以增加可读性。 + + \index{\cmd{circ}} + +\item + \begin{exampleshow}{k} + \(k=1.38\times10^{-16}\,\textrm{erg}/^\circ\textrm K\) + \end{exampleshow} + + \examplecode{k} + + 单位\(\textrm{erg}/^\circ\textrm{K}\)与数字之间应该有一个窄空 + 格\cmd{,}。 + + \index{\cmd{,}} + +\item + \begin{exampleshow}{phiinnl} + \(\bar\Phi\subset NL_1^*/N= + \bar L_1^*\subseteq\dots\subseteq NL_n^*/N=\bar L_n^*\) + \end{exampleshow} + + \examplecode{phiinnl} + +\item + \begin{exampleshow}{ilambda} + \(I(\lambda)=\iint_Dg(x,y)e^{i\lambda h(x,y)}\dif x\dif y\) + \end{exampleshow} + + \examplecode{ilambda} + + 在 \citetitle{TeXbook}中微分符号都是写作斜体的\(dx\),但根 + 据\citeauthor{ISO80000-2},应该采用竖直的罗马体。所以使 + 用 \texttt{commath}宏包的 \cmd{dif}命令以符合标准的要求。 + + \index{\cmd{dif}} + +\item + \begin{exampleshow}{intdotsint} + \(\int_0^1\dotsi\int_0^1f(x_1,\dots,x_n)\dif x_1\dots\dif x_n\) + \end{exampleshow} + + \examplecode{intdotsint} + + \citetitle{TeXbook}认为应该在第一个积分符号后面插入一个负空 + 格 \cmd{!}。 但我认为没有合适的排版上的理由这要做。 + +\item + \begin{exampleshow}{x2m} + \[x_{2m}\equiv\begin{cases} + Q(X_m^2-P_2W_m^2)-2S^2&(m\textrm{ odd})\\ + P_2^2(X_m^2-P_2W_m^2)-2S^2&(m\textrm{ even}) + \end{cases}\pmod N + \] + \end{exampleshow} + + \examplecode{x2m} + + 两行公式略显拥挤。如果采用 \texttt{mathtools}宏包的 + \cmd{dcases}将会取得更好的结果。 + + \index{\cmd{pmod}} + +\item + \begin{exampleshow}{1x1z} + \[(1+x_1z+x_1^2z^2+\dotsb)\dots(1+x_nz+x_n^2z^2+\dotsb)= + \frac1{(1-x_1z)\dots(1-x_nz)} + \] + \end{exampleshow} + + \examplecode{1x1z} + + \cmd{dots}自动判断在这个例子中不起作用,所以需要语义化的版本 + \cmd{dotsb}。 + + \index{\cmd{dots}!\cmd{dotsb}} + + \citetitle{TeXbook}在两处“\(+\dotsb\)”后面都增加了窄空格。 + +\item + \begin{exampleshow}{pi} + \[\prod_{j\ge0}\biggl(\sum_{k\ge0}a_{jk}z^k\biggr)= + \sum_{n\ge0}z^n\Biggl(\sum_{\substack{ + k_0,k_1,\dotsc\ge0\\ + k_0+k_1+\dots=n}} a_{0k_0}a_{1k_1}\dots\Biggr) + \] + \end{exampleshow} + + \examplecode{pi} + + 如果采用 \cmd{left}和 \cmd{right}自动调整括号高度,会设置 + 为括号内部整个公式的高度,效果不令人满意。于是使用 \cmd{Biggl} + 和 \cmd{Biggr}手动调整大小。 + + \index{\cmd{Bigg}!\cmd{Biggl}} + \index{\cmd{Bigg}!\cmd{Biggr}} + + \citetitle{TeXbook}在\(z^n\)后面增加了窄空格。 +\item + \begin{exampleshow}{fracn1} + \[\frac{(n_1+n_2+\dots+n_m)!}{n_1!\,n_2!\dots n_m!}= + \binom{n_1+n_2}{n_2}\binom{n_1+n_2+n_3}{n_3}\dots + \binom{n_1+n_2+\dots+n_m}{n_m} + \] + \end{exampleshow} + + \examplecode{fracn1} + + \LaTeX 不能很好的计算后缀运算符周围的空白,所以我们需要在 + \(n_2!\)前面插入一个窄空格。 + +\item + \begin{exampleshow}{pir} + \[\Pi_R\genfrac[]{0pt}{} + {a_1,a_2,\dots,a_M} + {b_1,b_2,\dots,b_N} + =\prod_{n=0}^R\frac{ + (1-q^{a_1+n})(1-q^{a_2+n})\dots(1-q^{a_M+n})}{ + (1-q^{b_1+n})(1-q^{b_2+n})\dots(1-q^{b_N+n})} + \] + \end{exampleshow} + + \examplecode{pir} + + 使用 \cmd{genfrac} 可以生成向分数一样上下排列的两个公式, + 将第三个参数设置为零就可以取消掉中间的横线。 + + \index{\cmd{genfrac}} + +\item + \begin{exampleshow}{sigmap} + \[\sum_{p\textrm{ prime}}f(p)=\int_{t>1}f(t)\dif\pi(t)\] + \end{exampleshow} + + \examplecode{sigmap} + +\item + \begin{exampleshow}{underbrace} + \[\{\underbrace{\overbrace{\mathstrut a,\dots,a}^{k\;a\textrm{'s}}, + \overbrace{\mathstrut b,\dots,b}^{l\;b\textrm{'s}} + }_{k+l\textrm{ elements}}\} + \] + \end{exampleshow} + + \examplecode{underbrace} + + \cmd{mathstrut}等价于 \verb|\vphanthom(|,这相当于插入 + 了一个宽度为0,但高度与一个括号相等的盒子,使得两边的大括号 + 一样高。 + + \index{\cmd{mathstrut}} + +\item + \begin{exampleshow}{matrix} + \[\begin{pmatrix} + \begin{pmatrix}a&b\\c&d\end{pmatrix} & + \begin{pmatrix}e&f\\g&h\end{pmatrix} \\ + \noalign{\smallskip} 0 & + \begin{pmatrix}i&j\\k&l\end{pmatrix} + \end{pmatrix} + \] + \end{exampleshow} + + \examplecode{matrix} + + \verb|\noalign{\smallskip}|用于增加两行之间的间距。 + + \index{\cmd{noalign}\{\cmd{smallskip}\}} + +\item + \begin{exampleshow}{det} + \[\det\left| + \begin{array}{*{5}{l}} + c_0&c_1&c_2&\dots&c_n\\ + c_1&c_2&c_3&\dots&c_{n+1}\\ + c_2&c_1&c_4&\dots&c_{n+2}\\ + \,\vdots&\,\vdots&\,\vdots& &\,\vdots\\ + c_n&c_{n+1}&c_{n+2}&\dots&c_{2n} + \end{array}\right|>0\] + \end{exampleshow} + + \examplecode{det} + + 为了实现对齐,这里采用了 \texttt{array}环境。 + \texttt{mathtools}宏包提供了 \texttt{pmatrix*}环境, + 可以更方便地实现矩阵对齐。 + + \index{\cmd{left}} + \index{\cmd{right}} + +\item + \begin{exampleshow}{sumprime} + \[\mathop{{\sum}'}_{x\in A}f(x)\stackrel{\textrm{def}}= + \sum_{\substack{x\in A\\x\neq0}}f(x)\] + \end{exampleshow} + + \examplecode{sumprime} + + 由于在数学模式中 \texttt{'}等价于 \verb|^\prime|, + 而巨算符会改变上标 + 的位置,所以直接采用 \verb|\sum'_{x\in A}|是不可行的。 + 我们需要用 \cmd{mathop}临时制作一个新的巨算符。 + + \index{\cmd{mathop}} + +\item + \begin{exampleshow}{genfrac} + \newcommand*{\bottomalign}[1]% + {\genfrac{}{}{0pt}{}{#1}{}} + \[2\uparrow\uparrow k\stackrel{\textrm{def}}= + 2^{2^{2^{\cdot^{\cdot^{\cdot^2}}}}} + \bottomalign{\Bigr\}\scriptstyle k} + \] + \end{exampleshow} + + \examplecode{genfrac} + + \citetitle{TeXbook}采用了 \cmd{vbox}和 \cmd{hbox} + 的组合实现了大括号沿下侧对齐。而这里使用了 \cmd{genfrac} + 命令,可以达到相同的效果,同时兼容了 Mathjax。 + +\item + \begin{exampleshow}{tikzcd} + \[\begin{tikzcd} + & & & 0 \arrow[d] + & \\ + 0 \arrow[r] & \mathcal O_C \arrow[r,"\imath"] + \arrow[d,equal] & \mathcal E \arrow[r,"\rho"] + \arrow[d,"\phi"] & \mathcal L \arrow[r] \arrow[d,"\psi"] + & 0 \\ + 0 \arrow[r] & \mathcal O_C \arrow[r] & \pi_*\mathcal O_D + \arrow[r,"\delta"] & R^1F_*\mathcal O_V(-D) \arrow[r] + \arrow[d] + & 0 \\ + & & & R^1F_*(\mathcal O_V(-iM))\otimes\gamma^{-1} \arrow[d] & \\ + & & & 0 & + \end{tikzcd} + \] + \end{exampleshow} + + \examplecode{tikzcd} + + 这个交换图在 \citetitle{TeXbook}是用矩阵实现的。但利用宏包 + \texttt{tikz-cd},可以更方便,灵活地绘制交换图。 + +\end{enumerate} + +\printindex + +\printbibliography +\end{document} + +%%% Local Variables: +%%% mode: latex +%%% TeX-engine: xetex +%%% End: \ No newline at end of file diff --git a/latex-math/tikzcd.tex b/latex-math/tikzcd.tex new file mode 100644 index 0000000..7329428 --- /dev/null +++ b/latex-math/tikzcd.tex @@ -0,0 +1,16 @@ +\begin{tikzcd} +& & & 0 \arrow[d] + & \\ +0 \arrow[r] +& \mathcal O_C \arrow[r,"\imath"] \arrow[d,equal] + & \mathcal E \arrow[r,"\rho"] \arrow[d,"\phi"] + & \mathcal L \arrow[r] \arrow[d,"\psi"] + & 0 \\ +0 \arrow[r] +& \mathcal O_C \arrow[r] + & \pi_*\mathcal O_D \arrow[r,"\delta"] + & R^1F_*\mathcal O_V(-D) \arrow[r] \arrow[d] + & 0 \\ +& & & R^1F_*(\mathcal O_V(-iM))\otimes\gamma^{-1} \arrow[d] & \\ +& & & 0 & +\end{tikzcd} \ No newline at end of file diff --git a/movie-2019/index.html b/movie-2019/index.html new file mode 100644 index 0000000..3c046d7 --- /dev/null +++ b/movie-2019/index.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + pg999w's blog - 2019 个人年度电影 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 2019 个人年度电影 +

+ +
+ +
+

规则

+

虽然是一个自娱自乐的东西,但是不以规矩,不成方圆,入选的电影满足如下条件:

+
    +
  1. 是本年新观看的电影。
  2. +
  3. 包含观看的电影资源,不限于院线片。
  4. +
  5. 仅以个人喜好程度排序,不单独考虑艺术价值,社会影响等因素。
  6. +
+

2019 年度电影

+

遗憾的是,我选出的三部电影全部是非院线片,这意味着这些电影基本都是在较差的观影条件下观看的。

+
    +
  • +

    rank 1 《寄生虫(기생충)》2019 奉俊昊

    +

    最为传统的剧作结构,达到了最好的效果。这部电影给了我长久以来都没有过的,被情节抓住的体验。

    +
  • +
  • +

    rank 2 《宣告黎明的露之歌(夜明け告げるルーのうた)》2017 汤浅政明

    +

    没有什么特殊的理由,这部电影和我产生了某种共振。汤浅政明创造的这个异国故事,包含着一种奇妙的气氛,确实触动了我心中某个部分。

    +
  • +
  • +

    rank 3 《登月第一人(First Man)》2018 达米恩·查泽雷

    +

    许多电影里都有太空飞船,但是这一部真实地展现了那种天空深处的危机四伏。舱内狭隘的视角,剧烈的抖动,死亡与濒死体验,都无比真实。

    +
  • +
+

2019 年度原声音乐

+
    +
  • +

    《冰雪奇缘2(Frozen II)》2019 艺术家:Robert Lopez/Kristen Anderson Lopez

    +

    电影没得洗,确实是迪士尼缺乏诚意的制作,但是原声音乐仍然是高水准。比起第一部,这部更加戏剧化的音乐我反而更喜欢。

    +
  • +
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+2019 个人年度电影Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/movie-2020/index.html b/movie-2020/index.html new file mode 100644 index 0000000..18142f7 --- /dev/null +++ b/movie-2020/index.html @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + pg999w's blog - 2020 个人年度电影 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 2020 个人年度电影 +

+ +
+ +
+

规则

+

今年是第二年自娱自乐的评选了。同样基本按照去年的规则来,但是稍有修订。入选的电影满足如下条件:

+
    +
  1. 是本年新观看的电影。
  2. +
  3. 包含观看的资源片,不限于院线片。
  4. +
  5. 仅以个人喜好程度排序。
  6. +
+

另设「年度原声音乐」一个名额,范围是今年听到的电影原声带专辑。

+

2020 年度电影

+

今年首次出现了院线片。第三名本来是波兰斯基的《我控诉(J'accuse)》,可是2020年的最后一天在影院看了《心灵奇旅》,大概是受了氛围的影响,我临时改变了主意。

+

本次入围前三名的有两部动画电影。但是令人担忧的是,高产优质作品的京都动画受火灾的影响,前途未卜;而皮克斯在当下流量为王的流媒体时代,已经开始受到资本的裹挟。在动画电影领域,2020年会成为接下来十年中最好的一年吗?我无法乐观地给出否定的回答。

+
    +
  • +

    rank 1 《利兹与青鸟(リズと青い鳥)》2018 山田尚子

    +

    这是我近年来看过的电影感和作者风格最强的长篇动画电影。影片的整个核心故事都在学校教学楼这个封闭空间内完成。然而在这个受限的空间中,从与脚步声配合的配乐,到精心安排的背景摆件,到处充满着细节。山田尚子在九十分钟的时间内,尽情地使用视听语言,探索到了人物的内心最深之处。

    +
  • +
  • +

    rank 2 《爆裂鼓手(Whiplash)》2014 达米恩·查泽雷

    +

    我听说真正的爵士乐迷并不看好这个扭曲了爵士乐精神的片子。但是对于我这样不通爵士乐的人来说,那种癫狂的戏剧张力,反高潮的情节转折,使这部电影充满了力气。

    +
  • +
  • +

    rank 3 《心灵奇旅(Soul)》2020 彼特·道格特

    +

    也许皮克斯已经不是曾经的那个皮克斯了,但是非凡的想象力与一流的动画工业的结合,至少现在还没有丢失。从生之彼岸的桥上面下坠的画面,久违地给人以极大的愉悦。

    +
  • +
+

2020 年度原声音乐

+
    +
  • +

    《「girls,dance,staircase」(电影『利兹与青鸟』原声带)》2019 艺术家:牛尾憲輔/松田彬人

    +

    牛尾憲輔自然是延续一直以来的配乐风格,而这次松田彬人为配合电影专门按古典交响曲格式创作的《利兹与青鸟》 +完整的四乐章,实在难得。

    +
  • +
+

往年年度电影

+ + +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+2020 个人年度电影Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/movie-2021/index.html b/movie-2021/index.html new file mode 100644 index 0000000..ec1a8f2 --- /dev/null +++ b/movie-2021/index.html @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + pg999w's blog - 2021 个人年度电影 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 2021 个人年度电影 +

+ +
+ +
+

今年的年度电影规则和去年一样。虽然名单在元旦时就确定了,可是由于各种原因,这篇博客文章拖到了春节最后一天才写出来。值得一提的是所有入选电影全部是 2021 年新片。一方面当然是我接触新片更频繁了,另一方面也从侧面看出业界确实在复苏。

+

2021 年度电影

+
    +
  • +

    rank 1 《沙丘(Dune)》2021 丹尼斯·维伦纽瓦

    +

    如果说2021年还有什么电影值得人们非进电影院不可,那首先应当是《沙丘》。如果2021年还有什么在电影体现了一流的工业水准,那一定有《沙丘》。如果2021年有什么值得一提的科幻片,那只能是《沙丘》。

    +
  • +
  • +

    rank 2 《花束般的恋爱(花束みたいな恋をした)》2021 土井裕泰

    +

    我们和剧中的主人公的境况当然千差万别,但是步入社会的大学生,为了现实的压力而终于放弃了曾经热爱的东西——这种恐惧确实传达给我了。

    +
  • +
  • +

    rank 3 《酷爱电影的庞波小姐(映画大好きポンポさん)》2021 平尾隆之

    +

    我关注的影评人给这部电影打了不少低分。可是在我这里,尽管剧情确实有值得严肃的影评人诟病之处,但形式上的趣味压倒了一切。这部动画里一切外在的形式,包括突出的剪辑点,反复的插叙,以及精确控制的时间,无不体现了导演「剪辑使电影变得有趣」的理念。

    +
  • +
+

2021 年度原声音乐

+
    +
  • +

    《倒数时刻(Tick, Tick… boom!)》2021 艺术家:Janason Larson

    +

    选择音乐剧改编电影似乎有点作弊。不过就原作中的几首经典歌曲来说,比起原版的音乐剧,我更喜欢这张原声带的版本。

    +
  • +
+

往年年度电影

+ + +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+2021 个人年度电影Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/movie-2023/index.html b/movie-2023/index.html new file mode 100644 index 0000000..810c61a --- /dev/null +++ b/movie-2023/index.html @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + pg999w's blog - 2023 个人年度电影 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 2023 个人年度电影 +

+ +
+ +
+

2022 年由于疫情和学业的各种原因,我观看的电影实在太少,以至于放弃了当年的年度电影评选。2023 年,在完全恢复了正常的生活秩序后,观影频次有所提高。本年我一共看了 40 部电影,其中有 9 部是动画片。

+ +

对于许多同学来说,去校门附近的影院观影是一件习以为常的事情,但我却是今年才第一次获得了这样的环境。今年看的电影中,至少有 25 部是在影院看的。比较遗憾的是,《永安镇故事集》和《白塔之光》两部电影虽然在院线上映了,但是种种原因导致我第一个周末没有去看。后面再想看几乎已无排片。

+

2023 年度电影

+
    +
  • 年度最佳电影:坠落的审判(Anatomie d'une chute)》2023 茹斯汀·特里耶
    +个人评价的年度最佳。作为金棕榈大奖的艺术片,作者性和艺术性自不必说。同时这部电影在形式上并不乏味,法庭戏更是完全符合我的偏好。
  • +
  • 年度动画电影:吹响悠风号 合奏比赛篇(特別編 響け!ユーフォニアム アンサンブルコンテスト)》2023 石原立也
    +选这部完全是我个人的私心。虽然本片不到一小时、刚刚满足长篇动画的定义、而且完全可以看作是一个明年剧集的大型预告片,但本片作为纵火案之后第一部重启的《吹》系列作品,可以说是意义非凡。
  • +
  • 年度影展电影:登报风云(The Newspaper)》2020 萨拉特·科塔拉瓦拉1 / 库马拉·提利马杜拉
    +今年上影节看了 3 部电影,上海艺联的日常影展也看了一些。我觉得影展的意义一方面提供了仪式感,另一方面能够拓宽阅片的视野,能看到一些平时不太会看的电影。例如这部《登报风云》就是我上影节在和平影都看的一部斯里兰卡电影。影片也非常精彩。
  • +
+

往年年度电影

+ +
1 +

感谢 GPT-4 提供的译名。

+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+2023 个人年度电影Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/nanoblog/constexpr-string/index.html b/nanoblog/constexpr-string/index.html new file mode 100644 index 0000000..eccd061 --- /dev/null +++ b/nanoblog/constexpr-string/index.html @@ -0,0 +1,9 @@ + + + + + + +

Redirecting to /constexpr-string/ ...

+ + diff --git a/nanoblog/dvorak-symphony-9/index.html b/nanoblog/dvorak-symphony-9/index.html new file mode 100644 index 0000000..703706b --- /dev/null +++ b/nanoblog/dvorak-symphony-9/index.html @@ -0,0 +1,9 @@ + + + + + + +

Redirecting to /dvorak-symphony-9/ ...

+ + diff --git a/nanoblog/index.html b/nanoblog/index.html new file mode 100644 index 0000000..eedeeca --- /dev/null +++ b/nanoblog/index.html @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+ + + + + + + + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/nanoblog/movie-2019/index.html b/nanoblog/movie-2019/index.html new file mode 100644 index 0000000..192d0d0 --- /dev/null +++ b/nanoblog/movie-2019/index.html @@ -0,0 +1,9 @@ + + + + + + +

Redirecting to /movie-2019/ ...

+ + diff --git a/nanoblog/movie-2020/index.html b/nanoblog/movie-2020/index.html new file mode 100644 index 0000000..9bd8dcd --- /dev/null +++ b/nanoblog/movie-2020/index.html @@ -0,0 +1,9 @@ + + + + + + +

Redirecting to /movie-2020/ ...

+ + diff --git a/nanoblog/page/1/index.html b/nanoblog/page/1/index.html new file mode 100644 index 0000000..0383502 --- /dev/null +++ b/nanoblog/page/1/index.html @@ -0,0 +1,6 @@ + + + + +Redirect +

Click here to be redirected.

diff --git a/nanoblog/page/2/index.html b/nanoblog/page/2/index.html new file mode 100644 index 0000000..14e09ed --- /dev/null +++ b/nanoblog/page/2/index.html @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/nanoblog/perl-hex/index.html b/nanoblog/perl-hex/index.html new file mode 100644 index 0000000..b2fb464 --- /dev/null +++ b/nanoblog/perl-hex/index.html @@ -0,0 +1,9 @@ + + + + + + +

Redirecting to /perl-hex/ ...

+ + diff --git a/nanoblog/ros-docker/index.html b/nanoblog/ros-docker/index.html new file mode 100644 index 0000000..bc5d8d3 --- /dev/null +++ b/nanoblog/ros-docker/index.html @@ -0,0 +1,9 @@ + + + + + + +

Redirecting to /ros-docker/ ...

+ + diff --git a/nanoblog/tikz.svg b/nanoblog/tikz.svg new file mode 100644 index 0000000..51f3301 --- /dev/null +++ b/nanoblog/tikz.svg @@ -0,0 +1,63 @@ + +image/svg+xml \ No newline at end of file diff --git a/nanoblog/tikz/index.html b/nanoblog/tikz/index.html new file mode 100644 index 0000000..0d82e57 --- /dev/null +++ b/nanoblog/tikz/index.html @@ -0,0 +1,9 @@ + + + + + + +

Redirecting to /tikz/ ...

+ + diff --git a/nix-smartcross/index.html b/nix-smartcross/index.html new file mode 100644 index 0000000..2f68998 --- /dev/null +++ b/nix-smartcross/index.html @@ -0,0 +1,468 @@ + + + + + + + + + + + + + + + pg999w's blog - 用 Nix 管理交叉编译 Rust 项目的环境 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 用 Nix 管理交叉编译 Rust 项目的环境 +

+ +
+ +
+

SmartCross 项目的介绍见这里。其中的控制器组件用 Rust 写成,需要编译到 aarch64 平台。我尝试写了一个 Nix 表达式来管理该项目的环境。

+ +

Nix 是很多东西的总称,包括

+
    +
  • 一个函数式编程语言 Nix 表达式
  • +
  • 一个用 Nix Expression 进行打包的包管理系统 Nix,同时支持各种 Linux 和 MacOS,并允许多个版本的相同软件在系统中并存
  • +
  • 一个世界上最大的软件包仓库 Nixpkgs
  • +
  • 一个操作系统 NixOS,使用 Nix 表达式来定义整个系统的软件和配置,并实现了原子更新和方便地系统回滚
  • +
+

同时 Nixpkgs 提供了一流的交叉编译支持。下面将编写描述构建环境的 Nix 表达式。

+

CMake 项目的打包

+

首先该项目依赖的 libubootenv 没有在 nixpkgs 中打包,所以我们需要手动打包。只需要写一个 libubootenv.nix 文件,放在 nix/ 目录下即可。Nixpkgs 的“genericBuild”机制可以处理 CMake 项目,基本只需要按模版简单填空即可。

+
{ lib, stdenv, fetchFromGitHub, cmake, zlib }:
+
+stdenv.mkDerivation rec {
+  pname = "libubootenv";
+  version = "0.3.3";
+
+  src = fetchFromGitHub {
+    owner = "sbabic";
+    repo = "libubootenv";
+    rev = "v${version}";
+    sha256 = "sha256-BQZp+/UbaEkXFioYPAoEA74kVN2sXfBY1+0vitKdfho=";
+  };
+
+  nativeBuildInputs = [ cmake ];
+
+  buildInputs = [ zlib ];
+
+  # 这个选项是为了修复 pkg-config 给出路径错误的问题
+  cmakeFlags = [
+    "-DCMAKE_INSTALL_INCLUDEDIR=include"
+  ];
+
+  meta = with lib; {
+    description = "Generic library and tools to access and modify U-Boot environment from User Space";
+    homepage    = "https://github.com/sbabic/libubootenv";
+    license = licenses.mit;
+  };
+}
+
+

构建依赖

+

用 Nix 表达式写好一个包的定义之后,Nixpkgs 可以自动处理到不同平台的交叉编译。注意到如果包 A 依赖了包 BbuildPlatform 是编译时的平台,hostPlatform 是编译产物实际运行的平台,那么一般有以下两种情况

+
    +
  1. hostPlatform B == hostPlatform A
  2. +
  3. hostPlatform B == buildPlatform A
  4. +
+

如果符合情况 1,例如 BA 的运行时依赖,则需要将 B 放到 A.buildInputs 中去,如果符合情况 2,例如 B 是构建工具,则需要放到 A.nativeBuildInputs 中去。

+

Rust 项目打包

+

使用 buildRustPackage

+

Rust 项目可以用 nixpkgs.rustPlatform.buildRustPackage 打包。同样是简单按模版填空即可。下面是 nix/smartcross_controller.nix 文件。

+
{ rustToolchain,
+  makeRustPlatform, pkgconfig, protobuf, libubootenv, avahi, openssl, dbus, alsa-lib, zlib
+}:
+
+let rustPlatform = makeRustPlatform {
+  rustc = rustToolchain;
+  cargo = rustToolchain;
+};
+
+in rustPlatform.buildRustPackage rec {
+  pname = "smartcross_controller";
+  version = "0.1.0";
+
+  src = ../.;
+  cargoLock = {
+    lockFile = ../Cargo.lock;
+    outputHashes = {
+      "camilladsp-1.0.1" = "sha256-XpQE+XVgQyVRg/NHCkPnpB/SGLChsUZucvL8x/ieKzI=";
+      "libubootenv-rs-0.1.0" = "sha256-FRPnFjrlVS09W7MTjY0X8kwU04HZMcYxQAiVvEvKc08=";
+      "rfkill-rs-0.1.0" = "sha256-uN58uzTeaQWLAEizNFZSldq2wkmlo8Si5xbQDyfYmYI=";
+    };
+  };
+
+  nativeBuildInputs = [
+    pkgconfig
+    protobuf
+    rustPlatform.bindgenHook
+  ];
+
+  buildInputs = [
+    libubootenv
+    avahi
+    openssl
+    dbus.dev
+    alsa-lib.dev
+    zlib
+  ];
+}
+
+

基于可复现性的考虑,Rust 项目的 git 依赖需要全部填写哈希值。一些第三方库使用更高级的方法解决了这个问题。下面使用 Crane 来进行同样的打包。

+

使用 Crane 为 Rust 项目打包

+

Crane 比起 buildRustPackage 的另一个优点是提供了更细粒度的缓存。使用 buildRustPackage,每次更改项目代码后,重新编译都是一个 clean build,而使用 Crane,只要不更改依赖项,则不用重新编译 cargo 依赖。

+

遗憾的是,Crane 对交叉编译的支持没有 buildRustPackage 那么好。为了成功编译,我们需要给构建环境手动添加两个环境变量。其中一个环境变量名字中带有架构名称,使得一旦目标架构变化,我们就需要手动更改这个表达式。所幸我们不会频繁更换构建目标平台。

+
{ src, craneLib, target,
+  stdenv, pkgconfig, protobuf, rustPlatform, libubootenv, avahi, openssl, dbus, alsa-lib, zlib
+}:
+
+craneLib.buildPackage {
+  inherit src;
+
+  nativeBuildInputs = [
+    # ... 和前面相同
+  ];
+
+  buildInputs = [
+    # ... 和前面相同
+  ];
+
+  # 任何不含有特殊意义的字段都将会直接变成构建环境中的环境变量
+  CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = "${stdenv.cc.targetPrefix}cc";
+  CARGO_BUILD_TARGET = target;
+}
+
+

这里我们没有指定 src,而是将其作为参数,稍后我们会具体给 src 赋值。

+

编写 Flake 表达式

+

接下来我们在项目根目录编写 flake.nix。这里面包含了关于构建环境所有的配置信息。

+
{
+  # inputs 声明所有用到的库
+  inputs = {
+    rust-overlay = {
+      url = "github:oxalica/rust-overlay";
+      inputs = {
+        nixpkgs.follows = "nixpkgs";
+        flake-utils.follows = "flake-utils";
+      };
+    };
+    crane = {
+      url = "github:ipetkov/crane";
+      inputs = {
+        nixpkgs.follows = "nixpkgs";
+        flake-utils.follows = "flake-utils";
+      };
+    };
+    flake-utils.url = "github:numtide/flake-utils";
+    nixpkgs.url = "nixpkgs/nixos-unstable";
+  };
+
+  outputs = { self, rust-overlay, crane, flake-utils, nixpkgs }:
+    flake-utils.lib.eachDefaultSystem (system:
+      let # 下面将声明一系列变量(值)
+        target = "aarch64-unknown-linux-gnu"; # 目标平台
+        # pkgs 将成为 nixpkgs 特定目标平台的实例
+        pkgs = import nixpkgs {
+          inherit system;
+          overlays = [ (import rust-overlay) ]; # 为了自由选择 Rust 版本和组件,我们使用了 rust-overlay
+          crossSystem = {
+            config = target;
+          };
+        };
+        # 选择最新的 stable Rust,带有 minimal 配置的组件
+        rustToolchain = pkgs.pkgsBuildHost.rust-bin.stable.latest.minimal.override {
+          targets = [ target ]; # 同时能够交叉编译到目标平台
+        };
+        # 将我们选择的 Rust 工具链应用到 Crane
+        craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain;
+        # 辅助函数
+        protoFilter = path: _type: builtins.match ".*proto$" path != null;
+        # 一个函数,用来判断 path 是否是需要带入构建环境的文件
+        protoOrCargo = path: type:
+          (protoFilter path type) || (craneLib.filterCargoSources path type);
+      in {
+        # 这个 flake 可以生成的包
+        packages = rec {
+          default = smartcross_controller;
+
+          # 构建 libubootenv
+          libubootenv = pkgs.callPackage ./nix/libubootenv.nix {}; # 没有参数需要传递
+
+          # 构建 smartcross_controller
+          smartcross_controller = pkgs.callPackage ./nix/smartcross_controller.nix {
+            # src 参数。构建 Rust 项目所需要的文件。无关的文件如 README 将不会被带入编译环境,也就不会因为修改而引发重新构建
+            src = pkgs.lib.cleanSourceWith {
+              src = ./.;
+              filter = protoOrCargo;
+            };
+
+            # 语法糖,用于传递同名的参数
+            inherit craneLib target libubootenv;
+          };
+
+          # 并不真正产生二进制产物,只是用于提供构建环境
+          env = pkgs.callPackage (
+            { mkShell, llvm, clang }:
+            with smartcross_controller;
+            mkShell {
+              nativeBuildInputs = nativeBuildInputs ++ [ llvm clang ];
+              inherit buildInputs CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER;
+            }
+          ) {};
+        };
+      });
+}
+
+

接下来运行 nix build '.#' 即可开始 smartcross_controller 的构建。构建完成后,将留下 result 目录。

+
$ ls -ld result
+lrwxrwxrwx 1 pgw pgw 97 Dec 11 17:17 result -> /nix/store/7zsmhixg5kf03ni0q0cc8i75c47kw8vr-smartcross_controller-aarch64-unknown-linux-gnu-0.1.0/
+$ ls result/bin/
+smartcross_controller*  updater*
+$ file result/bin/smartcross_controller
+result/bin/smartcross_controller: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /nix/store/0kbwlxnaxl0jly5szmgxqljrj3dxzyw8-glibc-aarch64-unknown-linux-gnu-2.35-163/lib/ld-linux-aarch64.so.1, for GNU/Linux 2.6.32, not stripped
+
+

使用 nix build '.#libubootenv' 可以单独构建 libubootenv,使用 nix develop '.#env' 可以进入一个 shell,这里面有全部定义好的构建环境。

+
$ nix develop '.#env'
+[user@host SmartCrossCtrl]$ rustc --version
+rustc 1.65.0 (897e37553 2022-11-02)
+[user@host SmartCrossCtrl]$ cargo build
+...(build success)
+
+ + + +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+用 Nix 管理交叉编译 Rust 项目的环境Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/page/1/index.html b/page/1/index.html new file mode 100644 index 0000000..3001ba9 --- /dev/null +++ b/page/1/index.html @@ -0,0 +1,6 @@ + + + + +Redirect +

Click here to be redirected.

diff --git a/page/2/index.html b/page/2/index.html new file mode 100644 index 0000000..01e8633 --- /dev/null +++ b/page/2/index.html @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+ + + +
+ +
+

+ Too many channels in Rust but only one in Go +

+ +
+ +
+ +

Channel 是异步编程 CSP 模型和 Actor 模型的重要组成部分,是一种用于消息同步的数据结构。Go 语言中的 chan 类型即是一种 channel 的实现。在使用 Rust 进行异步编程的时候也需要使用 channel。然而 Rust 中的 channel 似乎太多了。

+ +
+ +
+ + + +
+ +
+

+ 用 Julia 编写 CUDA 程序 +

+ +
+ +
+

CUDA 本身是一个 C 库,而 CUDA kernel 则需要使用扩展的 C/C++ 语法。但 CUDA.jl 让 Julia CUDA 编程成为可能。然而虽然 CUDA.jl 实现了绝大多数 CUDA 的功能,但其文档仍很不完善。本文补充了一些常见 CUDA 功能在 Julia 中的写法。本文假设读者预先具有 Julia,CUDA,以及 CUDA.jl 的基本知识。

+ +
+ +
+ + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/page/3/index.html b/page/3/index.html new file mode 100644 index 0000000..7a139ac --- /dev/null +++ b/page/3/index.html @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+ +
+ +
+

+ 保研经历总结 +

+ +
+ +
+

2020年保研算是告一段落了。因为基地里面要组织给学弟学妹的分享活动,借此机会正好来写一篇总结。

+ +
+ +
+ +
+ +
+

+ Rust 编译到 musl target 的踩坑记录 +

+ +
+ +
+

Rust 在 x86_64-unknown-linux-gnu 目标下默认会动态链接到系统 C 运行时,而不同发行版之间的 libc 可能会有兼容性问题。如果想要把一次编译好的可执行文件放到不同的 Linux 发行版上面去跑,最好采用 x86_64-unknown-linux-musl 目标进行静态编译。

+ +
+ +
+ + + + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/page/4/index.html b/page/4/index.html new file mode 100644 index 0000000..c1f1fff --- /dev/null +++ b/page/4/index.html @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+ + + + + +
+ +
+

+ 从 C++ 的错误处理说起 +

+ +
+ +
+

错误处理是一个非常重要的软件工程问题。对软件中出现的非致命错误的不当处理,是几乎所有的灾难性系统故障的诱因。 编程语言往往需要提供一些用于错误处理的语言设施,这些设施反过来会影响项目中错误处理的方式。不同的语言错误处理方式不同。例如 Java 采用基于 try-throw-catch 语法的异常机制,而 Go 语言则选择手动检测函数返回的 error 对象。一个令人惊讶的事实是,C++ 到现在还没有一个被广泛接受的错误处理方式。

+ +
+ +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/page/5/index.html b/page/5/index.html new file mode 100644 index 0000000..47d63ce --- /dev/null +++ b/page/5/index.html @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+ + + +
+ +
+

+ \(\LaTeX\) 公式 +

+ +
+ +
+

\(\LaTeX\) 是一款非常优秀的文档准备系统,它强大的数学排版功能举世闻名。由于 Mathjax 的广泛采用,\(\LaTeX\) 数学公式也成为了 Web 技术上数学公式排版的事实标准。但 \(\LaTeX\) 的学习曲线陡峭,基本的命令难以轻松应对实际写作中遇到的复杂公式。本文选取并实现了 \(\textrm{The \TeX{}book}\) 第 18 章末尾提供的 20 个 Chanllenge。以期为想要深入学习 \(\LaTeX\) 公式排版的读者提供参考。

+ +
+ +
+ + + + + +
+ +
+

+ 使用基于 Github issue 的留言系统 +

+ +
+ +
+

流行的博客留言系统包括 Disqus 等,但是我并没有 Disqus 帐号,也并不想注册一个。考虑到该博客的受众应该都有 Github 帐号,采用基于 Github issue 的系统应该是合适的,而且还可以享受邮件提醒等功能。我选择了 utteranc.es 的方案。

+ +
+ +
+ +
+ + +
+
+ + + +
+ + + + + + + diff --git a/page/6/index.html b/page/6/index.html new file mode 100644 index 0000000..6f719c9 --- /dev/null +++ b/page/6/index.html @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+ +
+ +
+

+ 为什么编程语言总是应该使用UTF-8而不是UTF-16 +

+ +
+ +
+

前段时间研究字符编码的时候,看到了一个知乎问题,里面的回答基本上都概念不清,事实上,Unicode “字符”、 +“字符串”、“编码”等词语涉及到非常复杂的概念。而目前介绍这个主题的中文文章似乎较为稀少,于是有了这篇文章。

+ +
+ +
+ +
+ +
+

+ ACM 错误集 +

+ +
+ +
+

以下是平时做题时造成不能一遍AC的原因。

+ +
+ +
+ + + +
+ +
+

+ About Me +

+ +
+ +
+

I'm a CS graduate student of SJTU. My undergraduate degree was completed at NWPU.

+ +
+ +
+ + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/page/7/index.html b/page/7/index.html new file mode 100644 index 0000000..c94e3c0 --- /dev/null +++ b/page/7/index.html @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+ + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/perl-hex/index.html b/perl-hex/index.html new file mode 100644 index 0000000..f6f21cc --- /dev/null +++ b/perl-hex/index.html @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + pg999w's blog - 用 Perl 进制转换 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ 用 Perl 进制转换 +

+ +
+ +
+

现在需要把一堆十进制数转换为二进制数,在 Vim 里可以用 :'<,'>!command 来做转换。但是我发现常见的行处理程序 awk 根本搞不定进制转换,所以还是用最强大的字符处理语言 Perl 来搞。命令如下:

+
perl -ne 'printf("%032b\n", $_)'
+
+

如果输入是十六进制呢?加个 hex 函数就行:

+
perl -ne 'printf("%032b\n", hex($_))'
+
+

如果一行有多个字段,只想转换第二个呢?这就要用到 -a 参数,此时 $_ 变为数组 @F

+
perl -ane 'printf("%s\t%032b\n", @F[0], hex(@F[1]))'
+
+

最后注意在 Vim 中 % 符号需要转义。

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+用 Perl 进制转换Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/perl-replace/index.html b/perl-replace/index.html new file mode 100644 index 0000000..990701b --- /dev/null +++ b/perl-replace/index.html @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + pg999w's blog - 用 Perl 做查找替换 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ 用 Perl 做查找替换 +

+ +
+ +
+

现在需要把一篇文章中两个中文字符中的回车给删掉。这时候需要用到支持 Unicode 的正则表达式。这时候我们还是用最强大的字符处理语言 Perl 来搞。命令如下:

+
perl -CSAD -0p -i.bak -e 's/(\p{category=Po}|\p{sc=Han})\n *(\p{sc=Han})/$1$2/gms' file.md
+
+

如果不涉及 Unicode 处理,可以不用加 -CSAD,如果处理不跨行,可以不用 -0,如果不需要备份文件,可以删去 .bak

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+用 Perl 做查找替换Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..3bf8313 --- /dev/null +++ b/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: +Allow: / +Sitemap: https://blog.pg999w.top/sitemap.xml diff --git a/ros-docker/index.html b/ros-docker/index.html new file mode 100644 index 0000000..15beb05 --- /dev/null +++ b/ros-docker/index.html @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + pg999w's blog - Docker 搭建 RoboMaster RoboRTS 框架构建环境 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ Docker 搭建 RoboMaster RoboRTS 框架构建环境 +

+ +
+ +
+

RoboRTS 框架用于大疆的 RoboMaster ICRA 人工智能挑战赛。其构建环境基于 ROS,在非 Ubuntu/CentOS 的 Linux 机器上面安装较为困难。于是我们采用基于 docker 的构建方案。

+ +

Dockerfile 如下:

+
FROM ros:kinetic-perception
+COPY ./MVS-2.0.0_x86_64_20191126.deb /root/MVS-2.0.0_x86_64_20191126.deb
+# 换源
+RUN sed -i "s/archive.ubuntu.com/mirrors.aliyun.com/g" /etc/apt/sources.list && \
+    sed -i "s/security.ubuntu.com/mirrors.aliyun.com/g" /etc/apt/sources.list && \
+    echo "deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ xenial main" > /etc/apt/sources.list.d/ros1-latest.list && \
+    rm -Rf /var/lib/apt/lists/* && \
+    apt update
+
+# 安装必须软件
+RUN apt-get install -y ros-kinetic-opencv3              \
+                       ros-kinetic-cv-bridge            \
+                       ros-kinetic-image-transport      \
+                       ros-kinetic-stage-ros            \
+                       ros-kinetic-map-server           \
+                       ros-kinetic-laser-geometry       \
+                       ros-kinetic-interactive-markers  \
+                       ros-kinetic-tf                   \
+                       ros-kinetic-pcl-*                \
+                       ros-kinetic-libg2o               \
+                       ros-kinetic-rplidar-ros          \
+                       ros-kinetic-rviz                 \
+                       ros-kinetic-librealsense2        \
+                       protobuf-compiler                \
+                       libprotobuf-dev                  \
+                       libsuitesparse-dev               \
+                       libgoogle-glog-dev &&            \
+    dpkg -i /root/MVS-2.0.0_x86_64_20191126.deb &&      \
+    rm /root/MVS-2.0.0_x86_64_20191126.deb &&           \
+    apt-get clean &&                                    \
+    mkdir -p /root/catkin_ws/src
+
+

运行 docker build 即可构建,其中 roborts:1.0 是镜像名。

+
docker build --network=host -t roborts:1.0 .
+
+

以下命令启动一个 docker 镜像并将当前目录挂载到镜像内的 /root/catkin_ws/

+
docker run --rm -it --name ros-test -v $PWD/:/root/catkin_ws/ roborts:1.0
+
+

为了在 CLion 中编写该项目,我们需要在 Dockerfile 中再添加一层来安装 ssh 相关的包。

+
RUN apt-get install -y ssh gdb rsync \
+ && apt-get clean \
+ && ( \
+    echo 'LogLevel DEBUG2'; \
+    echo 'PermitRootLogin yes'; \
+    echo 'PasswordAuthentication yes'; \
+    echo 'Subsystem sftp /usr/lib/openssh/sftp-server'; \
+    echo 'PermitUserEnvironment yes'; \
+  ) > /etc/ssh/sshd_config_clion \
+ && mkdir -p /root/.ssh \
+ && . /opt/ros/kinetic/setup.sh \
+ && env > /root/.ssh/environment \
+ && mkdir /run/sshd \
+ && echo "root:password" | chpasswd
+
+CMD /usr/sbin/sshd -D -e -f /etc/ssh/sshd_config_clion
+
+

用下面的命令启动 demon 容器。

+
docker run -d --cap-add sys_ptrace -p127.0.0.1:2222:22 --name clion_remote_env roborts:ssh
+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+Docker 搭建 RoboMaster RoboRTS 框架构建环境Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/rss.xml b/rss.xml new file mode 100644 index 0000000..6246ab6 --- /dev/null +++ b/rss.xml @@ -0,0 +1,731 @@ + + + + pg999w's blog + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Wed, 24 Jan 2024 00:00:00 +0000 + + 在 Typst 中使用 Latin Modern 家族 + Wed, 24 Jan 2024 00:00:00 +0000 + Unknown + https://blog.pg999w.top/typst-latin-modern/ + https://blog.pg999w.top/typst-latin-modern/ + <p>高德纳在开发 TeX 时,也设计了一套字体叫 Computer Modern,作为 TeX 的默认字体。然而当时字体是采用 METAFONT 制作的,和当今的字体标准 OpenType 并不兼容。Latin Modern 通过技术手段将 Computer Modern 转换到了 OpenType 格式,并且做了扩充和微调。所以我们在 Typst 中也可以调用 Latin Modern 字体。</p> + + + + 2023 个人年度电影 + Mon, 01 Jan 2024 00:00:00 +0000 + Unknown + https://blog.pg999w.top/movie-2023/ + https://blog.pg999w.top/movie-2023/ + <p>2022 年由于疫情和学业的各种原因,我观看的电影实在太少,以至于放弃了当年的年度电影评选。2023 年,在完全恢复了正常的生活秩序后,观影频次有所提高。本年我一共看了 40 部电影,其中有 9 部是动画片。</p> + + + + Rust 和 C++ 的对象生命管理 + Tue, 14 Nov 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/rust-cpp-object-management/ + https://blog.pg999w.top/rust-cpp-object-management/ + <p>Rust 和 C++ 的对象都是值语义,都采用了 RAII 惯用法。所以他们需要处理类似的对象生命周期问题:需要专门的代码来处理对象的初始化,复制和析构。下面进行一个比较,我们能够看到两种语言之间内在的对称性。</p> + + + + 为 Typst 添加中文排版支持 + Sat, 22 Jul 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/typst-clreq/ + https://blog.pg999w.top/typst-clreq/ + <style> +img { +max-width: 300px; +display: block; +margin: auto; +} +</style> +<p>Typst 是一个 2023 年初开源的一个排版软件。类似于 LaTeX,它通过纯文本编写源代码,然后通过编译器将源代码转换为排版好的 PDF 文件。虽然目前 Typst 的生态还不如 LaTeX,但是比起 LaTeX,它有一些明显的优势:</p> + + + + 在中国大陆境内编辑维基百科 + Fri, 27 Jan 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/wiki-exemption/ + https://blog.pg999w.top/wiki-exemption/ + <p>由于 GFW 的存在,在中国境内使用维基百科必须要透过代理进行访问。然而为了避免恶意破坏,维基百科禁止匿名代理服务器修改词条。如果你的代理不幸处于封禁的 IP 段,那么将会得到类似于这样的警告:</p> + + + + 用 Nix 管理交叉编译 Rust 项目的环境 + Sun, 11 Dec 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nix-smartcross/ + https://blog.pg999w.top/nix-smartcross/ + <p>SmartCross 项目的介绍见<a href="https://blog.t123yh.xyz:2/index.php/archives/1077">这里</a>。其中的控制器组件用 Rust 写成,需要编译到 aarch64 平台。我尝试写了一个 <a href="https://nixos.org/">Nix</a> 表达式来管理该项目的环境。</p> + + + + Too many channels in Rust but only one in Go + Thu, 24 Mar 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/too-many-channels/ + https://blog.pg999w.top/too-many-channels/ + <style type="text/css"> +em, strong { +color: purple; +} +img { +display: block; +margin: 0 auto; +} +</style> +<p>Channel 是异步编程 CSP 模型和 Actor 模型的重要组成部分,是一种用于消息同步的数据结构。Go 语言中的 <code>chan</code> 类型即是一种 channel 的实现。在使用 Rust 进行异步编程的时候也需要使用 channel。然而 Rust 中的 channel 似乎太多了。</p> + + + + 2021 个人年度电影 + Sun, 06 Feb 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/movie-2021/ + https://blog.pg999w.top/movie-2021/ + <p>今年的年度电影规则和去年一样。虽然名单在元旦时就确定了,可是由于各种原因,这篇博客文章拖到了春节最后一天才写出来。值得一提的是所有入选电影全部是 2021 年新片。一方面当然是我接触新片更频繁了,另一方面也从侧面看出业界确实在复苏。</p> +<h2 id="2021-nian-du-dian-ying">2021 年度电影</h2> +<ul> +<li> +<p><strong>rank 1</strong> 《沙丘(Dune)》2021 丹尼斯·维伦纽瓦</p> +<p>如果说2021年还有什么电影值得人们非进电影院不可,那首先应当是《沙丘》。如果2021年还有什么在电影体现了一流的工业水准,那一定有《沙丘》。如果2021年有什么值得一提的科幻片,那只能是《沙丘》。</p> +</li> +<li> +<p><strong>rank 2</strong> 《花束般的恋爱(花束みたいな恋をした)》2021 土井裕泰</p> +<p>我们和剧中的主人公的境况当然千差万别,但是步入社会的大学生,为了现实的压力而终于放弃了曾经热爱的东西——这种恐惧确实传达给我了。</p> +</li> +<li> +<p><strong>rank 3</strong> 《酷爱电影的庞波小姐(映画大好きポンポさん)》2021 平尾隆之</p> +<p>我关注的影评人给这部电影打了不少低分。可是在我这里,尽管剧情确实有值得严肃的影评人诟病之处,但形式上的趣味压倒了一切。这部动画里一切外在的形式,包括突出的剪辑点,反复的插叙,以及精确控制的时间,无不体现了导演「剪辑使电影变得有趣」的理念。</p> +</li> +</ul> +<h2 id="2021-nian-du-yuan-sheng-yin-le">2021 年度原声音乐</h2> +<ul> +<li> +<p>《倒数时刻(Tick, Tick… boom!)》2021 艺术家:Janason Larson</p> +<p>选择音乐剧改编电影似乎有点作弊。不过就原作中的几首经典歌曲来说,比起原版的音乐剧,我更喜欢这张原声带的版本。</p> +</li> +</ul> +<h2 id="wang-nian-nian-du-dian-ying">往年年度电影</h2> +<ul> +<li><a href="https://blog.pg999w.top/nanoblog/movie-2020/">2020 个人年度电影</a></li> +<li><a href="https://blog.pg999w.top/nanoblog/movie-2019/">2019 个人年度电影</a></li> +</ul> + + + + 用 Julia 编写 CUDA 程序 + Sat, 22 Jan 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/julia-cuda/ + https://blog.pg999w.top/julia-cuda/ + <p>CUDA 本身是一个 C 库,而 CUDA kernel 则需要使用扩展的 C/C++ 语法。但 <a href="https://cuda.juliagpu.org/stable/">CUDA.jl</a> 让 Julia CUDA 编程成为可能。然而虽然 CUDA.jl 实现了绝大多数 CUDA 的功能,但其文档仍很不完善。本文补充了一些常见 CUDA 功能在 Julia 中的写法。本文假设读者预先具有 Julia,CUDA,以及 CUDA.jl 的基本知识。</p> + + + + 用 Perl 做查找替换 + Sat, 01 Jan 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/perl-replace/ + https://blog.pg999w.top/perl-replace/ + <p>现在需要把一篇文章中两个中文字符中的回车给删掉。这时候需要用到支持 Unicode 的正则表达式。这时候我们还是用最强大的字符处理语言 Perl 来搞。命令如下:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -CSAD -0p -i</span><span>.bak</span><span style="color:#bf616a;"> -e </span><span>&#39;</span><span style="color:#a3be8c;">s/(\p{category=Po}|\p{sc=Han})\n *(\p{sc=Han})/$1$2/gms</span><span>&#39; file.md +</span></code></pre> +<p>如果不涉及 Unicode 处理,可以不用加 <code>-CSAD</code>,如果处理不跨行,可以不用 <code>-0</code>,如果不需要备份文件,可以删去 <code>.bak</code>。</p> + + + + 保研经历总结 + Fri, 01 Oct 2021 00:00:00 +0000 + Unknown + https://blog.pg999w.top/baoyan/ + https://blog.pg999w.top/baoyan/ + <p>2020年保研算是告一段落了。因为基地里面要组织给学弟学妹的分享活动,借此机会正好来写一篇总结。</p> + + + + Rust 编译到 musl target 的踩坑记录 + Sun, 30 May 2021 00:00:00 +0000 + Unknown + https://blog.pg999w.top/rust-musl-target/ + https://blog.pg999w.top/rust-musl-target/ + <p>Rust 在 x86_64-unknown-linux-gnu 目标下默认会动态链接到系统 C 运行时,而不同发行版之间的 libc 可能会有兼容性问题。如果想要把一次编译好的可执行文件放到不同的 Linux 发行版上面去跑,最好采用 x86_64-unknown-linux-musl 目标进行静态编译。</p> + + + + 2020 个人年度电影 + Mon, 04 Jan 2021 00:00:00 +0000 + Unknown + https://blog.pg999w.top/movie-2020/ + https://blog.pg999w.top/movie-2020/ + <h2 id="gui-ze">规则</h2> +<p>今年是第二年自娱自乐的评选了。同样基本按照去年的规则来,但是稍有修订。入选的电影满足如下条件:</p> +<ol> +<li>是本年新观看的电影。</li> +<li>包含观看的资源片,不限于院线片。</li> +<li>仅以个人喜好程度排序。</li> +</ol> +<p>另设「年度原声音乐」一个名额,范围是今年听到的电影原声带专辑。</p> +<h2 id="2020-nian-du-dian-ying">2020 年度电影</h2> +<p>今年首次出现了院线片。第三名本来是波兰斯基的《我控诉(J'accuse)》,可是2020年的最后一天在影院看了《心灵奇旅》,大概是受了氛围的影响,我临时改变了主意。</p> +<p>本次入围前三名的有两部动画电影。但是令人担忧的是,高产优质作品的京都动画受火灾的影响,前途未卜;而皮克斯在当下流量为王的流媒体时代,已经开始受到资本的裹挟。在动画电影领域,2020年会成为接下来十年中最好的一年吗?我无法乐观地给出否定的回答。</p> +<ul> +<li> +<p><strong>rank 1</strong> 《利兹与青鸟(リズと青い鳥)》2018 山田尚子</p> +<p>这是我近年来看过的电影感和作者风格最强的长篇动画电影。影片的整个核心故事都在学校教学楼这个封闭空间内完成。然而在这个受限的空间中,从与脚步声配合的配乐,到精心安排的背景摆件,到处充满着细节。山田尚子在九十分钟的时间内,尽情地使用视听语言,探索到了人物的内心最深之处。</p> +</li> +<li> +<p><strong>rank 2</strong> 《爆裂鼓手(Whiplash)》2014 达米恩·查泽雷</p> +<p>我听说真正的爵士乐迷并不看好这个扭曲了爵士乐精神的片子。但是对于我这样不通爵士乐的人来说,那种癫狂的戏剧张力,反高潮的情节转折,使这部电影充满了力气。</p> +</li> +<li> +<p><strong>rank 3</strong> 《心灵奇旅(Soul)》2020 彼特·道格特</p> +<p>也许皮克斯已经不是曾经的那个皮克斯了,但是非凡的想象力与一流的动画工业的结合,至少现在还没有丢失。从生之彼岸的桥上面下坠的画面,久违地给人以极大的愉悦。</p> +</li> +</ul> +<h2 id="2020-nian-du-yuan-sheng-yin-le">2020 年度原声音乐</h2> +<ul> +<li> +<p>《「girls,dance,staircase」(电影『利兹与青鸟』原声带)》2019 艺术家:牛尾憲輔/松田彬人</p> +<p>牛尾憲輔自然是延续一直以来的配乐风格,而这次松田彬人为配合电影专门按古典交响曲格式创作的《利兹与青鸟》 +完整的四乐章,实在难得。</p> +</li> +</ul> +<h2 id="wang-nian-nian-du-dian-ying">往年年度电影</h2> +<ul> +<li><a href="https://blog.pg999w.top/nanoblog/movie-2019/">2019 个人年度电影</a></li> +</ul> + + + + 2020 个人年度电影 + Mon, 04 Jan 2021 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nanoblog/movie-2020/ + https://blog.pg999w.top/nanoblog/movie-2020/ + <h2 id="gui-ze">规则</h2> +<p>今年是第二年自娱自乐的评选了。同样基本按照去年的规则来,但是稍有修订。 +入选的电影满足如下条件:</p> +<ol> +<li>是本年新观看的电影。</li> +<li>包含观看的资源片,不限于院线片。</li> +<li>仅以个人喜好程度排序。</li> +</ol> +<p>另设「年度原声音乐」一个名额,范围是今年听到的电影原声带专辑。</p> +<h2 id="2020-nian-du-dian-ying">2020 年度电影</h2> +<p>今年首次出现了院线片。 +第三名本来是波兰斯基的《我控诉(J'accuse)》,可是2020年的最后一天在影院看了《心灵奇旅》, +大概是受了氛围的影响,我临时改变了主意。</p> +<p>本次入围前三名的有两部动画电影。但是令人担忧的是,高产优质作品的京都动画受火灾的影响,前途未卜; +而皮克斯在当下流量为王的流媒体时代,已经开始受到资本的裹挟。在动画电影领域,2020年会成为接下来十年中最好的一年吗? +我无法乐观地给出否定的回答。</p> +<ul> +<li> +<p><strong>rank 1</strong> 《利兹与青鸟(リズと青い鳥)》2018 山田尚子</p> +<p>这是我近年来看过的电影感和作者风格最强的长篇动画电影。影片的整个核心故事都在学校教学楼这个封闭空间内完成。 +然而在这个受限的空间中,从与脚步声配合的配乐,到精心安排的背景摆件,到处充满着细节。 +山田尚子在九十分钟的时间内,尽情地使用视听语言,探索到了人物的内心最深之处。</p> +</li> +<li> +<p><strong>rank 2</strong> 《爆裂鼓手(Whiplash)》2014 达米恩·查泽雷</p> +<p>我听说真正的爵士乐迷并不看好这个扭曲了爵士乐精神的片子。但是对于我这样不通爵士乐的人来说, +那种癫狂的戏剧张力,反高潮的情节转折,使这部电影充满了力气。</p> +</li> +<li> +<p><strong>rank 3</strong> 《心灵奇旅(Soul)》2020 彼特·道格特</p> +<p>也许皮克斯已经不是曾经的那个皮克斯了,但是非凡的想象力与一流的动画工业的结合,至少现在还没有丢失。 +从生之彼岸的桥上面下坠的画面,久违地给人以极大的愉悦。</p> +</li> +</ul> +<h2 id="2020-nian-du-yuan-sheng-yin-le">2020 年度原声音乐</h2> +<ul> +<li> +<p>《「girls,dance,staircase」(电影『利兹与青鸟』原声带)》2019 艺术家:牛尾憲輔/松田彬人</p> +<p>牛尾憲輔自然是延续一直以来的配乐风格,而这次松田彬人为配合电影专门按古典交响曲格式创作的《利兹与青鸟》 +完整的四乐章,实在难得。</p> +</li> +</ul> +<h2 id="wang-nian-nian-du-dian-ying">往年年度电影</h2> +<ul> +<li><a href="https://blog.pg999w.top/nanoblog/movie-2019/">2019 个人年度电影</a></li> +</ul> + + + + 赏析:德沃夏克,第九交响曲“自新世界”,作品95号 + Sun, 06 Dec 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/dvorak-symphony-9/ + https://blog.pg999w.top/dvorak-symphony-9/ + <p><strong>第二乐章 Largo “广板”,D♭大调</strong></p> + + + + 赏析:德沃夏克,第九交响曲“自新世界”,作品95号 + Sun, 06 Dec 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nanoblog/dvorak-symphony-9/ + https://blog.pg999w.top/nanoblog/dvorak-symphony-9/ + <p><strong>第二乐章 Largo “广板”,D♭大调</strong></p> + + + + C++ 每三年才解决一点点问题 + Mon, 30 Nov 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/c-template-constraint/ + https://blog.pg999w.top/c-template-constraint/ + <p>或:怎样优雅地给 C++ 模板添加约束?</p> + + + + Docker 搭建 RoboMaster RoboRTS 框架构建环境 + Sun, 02 Aug 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nanoblog/ros-docker/ + https://blog.pg999w.top/nanoblog/ros-docker/ + <p><a href="https://github.com/RoboMaster/RoboRTS">RoboRTS</a> 框架用于大疆的 RoboMaster ICRA 人工智能挑战赛。其构建环境基于 <a href="https://www.ros.org/">ROS</a>, +在非 Ubuntu/CentOS 的 Linux 机器上面安装较为困难。于是我们采用基于 docker 的构建方案。</p> + + + + Docker 搭建 RoboMaster RoboRTS 框架构建环境 + Sun, 02 Aug 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/ros-docker/ + https://blog.pg999w.top/ros-docker/ + <p><a href="https://github.com/RoboMaster/RoboRTS">RoboRTS</a> 框架用于大疆的 RoboMaster ICRA 人工智能挑战赛。其构建环境基于 <a href="https://www.ros.org/">ROS</a>,在非 Ubuntu/CentOS 的 Linux 机器上面安装较为困难。于是我们采用基于 docker 的构建方案。</p> + + + + 用 Perl 进制转换 + Mon, 22 Jun 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nanoblog/perl-hex/ + https://blog.pg999w.top/nanoblog/perl-hex/ + <p>现在需要把一堆十进制数转换为二进制数,在 Vim 里可以用 <code>:'&lt;,'&gt;!command</code> 来做转换。 +但是我发现常见的行处理程序 <code>awk</code> 根本搞不定进制转换,所以还是用最强大的字符处理语言 Perl 来搞。 +命令如下:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ne </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%032b\n&quot;, $_)</span><span>&#39; +</span></code></pre> +<p>如果输入是十六进制呢?加个 <code>hex</code> 函数就行:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ne </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%032b\n&quot;, hex($_))</span><span>&#39; +</span></code></pre> +<p>如果一行有多个字段,只想转换第二个呢?这就要用到 <code>-a</code> 参数,此时 <code>$_</code> 变为数组 <code>@F</code>:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ane </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%s\t%032b\n&quot;, @F[0], hex(@F[1]))</span><span>&#39; +</span></code></pre> +<p>最后注意在 Vim 中 <code>%</code> 符号需要转义。</p> + + + + 用 Perl 进制转换 + Mon, 22 Jun 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/perl-hex/ + https://blog.pg999w.top/perl-hex/ + <p>现在需要把一堆十进制数转换为二进制数,在 Vim 里可以用 <code>:'&lt;,'&gt;!command</code> 来做转换。但是我发现常见的行处理程序 <code>awk</code> 根本搞不定进制转换,所以还是用最强大的字符处理语言 Perl 来搞。命令如下:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ne </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%032b\n&quot;, $_)</span><span>&#39; +</span></code></pre> +<p>如果输入是十六进制呢?加个 <code>hex</code> 函数就行:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ne </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%032b\n&quot;, hex($_))</span><span>&#39; +</span></code></pre> +<p>如果一行有多个字段,只想转换第二个呢?这就要用到 <code>-a</code> 参数,此时 <code>$_</code> 变为数组 <code>@F</code>:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ane </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%s\t%032b\n&quot;, @F[0], hex(@F[1]))</span><span>&#39; +</span></code></pre> +<p>最后注意在 Vim 中 <code>%</code> 符号需要转义。</p> + + + + 从 C++ 的错误处理说起 + Thu, 16 Apr 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/c-p0709/ + https://blog.pg999w.top/c-p0709/ + <p><strong>错误处理</strong>是一个非常重要的软件工程问题。对软件中出现的非致命错误的不当处理,是几乎所有的灾难性系统故障的诱因。 编程语言往往需要提供一些用于错误处理的语言设施,这些设施反过来会影响项目中错误处理的方式。不同的语言错误处理方式不同。例如 Java 采用基于 try-throw-catch 语法的异常机制,而 Go 语言则选择手动检测函数返回的 <code>error</code> 对象。一个令人惊讶的事实是,C++ 到现在还没有一个被广泛接受的错误处理方式。</p> + + + + 2019 个人年度电影 + Sat, 28 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/movie-2019/ + https://blog.pg999w.top/movie-2019/ + <h2 id="gui-ze">规则</h2> +<p>虽然是一个自娱自乐的东西,但是不以规矩,不成方圆,入选的电影满足如下条件:</p> +<ol> +<li>是本年新观看的电影。</li> +<li>包含观看的电影资源,不限于院线片。</li> +<li>仅以个人喜好程度排序,不单独考虑艺术价值,社会影响等因素。</li> +</ol> +<h2 id="2019-nian-du-dian-ying">2019 年度电影</h2> +<p>遗憾的是,我选出的三部电影全部是非院线片,这意味着这些电影基本都是在较差的观影条件下观看的。</p> +<ul> +<li> +<p><strong>rank 1</strong> 《寄生虫(기생충)》2019 奉俊昊</p> +<p>最为传统的剧作结构,达到了最好的效果。这部电影给了我长久以来都没有过的,被情节抓住的体验。</p> +</li> +<li> +<p><strong>rank 2</strong> 《宣告黎明的露之歌(夜明け告げるルーのうた)》2017 汤浅政明</p> +<p>没有什么特殊的理由,这部电影和我产生了某种共振。汤浅政明创造的这个异国故事,包含着一种奇妙的气氛,确实触动了我心中某个部分。</p> +</li> +<li> +<p><strong>rank 3</strong> 《登月第一人(First Man)》2018 达米恩·查泽雷</p> +<p>许多电影里都有太空飞船,但是这一部真实地展现了那种天空深处的危机四伏。舱内狭隘的视角,剧烈的抖动,死亡与濒死体验,都无比真实。</p> +</li> +</ul> +<h2 id="2019-nian-du-yuan-sheng-yin-le">2019 年度原声音乐</h2> +<ul> +<li> +<p>《冰雪奇缘2(Frozen II)》2019 艺术家:Robert Lopez/Kristen Anderson Lopez</p> +<p>电影没得洗,确实是迪士尼缺乏诚意的制作,但是原声音乐仍然是高水准。比起第一部,这部更加戏剧化的音乐我反而更喜欢。</p> +</li> +</ul> + + + + 2019 个人年度电影 + Sat, 28 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nanoblog/movie-2019/ + https://blog.pg999w.top/nanoblog/movie-2019/ + <h2 id="gui-ze">规则</h2> +<p>虽然是一个自娱自乐的东西,但是不以规矩,不成方圆,入选的电影满足如下条件:</p> +<ol> +<li>是本年新观看的电影。</li> +<li>包含观看的电影资源,不限于院线片。</li> +<li>仅以个人喜好程度排序,不单独考虑艺术价值,社会影响等因素。</li> +</ol> +<h2 id="2019-nian-du-dian-ying">2019 年度电影</h2> +<p>遗憾的是,我选出的三部电影全部是非院线片,这意味着这些电影基本都是在较差的观影条件下观看的。</p> +<ul> +<li> +<p><strong>rank 1</strong> 《寄生虫(기생충)》2019 奉俊昊</p> +<p>最为传统的剧作结构,达到了最好的效果。这部电影给了我长久以来都没有过的,被情节抓住的体验。</p> +</li> +<li> +<p><strong>rank 2</strong> 《宣告黎明的露之歌(夜明け告げるルーのうた)》2017 汤浅政明</p> +<p>没有什么特殊的理由,这部电影和我产生了某种共振。汤浅政明创造的这个异国故事,包含着一种奇妙的气氛,确实触动了我心中某个部分。</p> +</li> +<li> +<p><strong>rank 3</strong> 《登月第一人(First Man)》2018 达米恩·查泽雷</p> +<p>许多电影里都有太空飞船,但是这一部真实地展现了那种天空深处的危机四伏。舱内狭隘的视角,剧烈的抖动,死亡与濒死体验,都无比真实。</p> +</li> +</ul> +<h2 id="2019-nian-du-yuan-sheng-yin-le">2019 年度原声音乐</h2> +<ul> +<li> +<p>《冰雪奇缘2(Frozen II)》2019 艺术家:Robert Lopez/Kristen Anderson Lopez</p> +<p>电影没得洗,确实是迪士尼缺乏诚意的制作,但是原声音乐仍然是高水准。比起第一部,这部更加戏剧化的音乐我反而更喜欢。</p> +</li> +</ul> + + + + Inverse clip in TikZ + Sun, 22 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nanoblog/tikz/ + https://blog.pg999w.top/nanoblog/tikz/ + <p><a href="https://en.wikipedia.org/wiki/PGF/TikZ">Ti<em>k</em>Z</a> 虽然强大,但是也过于复杂。下面尝试绘制下面的图形。</p> +<p><img src="../tikz.svg" alt="ellipse" /></p> +<p>首先绘制一个直立的图形,再全局旋转。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#b48ead;">\begin</span><span>{tikzpicture}[</span><span style="color:#bf616a;">rotate</span><span>=20,even odd rule] +</span></code></pre> +<p>绘制实线部分,一个椭圆,一个半椭圆和两条直线。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#96b5b4;">\coordinate</span><span> (move) at (0,3); +</span><span style="color:#96b5b4;">\draw</span><span> (move) ellipse [x radius=1,y radius=0.4]; +</span><span style="color:#96b5b4;">\draw</span><span> (-1,0) -- +(move); +</span><span style="color:#96b5b4;">\draw</span><span> (1,0) -- +(move); +</span><span style="color:#96b5b4;">\draw</span><span> (1,0) arc (0:-180:1 and 0.4); +</span><span style="color:#96b5b4;">\draw</span><span>[dashed] (1,0) arc (0:180:1 and 0.4); +</span></code></pre> +<p>接下来绘制带有遮挡关系的直线。这里需要使用一个辅助的样式 <code>invclip</code>,用于产生「剪除」的效果。<code>clip</code> 相当于一个蒙板,加上 <code>invclip</code> 后变为一个反向蒙板。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#96b5b4;">\tikzset</span><span>{ +</span><span> invclip/.style={ +</span><span> insert path={ (-3,-2) -- (-3,5) -- (3,5) -- (3,-2) -- (-3,-2) } +</span><span> } +</span><span>} +</span></code></pre> +<p><code>\foreach</code> 只运行两次。针对同一条直线,第一次 <code>\sty</code> 为 <code>dashed</code>,<code>\inv</code> 为 <code>{}</code>;第二次 <code>\sty</code> 为 <code>{}</code>,<code>\inv</code> 为 <code>invclip</code>。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#96b5b4;">\foreach \sty</span><span>/</span><span style="color:#96b5b4;">\inv</span><span> in {dashed/{},{}/invclip} { +</span><span> </span><span style="color:#b48ead;">\begin</span><span>{scope} +</span><span> </span><span style="color:#96b5b4;">\path</span><span>[clip,</span><span style="color:#96b5b4;">\inv</span><span>] +</span><span> (-1,0) +</span><span> -- +(move) -- ($</span><span style="color:#d08770;">(</span><span style="color:#bf616a;">move</span><span style="color:#d08770;">)</span><span>+</span><span style="color:#d08770;">(1</span><span>,</span><span style="color:#d08770;">0)</span><span>$) +</span><span> -- (1,0) arc (0:-180:1 and 0.4); +</span><span> </span><span style="color:#96b5b4;">\draw</span><span>[</span><span style="color:#96b5b4;">\sty</span><span>,thick] (0,4) -- (0,-1); +</span><span> </span><span style="color:#b48ead;">\end</span><span>{scope} +</span><span>} +</span><span style="color:#b48ead;">\end</span><span>{tikzpicture} +</span></code></pre> + + + + Inverse clip in TikZ + Sun, 22 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/tikz/ + https://blog.pg999w.top/tikz/ + <p><a href="https://en.wikipedia.org/wiki/PGF/TikZ">Ti<em>k</em>Z</a> 虽然强大,但是也过于复杂。下面尝试绘制下面的图形。</p> +<p><img src="../tikz.svg" alt="ellipse" /></p> +<p>首先绘制一个直立的图形,再全局旋转。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#b48ead;">\begin</span><span>{tikzpicture}[</span><span style="color:#bf616a;">rotate</span><span>=20,even odd rule] +</span></code></pre> +<p>绘制实线部分,一个椭圆,一个半椭圆和两条直线。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#96b5b4;">\coordinate</span><span> (move) at (0,3); +</span><span style="color:#96b5b4;">\draw</span><span> (move) ellipse [x radius=1,y radius=0.4]; +</span><span style="color:#96b5b4;">\draw</span><span> (-1,0) -- +(move); +</span><span style="color:#96b5b4;">\draw</span><span> (1,0) -- +(move); +</span><span style="color:#96b5b4;">\draw</span><span> (1,0) arc (0:-180:1 and 0.4); +</span><span style="color:#96b5b4;">\draw</span><span>[dashed] (1,0) arc (0:180:1 and 0.4); +</span></code></pre> +<p>接下来绘制带有遮挡关系的直线。这里需要使用一个辅助的样式 <code>invclip</code>,用于产生「剪除」的效果。<code>clip</code> 相当于一个蒙板,加上 <code>invclip</code> 后变为一个反向蒙板。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#96b5b4;">\tikzset</span><span>{ +</span><span> invclip/.style={ +</span><span> insert path={ (-3,-2) -- (-3,5) -- (3,5) -- (3,-2) -- (-3,-2) } +</span><span> } +</span><span>} +</span></code></pre> +<p><code>\foreach</code> 只运行两次。针对同一条直线,第一次 <code>\sty</code> 为 <code>dashed</code>,<code>\inv</code> 为 <code>{}</code>;第二次 <code>\sty</code> 为 <code>{}</code>,<code>\inv</code> 为 <code>invclip</code>。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#96b5b4;">\foreach \sty</span><span>/</span><span style="color:#96b5b4;">\inv</span><span> in {dashed/{},{}/invclip} { +</span><span> </span><span style="color:#b48ead;">\begin</span><span>{scope} +</span><span> </span><span style="color:#96b5b4;">\path</span><span>[clip,</span><span style="color:#96b5b4;">\inv</span><span>] +</span><span> (-1,0) +</span><span> -- +(move) -- ($</span><span style="color:#d08770;">(</span><span style="color:#bf616a;">move</span><span style="color:#d08770;">)</span><span>+</span><span style="color:#d08770;">(1</span><span>,</span><span style="color:#d08770;">0)</span><span>$) +</span><span> -- (1,0) arc (0:-180:1 and 0.4); +</span><span> </span><span style="color:#96b5b4;">\draw</span><span>[</span><span style="color:#96b5b4;">\sty</span><span>,thick] (0,4) -- (0,-1); +</span><span> </span><span style="color:#b48ead;">\end</span><span>{scope} +</span><span>} +</span><span style="color:#b48ead;">\end</span><span>{tikzpicture} +</span></code></pre> + + + + constexpr string + Thu, 19 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/constexpr-string/ + https://blog.pg999w.top/constexpr-string/ + <p>按照 <a href="https://akrzemi1.wordpress.com/2017/06/28/compile-time-string-concatenation/">Andrzej's C++ blog</a> 里这篇文章的思路,我实现了一个编译期的字符串拼接:</p> +<pre data-lang="c++" style="background-color:#eff1f5;color:#4f5b66;" class="language-c++ "><code class="language-c++" data-lang="c++"><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#b48ead;">class </span><span style="color:#d08770;">sstring </span><span style="color:#343d46;">{ +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">char</span><span style="color:#343d46;"> inner[N]; +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">() </span><span>= </span><span style="color:#b48ead;">default</span><span style="color:#343d46;">; +</span><span style="color:#343d46;"> +</span><span style="color:#b48ead;">public</span><span style="color:#343d46;">: +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">const char </span><span style="color:#343d46;">(</span><span>&amp;</span><span style="color:#343d46;">s)[N]) : </span><span style="color:#bf616a;">inner</span><span style="color:#343d46;">{} { +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> N; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i] </span><span>=</span><span style="color:#343d46;"> s[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">template</span><span style="color:#343d46;">&lt;</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> M&gt; +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">const</span><span style="color:#343d46;"> sstring&lt;M&gt; </span><span>&amp;</span><span style="color:#bf616a;">lhs</span><span style="color:#343d46;">, </span><span style="color:#b48ead;">const</span><span style="color:#343d46;"> sstring&lt;N </span><span>-</span><span style="color:#343d46;"> M&gt; </span><span>&amp;</span><span style="color:#bf616a;">rhs</span><span style="color:#343d46;">) : </span><span style="color:#bf616a;">inner</span><span style="color:#343d46;">{} { +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> M; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i] </span><span>=</span><span style="color:#343d46;"> lhs[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> N </span><span>-</span><span style="color:#343d46;"> M; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i </span><span>+</span><span style="color:#343d46;"> N] </span><span>=</span><span style="color:#343d46;"> rhs[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr char </span><span style="color:#8fa1b3;">operator[]</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int </span><span style="color:#bf616a;">i</span><span style="color:#343d46;">) </span><span style="color:#b48ead;">const </span><span style="color:#343d46;">{ +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">return</span><span style="color:#343d46;"> inner[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;">}</span><span>; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#bf616a;">sstring</span><span>(</span><span style="color:#b48ead;">char </span><span>(&amp;s)[N]) -&gt; </span><span style="color:#bf616a;">sstring</span><span>&lt;N&gt;; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> M, </span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#bf616a;">sstring</span><span>(</span><span style="color:#b48ead;">const</span><span> sstring&lt;M&gt; &amp;lhs, </span><span style="color:#b48ead;">const</span><span> sstring&lt;N&gt; &amp;rhs) -&gt; </span><span style="color:#bf616a;">sstring</span><span>&lt;N + M&gt;; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> M, </span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#b48ead;">constexpr auto </span><span style="color:#8fa1b3;">operator+</span><span>(</span><span style="color:#b48ead;">const</span><span> sstring&lt;M&gt; &amp;</span><span style="color:#bf616a;">lhs</span><span>, </span><span style="color:#b48ead;">const</span><span> sstring&lt;N&gt; &amp;</span><span style="color:#bf616a;">rhs</span><span>) { +</span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">sstring</span><span>(lhs, rhs); +</span><span>} +</span><span> +</span><span style="color:#b48ead;">constexpr</span><span> sstring s {&quot;</span><span style="color:#a3be8c;">123</span><span>&quot;}; +</span><span style="color:#b48ead;">constexpr</span><span> sstring q {&quot;</span><span style="color:#a3be8c;">456</span><span>&quot;}; +</span><span style="color:#b48ead;">constexpr</span><span> sstring r {s + q}; +</span></code></pre> +<ul> +<li><code>constexpr string</code> 有什么用?这至少在初始化全局静态变量时有用。<code>constexpr</code> 静态变量不会存在烦人的初始化顺序问题。</li> +<li>因为用到了 deducing guide,所以至少需要在 C++ 17 下编译。</li> +<li><code>std::string</code> 将在 C++ 20 支持 <code>constexpr</code>,不过编译器全部普及这个特性可能还要等好几年。 +<ul> +<li>C++ 现在有一种「<code>constexpr</code> Everything」的倾向。这是为了更好的实现元编程。这是好事。</li> +</ul> +</li> +<li><code>constexpr</code> 构造函数要求初始化每个每个子对象和非静态数据成员必须被初始化。奇怪的是,clang 可以通过未初始化的代码。</li> +</ul> + + + + constexpr string + Thu, 19 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nanoblog/constexpr-string/ + https://blog.pg999w.top/nanoblog/constexpr-string/ + <p>按照 <a href="https://akrzemi1.wordpress.com/2017/06/28/compile-time-string-concatenation/">Andrzej's C++ blog</a> 里这篇文章的思路,我实现了一个编译期的字符串拼接:</p> +<pre data-lang="c++" style="background-color:#eff1f5;color:#4f5b66;" class="language-c++ "><code class="language-c++" data-lang="c++"><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#b48ead;">class </span><span style="color:#d08770;">sstring </span><span style="color:#343d46;">{ +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">char</span><span style="color:#343d46;"> inner[N]; +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">() </span><span>= </span><span style="color:#b48ead;">default</span><span style="color:#343d46;">; +</span><span style="color:#343d46;"> +</span><span style="color:#b48ead;">public</span><span style="color:#343d46;">: +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">const char </span><span style="color:#343d46;">(</span><span>&amp;</span><span style="color:#343d46;">s)[N]) : </span><span style="color:#bf616a;">inner</span><span style="color:#343d46;">{} { +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> N; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i] </span><span>=</span><span style="color:#343d46;"> s[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">template</span><span style="color:#343d46;">&lt;</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> M&gt; +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">const</span><span style="color:#343d46;"> sstring&lt;M&gt; </span><span>&amp;</span><span style="color:#bf616a;">lhs</span><span style="color:#343d46;">, </span><span style="color:#b48ead;">const</span><span style="color:#343d46;"> sstring&lt;N </span><span>-</span><span style="color:#343d46;"> M&gt; </span><span>&amp;</span><span style="color:#bf616a;">rhs</span><span style="color:#343d46;">) : </span><span style="color:#bf616a;">inner</span><span style="color:#343d46;">{} { +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> M; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i] </span><span>=</span><span style="color:#343d46;"> lhs[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> N </span><span>-</span><span style="color:#343d46;"> M; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i </span><span>+</span><span style="color:#343d46;"> N] </span><span>=</span><span style="color:#343d46;"> rhs[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr char </span><span style="color:#8fa1b3;">operator[]</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int </span><span style="color:#bf616a;">i</span><span style="color:#343d46;">) </span><span style="color:#b48ead;">const </span><span style="color:#343d46;">{ +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">return</span><span style="color:#343d46;"> inner[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;">}</span><span>; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#bf616a;">sstring</span><span>(</span><span style="color:#b48ead;">char </span><span>(&amp;s)[N]) -&gt; </span><span style="color:#bf616a;">sstring</span><span>&lt;N&gt;; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> M, </span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#bf616a;">sstring</span><span>(</span><span style="color:#b48ead;">const</span><span> sstring&lt;M&gt; &amp;lhs, </span><span style="color:#b48ead;">const</span><span> sstring&lt;N&gt; &amp;rhs) -&gt; </span><span style="color:#bf616a;">sstring</span><span>&lt;N + M&gt;; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> M, </span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#b48ead;">constexpr auto </span><span style="color:#8fa1b3;">operator+</span><span>(</span><span style="color:#b48ead;">const</span><span> sstring&lt;M&gt; &amp;</span><span style="color:#bf616a;">lhs</span><span>, </span><span style="color:#b48ead;">const</span><span> sstring&lt;N&gt; &amp;</span><span style="color:#bf616a;">rhs</span><span>) { +</span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">sstring</span><span>(lhs, rhs); +</span><span>} +</span><span> +</span><span style="color:#b48ead;">constexpr</span><span> sstring s {&quot;</span><span style="color:#a3be8c;">123</span><span>&quot;}; +</span><span style="color:#b48ead;">constexpr</span><span> sstring q {&quot;</span><span style="color:#a3be8c;">456</span><span>&quot;}; +</span><span style="color:#b48ead;">constexpr</span><span> sstring r {s + q}; +</span></code></pre> +<ul> +<li><code>constexpr string</code> 有什么用?这至少在初始化全局静态变量时有用。<code>constexpr</code> 静态变量不会存在烦人的初始化顺序问题。</li> +<li>因为用到了 deducing guide,所以至少需要在 C++ 17 下编译。</li> +<li><code>std::string</code> 将在 C++ 20 支持 <code>constexpr</code>,不过编译器全部普及这个特性可能还要等好几年。 +<ul> +<li>C++ 现在有一种「<code>constexpr</code> Everything」的倾向。这是为了更好的实现元编程。这是好事。</li> +</ul> +</li> +<li><code>constexpr</code> 构造函数要求初始化每个每个子对象和非静态数据成员必须被初始化。奇怪的是,clang 可以通过未初始化的代码。</li> +</ul> + + + + \(\LaTeX\) 公式 + Wed, 07 Aug 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/latex-math-formula/ + https://blog.pg999w.top/latex-math-formula/ + <p>\(\LaTeX\) 是一款非常优秀的文档准备系统,它强大的数学排版功能举世闻名。由于 <a href="https://www.mathjax.org/">Mathjax</a> 的广泛采用,\(\LaTeX\) 数学公式也成为了 Web 技术上数学公式排版的事实标准。但 \(\LaTeX\) 的学习曲线陡峭,基本的命令难以轻松应对实际写作中遇到的复杂公式。本文选取并实现了 <a href="https://ctan.org/pkg/texbook">\(\textrm{The \TeX{}book}\)</a> 第 18 章末尾提供的 20 个 Chanllenge。以期为想要深入学习 \(\LaTeX\) 公式排版的读者提供参考。</p> + + + + 你应该使用 Neovim + Fri, 19 Apr 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/you-should-use-neovim/ + https://blog.pg999w.top/you-should-use-neovim/ + <p>本文旨在说服读者将自己的文本编辑工具从 Vim 转到 Neovim。</p> + + + + Rust 的指针别名优化 + Sat, 19 Jan 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/csarpp-opt/ + https://blog.pg999w.top/csarpp-opt/ + <p>本文研究了基于 Rust 具有的所有权语义的一些优化。</p> + + + + 使用基于 Github issue 的留言系统 + Thu, 20 Dec 2018 00:00:00 +0000 + Unknown + https://blog.pg999w.top/gh-issue-comments/ + https://blog.pg999w.top/gh-issue-comments/ + <p>流行的博客留言系统包括 Disqus 等,但是我并没有 Disqus 帐号,也并不想注册一个。考虑到该博客的受众应该都有 Github 帐号,采用基于 Github issue 的系统应该是合适的,而且还可以享受邮件提醒等功能。我选择了 <a href="https://utteranc.es">utteranc.es</a> 的方案。</p> + + + + 为什么编程语言总是应该使用UTF-8而不是UTF-16 + Sat, 15 Dec 2018 00:00:00 +0000 + Unknown + https://blog.pg999w.top/why-utf16-sacks/ + https://blog.pg999w.top/why-utf16-sacks/ + <p>前段时间研究字符编码的时候,看到了一个<a href="https://www.zhihu.com/question/35214880">知乎问题</a>,里面的回答基本上都概念不清,事实上,Unicode “字符”、 +“字符串”、“编码”等词语涉及到非常复杂的概念。而目前介绍这个主题的中文文章似乎较为稀少,于是有了这篇文章。</p> + + + + ACM 错误集 + Thu, 20 Sep 2018 20:26:00 +0800 + Unknown + https://blog.pg999w.top/acm-error-set/ + https://blog.pg999w.top/acm-error-set/ + <p>以下是平时做题时造成不能一遍AC的原因。</p> + + + + 稻城游记 + Mon, 13 Aug 2018 00:00:00 +0000 + Unknown + https://blog.pg999w.top/daocheng-journey/ + https://blog.pg999w.top/daocheng-journey/ + <figure style="margin:0"> +<img alt="Photo of Yading" src="https://user-images.githubusercontent.com/12483662/44626880-0d964180-a957-11e8-8cc7-fae7b81e1f59.jpg"> +<figcaption>摄于2018年8月10日</figcaption> +</figure> + + + + About Me + Sun, 29 Jul 2018 00:00:00 +0000 + Unknown + https://blog.pg999w.top/about/ + https://blog.pg999w.top/about/ + <p>I'm a CS graduate student of <a href="https://www.sjtu.edu.cn/">SJTU</a>. My undergraduate degree was completed at <a href="http://www.nwpu.edu.cn">NWPU</a>.</p> + + + + 关于这个博客 + Wed, 18 Jul 2018 12:00:00 +0800 + Unknown + https://blog.pg999w.top/first-blog/ + https://blog.pg999w.top/first-blog/ + <p>除去第一个 <a href="https://blog.pg999w.top/hello/">Hello World</a> 页面,这是第一篇文章。 + + + Hello World + Wed, 18 Jul 2018 10:00:00 +0800 + Unknown + https://blog.pg999w.top/hello/ + https://blog.pg999w.top/hello/ + <p>Hello world!</p> + + + + diff --git a/rust-cpp-object-management/index.html b/rust-cpp-object-management/index.html new file mode 100644 index 0000000..710f2fe --- /dev/null +++ b/rust-cpp-object-management/index.html @@ -0,0 +1,376 @@ + + + + + + + + + + + + + + + pg999w's blog - Rust 和 C++ 的对象生命管理 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ Rust 和 C++ 的对象生命管理 +

+ +
+ +
+

Rust 和 C++ 的对象都是值语义,都采用了 RAII 惯用法。所以他们需要处理类似的对象生命周期问题:需要专门的代码来处理对象的初始化,复制和析构。下面进行一个比较,我们能够看到两种语言之间内在的对称性。

+

普通构造

+

每个对象都需要一个初始化过程,但 Rust 没有构造函数的概念。没有类继承概念的 Rust 可以利用 struct 表达式初始化每个对象。这类似于 C++ 聚合初始化

+

Rust:

+
struct S { a: i32, b: i32 }
+
+let s = S { a: 1, b: 2 };
+
+

C++:

+
struct S { int32_t a; int32_t b; };
+
+auto s = S { .a = 1, .b = 2 };
+
+

而其他形式的初始化封装就由函数来解决,而不是像 C++ 专门引入了构造函数的概念。

+

Rust:

+
impl S {
+  fn new(a: i32) -> S {
+    println!("Do other thing!")
+    S { a, b: a + 1 }
+  }
+}
+
+

C++:

+
struct S {
+  // ...
+  S(int32_t a) : a{a}, b{a + 1} {
+    std::println("Do other thing!");
+  }
+}
+
+

C++ 可以生成默认构造函数 T::T()。Rust 可以生成普通函数 fn Default::default() -> T

+

Rust:

+
#[derive(Default)]
+struct S { /*...*/ }
+
+let s = S::default();
+
+

C++:

+
struct S {
+  S() = default;
+};
+
+auto s = S{};
+
+

复制构造

+

Rust 的非 POD 对象默认不能复制。实现了 Clone trait 的对象就能复制了。当然 Clone 可以默认生成。

+

Rust:

+
#[derive(Clone)]
+struct S {}
+
+let s1 = S::new();
+s1.clone(); // copy to a temporary
+
+

C++:

+
struct S {
+   S(const S&) = default;
+};
+
+auto s1 = S{};
+auto s2 = s1; // no temporary, only s1 and s2
+
+

移动构造

+

注意到上面例子细微的对称性破缺:在 C++ 中,auto s2 = s1; 是单条初始化语句,而在 Rust 中,let s2 = s1.clone() 涉及到了两个操作:s1.clone() 复制了 s1,并返回了一个临时对象;临时对象被移动进 s2 中完成初始化。所以在上面的 Rust 例子中我只写了单个 s1.clone() 表达式。

+

这里涉及到 Rust 移动语义与 C++ 最大的不同:Rust 中所有对象都可移动,移动总是高效的,移动不会发生任何可观测的副作用。 Rust 的移动操作是默认发生的,总是可以被理解为对象的按字节浅复制。(没错,Rust 中的移动在语义上实际上是复制)

+

而 C++ 的移动,套用 Effective Modern C++, Item 29 的话来说:移动操作可能不存在,成本高,或未被使用。

+

之所以会是如此,是因为 C++ 中对象被移动后仍然可用,仍然会被调用构造函数。在 C++ 中必须要给被移动走的对象设置一个空状态。而 Rust 则没有这个问题。

+

这两个语言的移动区别如此之大,以至于本节无法给出有意义的代码对比。

+

赋值操作符

+

Rust 的赋值操作符总是移动。神奇的是,C++ 中其实有类似的对应物,那就是 by-value assignment operator。请看:

+
struct S {
+  // constructors...
+  void operator=(S rhs) { // by value
+    swap(rhs, *this);
+  }
+}
+
+S a, b;
+a = b; // stmt1
+a = std::move(b); // stmt2
+
+

这里返回值为 void 是为了和 Rust 保持对称。这里使用 copy-and-swap 的 C++ 技巧,将赋值运算符巧妙地转发到了构造函数,同时这一个赋值操作符重栽同时替代了复制和移动操作符重载。swap 一般可以由简单的 memcpy 实现。现在看看 stmt1 发生的复制和移动:

+
    +
  • b -> rhs,一次复制
  • +
  • rhs -> *this,一次浅复制
  • +
+

而 stmt2:

+
    +
  • b -> rhs,一次移动
  • +
  • rhs -> *this,一次浅复制
  • +
+

而对应的 Rust 代码:

+
a = b.clone(); // stmt1
+a = b; // stmt2
+
+

stmt1 发生了一次复制和一次移动,stmt2 发生一次移动。Rust 的移动基本相当于 C++ 的浅复制。我们可以看到对于复制操作,两种语言所具有的对称性。Rust 正是使用这种机制为所有对象实现了默认且不可重载的赋值操作。

+

析构函数

+

这一段比较平凡。用户可以提供自己的析构函数,也可以用默认的。直接看代码:

+

Rust:

+
impl Drop for S {
+  fn drop(&mut self) {
+    println!("Doing something");
+  }
+}
+
+

C++:

+
struct S {
+  // ...
+  ~S() {
+    std::println("Doing something");
+  }
+}
+
+

总结

+

总的看来,Rust 在没有历史包袱的情况下,得以采取更加简单的机制组合策略来实现同样的功能。而 C++ 则倾向于提供更高的定制性。下面是一个简单的对照表:

+ + + + + + + +
C++Rust
构造函数普通函数
默认构造Default
复制构造Clone
移动构造默认机制
赋值操作符默认机制
析构Drop
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+Rust 和 C++ 的对象生命管理Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/rust-musl-target/index.html b/rust-musl-target/index.html new file mode 100644 index 0000000..f2f16e6 --- /dev/null +++ b/rust-musl-target/index.html @@ -0,0 +1,307 @@ + + + + + + + + + + + + + + + pg999w's blog - Rust 编译到 musl target 的踩坑记录 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ Rust 编译到 musl target 的踩坑记录 +

+ +
+ +
+

Rust 在 x86_64-unknown-linux-gnu 目标下默认会动态链接到系统 C 运行时1,而不同发行版之间的 libc 可能会有兼容性问题。如果想要把一次编译好的可执行文件放到不同的 Linux 发行版上面去跑,最好采用 x86_64-unknown-linux-musl 目标进行静态编译。

+ +
+

本文使用的 Rust 版本为 1.54.0-nightly (5dc8789e3 2021-05-21)。

+
+

静态编译到 musl 的难度取决于程序是否依赖 C/C++。一般来说纯 Rust 项目 < 只依赖 C 的项目 < 依赖 C++ 的项目。其实 Rust 的交叉编译用 cross 就可以方便地完成,但是 cross 在 musl 下却不支持 C++。我之前在项目中不幸用到了依赖了 C++ 库的grpc-rs,一番折腾之后也没有能够成功编译到 musl。有没有更方便的方法编译呢?

+

这时候我看到了 Zig Makes Rust Cross-compilation Just Work 这篇文章。 +Zig 是一门尚未到达 1.0 的新语言,但是其开发者在交叉编译领域已经投入了非常多的精力。结果就是 Zig 在 12MiB 的安装包里面带了 47 个 target 的工具链,并且自带了 C/C++ 编译器。只需要安装好 Zig,就能极大简化 musl 编译。

+

设置 Zig wrapper

+

首先安装 Zig,然后在项目里面创建两个文件 musl-zccmusl-zcxx

+
$ cat musl-zcc
+#!/bin/sh
+zig cc -target x86_64-linux-musl $@
+
+$ cat musl-zcxx
+#!/bin/sh
+zig c++ -target x86_64-linux-musl $@
+
+

使用 rustup target add x86_64-unknown-linux-musl 添加 Rust 的 musl 工具链,然后进行编译:

+
CC=$PWD/musl-zcc CXX=$PWD/musl-zcxx cargo build --target x86_64-unknown-linux-musl
+
+

然后报错了:

+
   Compiling grpcio-sys v0.9.0+1.38.0
+error: failed to run custom build command for `grpcio-sys v0.9.0+1.38.0`
+[...]
+  -- The ASM compiler identification is unknown
+  -- Found assembler: [...]/musl-zcc
+  -- Warning: Did not find file Compiler/-ASM
+[...]
+  zig: error: unsupported argument '-g' to option 'Wa,'
+[...]
+  make: *** [crypto] Error 2
+  thread 'main' panicked at
+  command did not execute successfully, got: exit status: 2
+
+

原来是 CMake 没有识别汇编编译器,结果传了一个无效的参数进去。这个参数 GCC 应该是支持的,但是 Clang 则不支持。(Zig 兼容 Clang 的参数)应该是 CMake 版本太老了,从 CMake 3.10 升级到 3.20 就解决了这个问题。

+

设置 Zig 为链接器

+

我们现在已经能成功编译 grpcio-sys 了。但是光能编译还不够,链接仍然会报错,我们需要指定 Zig 为链接器。创建 .cargo/config.toml 文件再进行编译:

+
$ cat .cargo/config.toml
+[target."x86_64-unknown-linux-musl"]
+linker = "./musl-zcxx"
+
+

⋯⋯然后又报错了。

+
[...]
+ld.lld: error: duplicate symbol: _start
+>>> defined at crt1.c
+>>>            /home/user/.cache/zig/o/7206d15b47617c14656a831114cf92e7/crt1.o:(.text+0x0)
+>>> defined at rcrt1.c
+>>>            /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-musl/lib/self-contained/rcrt1.o:(.text+0x0)
+[...]
+
+

怎么同时链接了 Rust 自带的 crt 和 Zig 中的 crt 啊。尝试让 zig cc 不要链接 crt1.o,试了半天没有成功。最后发现 Rust 有一个参数可以禁用自带的 crt,于是修改 .cargo/config.toml

+
$ cat .cargo/config.toml
+[target."x86_64-unknown-linux-musl"]
+rustflags = ["-C", "linker-flavor=gcc", "-C", "link-self-contained=no"]
+linker = "./musl-zcxx"
+
+

如此终于能够编译成功。

+
$ file target/x86_64-unknown-linux-musl/debug/xxx
+target/x86_64-unknown-linux-musl/debug/xxx: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped
+
+$ ldd target/x86_64-unknown-linux-musl/debug/xxx
+        not a dynamic executable
+
+

一番折腾之后,总结下来就是添加两个 Zig wrapper,然后配置一下 Cargo config 就可以了。附加的一点就是编译工具需要支持 Clang,上面遇到的问题就是老旧的 CMake 不支持 Clang 编译汇编。总的来说,zig cc 还是很好用的。

+
+

Footnote

+
1 +

参见 Rust Reference

+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+Rust 编译到 musl target 的踩坑记录Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/site.css b/site.css new file mode 100644 index 0000000..c4a1b1f --- /dev/null +++ b/site.css @@ -0,0 +1 @@ +/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible;}pre{font-family:monospace,monospace;font-size:1em;}a{background-color:rgba(0,0,0,0);-webkit-text-decoration-skip:objects;}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted;}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em;}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0;}button,input{overflow:visible}button,select{text-transform:none}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button;}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal;}progress{display:inline-block;vertical-align:baseline;}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0;}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px;}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit;}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}html{font-size:16px;box-sizing:border-box}body{line-height:1.5;color:#34495e;background:#fefefe;border-top:3px solid #c05b4d;overflow-y:scroll}.container{width:800px;margin:0 auto;padding-bottom:4rem}@media screen and (max-width: 800px){body{border-top:0}.container{width:100%;box-shadow:-1px -5px 5px #cacaca}}a{color:#c05b4d;text-decoration:none}img{max-width:100%;height:auto;display:inline-block;vertical-align:middle}pre{display:block;padding:1rem}pre,code{font-size:90%;overflow:auto;font-family:Consolas,Monaco,Menlo,Consolas,monospace;margin-bottom:0;color:#c9d2dc}pre>code{display:block;border:none;font-style:normal;font-size:1rem}iframe{border:0}#header{padding:20px;display:flex;justify-content:space-between}#header .logo{font-family:Chancery,cursive,LiSu,sans-serif;font-weight:400}#header .logo a{color:#34495e;font-size:48px}#header .menu{align-self:flex-start;font-family:Athelas,STHeiti,Microsoft Yahei,serif}#header .menu ul{display:inline-block;padding-right:25px}#header .menu ul li{display:inline-block;margin-right:5px;display:inline-block;vertical-align:middle;transform:translateZ(0);backface-visibility:hidden;box-shadow:0 0 1px rgba(0,0,0,0);position:relative;overflow:hidden}#header .menu ul li:before{content:"";position:absolute;z-index:-1;height:2px;bottom:0;left:51%;right:51%;background:#c05b4d;transition-duration:.2s;transition-property:right,left;transition-timing-function:ease-out}#header .menu ul li.active:before,#header .menu ul li:active:before,#header .menu ul li:focus:before,#header .menu ul li:hover:before{right:0;left:0}#header .menu ul li a{color:#34495e;font-size:18px}@media screen and (max-width: 800px){#header{padding:50px 0 0;text-align:center}#header .logo,#header .menu{display:none}}main{clear:both}main .content{padding:0 20px}main .posts{margin-bottom:20px;border-bottom:1px solid #e6e6e6}main .read-more a{color:#c05b4d;font-family:Athelas,STHeiti,Microsoft Yahei,serif;font-size:1.1rem}main .read-more a:hover{text-decoration:underline}main .about,main .post{padding:1.5rem 0}main .about .post__title,main .post .post__title{font-family:Athelas,STHeiti,Microsoft Yahei,serif}main .about .post__title a,main .post .post__title a{color:#34495e}main .about h2,main .about h3,main .about h4,main .about h5,main .about h6,main .post h2,main .post h3,main .post h4,main .post h5,main .post h6{font-weight:400}main .about :not(pre)>code,main .post :not(pre)>code{padding:3px 5px;border-radius:4px;color:#c7254e;background:#f8f5ec}main .about p>a,main .post p>a{color:#c05b4d}main .about p>a:hover,main .post p>a:hover{border-bottom:1px solid #c05b4d}main .about blockquote,main .post blockquote{margin:2em 0;padding:10px 20px;position:relative;color:rgba(52,73,94,.8);background-color:rgba(192,91,77,.05);border-left:3px solid rgba(192,91,77,.3);box-shadow:1px 1px 2px rgba(0,0,0,.125)}main .about blockquote p,main .post blockquote p{margin:0}main .about table,main .post table{width:100%;max-width:100%;margin:10px 0;border-spacing:0;box-shadow:2px 2px 3px rgba(0,0,0,.125)}main .about table thead,main .post table thead{background:#f8f5ec}main .about table th,main .about table td,main .post table th,main .post table td{padding:5px 15px;border:1px double #f4efe1}main .about table tr:hover,main .post table tr:hover{background-color:#f8f5ec}main .about .footnote-definition,main .post .footnote-definition{padding-left:40px}main .about .footnote-definition:first-of-type,main .post .footnote-definition:first-of-type{border-top:2px dashed #c05b4d;margin-top:1rem;padding-top:1rem}main .about .footnote-definition sup.footnote-definition-label,main .post .footnote-definition sup.footnote-definition-label{font-size:100%;position:unset;top:unset}main .about .footnote-definition sup.footnote-definition-label::after,main .post .footnote-definition sup.footnote-definition-label::after{content:"."}main .about .footnote-definition p,main .post .footnote-definition p{display:inline}main .about__title,main .post__title{margin:0;font-size:26px;font-weight:400;display:inline-block;vertical-align:middle;transform:translateZ(0);backface-visibility:hidden;box-shadow:0 0 1px rgba(0,0,0,0);position:relative;overflow:hidden}main .about__title:before,main .post__title:before{content:"";position:absolute;z-index:-1;height:2px;bottom:0;left:51%;right:51%;background:#c05b4d;transition-duration:.2s;transition-property:right,left;transition-timing-function:ease-out}main .about__title.active:before,main .about__title:active:before,main .about__title:focus:before,main .about__title:hover:before,main .post__title.active:before,main .post__title:active:before,main .post__title:focus:before,main .post__title:hover:before{right:0;left:0}main .about__category,main .post__category{display:inline}main .about__category a,main .post__category a{color:inherit}main .about__category a:before,main .post__category a:before{content:"·";padding-right:2px}main .about__category a:hover,main .post__category a:hover{color:#c05b4d}main .about__meta,main .post__meta{font-size:14px;color:#8a8a8a}main .about__summary,main .post__summary{margin-bottom:1rem}main .post-footer{margin-top:20px;border-top:1px solid #e6e6e6}main .post-footer:after{clear:both;height:0;width:100%;content:"";display:block}main .post-tags{padding:15px 0}main .post-tags a{margin-right:5px;color:#c05b4d;word-break:break-all}main .post-nav{margin:1rem 0}main .post+.post{border-top:1px solid #e6e6e6}main .pagination{clear:both;font-family:Athelas,STHeiti,Microsoft Yahei,serif;margin:2rem 0}main .pagination a{color:#34495e}.next,.previous{font-weight:600;font-size:20px;transition:transform .3s ease-out}.next:hover,.previous:hover{color:#c05b4d}.next{float:right}.next:hover{transform:translateX(4px)}.previous{float:left}.previous:hover{transform:translateX(-4px)}.taxonomies{margin:2em 0 3em;text-align:center}.taxonomies__title{display:inline-block;font-size:18px;color:#c05b4d;border-bottom:2px solid #c05b4d}.taxonomies__items{margin:10px 0}.taxonomies__items a{margin:5px 10px;word-wrap:break-word;position:relative}.taxonomies__items a:hover{color:#c05b4d}.taxonomies__items a .count{position:relative;top:-8px;right:-2px;color:#c05b4d;font-size:12px}.taxonomy{margin:2em 0}.taxonomy__item{padding:3px 20px;border-left:1px solid #cacaca;transition:.2s ease-out}.taxonomy__item:hover{border-left:3px solid #c05b4d;transform:translateX(4px)}.taxonomy__item__time{margin-right:10px;color:#8a8a8a}.taxonomy__item__title a{color:#c05b4d}.about p>a{color:#c05b4d;word-break:break-all}.about p>a:hover{border-bottom:1px solid #c05b4d}.mobile-navbar{display:none;position:fixed;top:0;left:0;width:100%;height:50px;background:#fff;box-shadow:0px 2px 2px #cacaca;text-align:center;transition:transform 300ms ease;z-index:99}.mobile-navbar.fixed-open{transform:translate3d(180px, 0px, 0px)}.mobile-navbar .mobile-header-logo{display:inline-block;margin-right:50px}.mobile-navbar .mobile-header-logo .logo{font-size:22px;line-height:50px}.mobile-navbar .mobile-navbar-icon{cursor:pointer;color:#c05b4d;height:50px;width:50px;font-size:24px;text-align:center;float:left;position:relative;transition:background .5s}@keyframes clickfirst{0%{transform:translateY(6px) rotate(0deg)}100%{transform:translateY(0) rotate(45deg)}}@keyframes clickmid{0%{opacity:1}100%{opacity:0}}@keyframes clicklast{0%{transform:translateY(-6px) rotate(0deg)}100%{transform:translateY(0) rotate(-45deg)}}@keyframes outfirst{0%{transform:translateY(0) rotate(-45deg)}100%{transform:translateY(-6px) rotate(0deg)}}@keyframes outmid{0%{opacity:0}100%{opacity:1}}@keyframes outlast{0%{transform:translateY(0) rotate(45deg)}100%{transform:translateY(6px) rotate(0deg)}}.mobile-navbar .mobile-navbar-icon span{position:absolute;left:15px;top:25px;left:calc((100% - 20px)/2);top:calc((100% - 1px)/2);width:20px;height:1px;background-color:#c05b4d}.mobile-navbar .mobile-navbar-icon span:nth-child(1){transform:translateY(6px) rotate(0deg)}.mobile-navbar .mobile-navbar-icon span:nth-child(3){transform:translateY(-6px) rotate(0deg)}.mobile-navbar .mobile-navbar-icon.icon-click span:nth-child(1){animation-duration:.5s;animation-fill-mode:both;animation-name:clickfirst}.mobile-navbar .mobile-navbar-icon.icon-click span:nth-child(2){animation-duration:.2s;animation-fill-mode:both;animation-name:clickmid}.mobile-navbar .mobile-navbar-icon.icon-click span:nth-child(3){animation-duration:.5s;animation-fill-mode:both;animation-name:clicklast}.mobile-navbar .mobile-navbar-icon.icon-out span:nth-child(1){animation-duration:.5s;animation-fill-mode:both;animation-name:outfirst}.mobile-navbar .mobile-navbar-icon.icon-out span:nth-child(2){animation-duration:.2s;animation-fill-mode:both;animation-name:outmid}.mobile-navbar .mobile-navbar-icon.icon-out span:nth-child(3){animation-duration:.5s;animation-fill-mode:both;animation-name:outlast}.mobile-menu{background-color:rgba(248,245,236,.5)}.mobile-menu .mobile-menu-list{position:relative;list-style:none;margin-top:50px;padding:0;border-top:1px solid #f8f5ec}.mobile-menu .mobile-menu-list .mobile-menu-item{padding:10px 30px;border-bottom:1px solid #f8f5ec}.mobile-menu .mobile-menu-list a{font-size:18px}.mobile-menu .mobile-menu-list a:hover{color:#c05b4d}@media screen and (max-width: 800px){.mobile-navbar{display:block}}.slideout-menu{position:fixed;top:0;left:0px;bottom:0;width:180px;min-height:100vh;overflow-y:hidden;-webkit-overflow-scrolling:touch;z-index:0;display:none}.slideout-panel{position:relative;z-index:1;background-color:#fff}.slideout-open,.slideout-open body,.slideout-open .slideout-panel{overflow:hidden}.slideout-open .slideout-menu{display:block}.post-toc{position:absolute;width:200px;margin-left:785px;padding:10px;border-radius:5px;background:rgba(248,245,236,.6);box-shadow:1px 1px 2px rgba(0,0,0,.125);word-wrap:break-word;box-sizing:border-box;position:fixed;top:20px}.post-toc .post-toc-title{margin:0 10px;font-size:20px;font-weight:400;text-transform:uppercase}.post-toc .post-toc-content{font-size:15px}.post-toc .post-toc-content.always-active ul{display:block}.post-toc .post-toc-content>nav>ul{margin:10px 0}.post-toc .post-toc-content ul{padding-left:20px;list-style:square}.post-toc .post-toc-content ul ul{padding-left:15px;display:none}.post-toc .post-toc-content ul .has-active>ul{display:block}.post-toc .post-toc-content .toc-link.active{color:#c05b4d}@media screen and (max-width: 1185px){.post-toc{display:none}} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..523b9b4 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,245 @@ + + + + https://blog.pg999w.top/ + + + https://blog.pg999w.top/about/ + 2018-07-29 + + + https://blog.pg999w.top/acm-error-set/ + 2018-09-20T20:26:00+08:00 + + + https://blog.pg999w.top/baoyan/ + 2021-10-01 + + + https://blog.pg999w.top/c-p0709/ + 2020-04-16 + + + https://blog.pg999w.top/c-template-constraint/ + 2020-11-30 + + + https://blog.pg999w.top/constexpr-string/ + 2019-12-19 + + + https://blog.pg999w.top/csarpp-opt/ + 2019-01-19 + + + https://blog.pg999w.top/daocheng-journey/ + 2018-08-13 + + + https://blog.pg999w.top/dvorak-symphony-9/ + 2020-12-06 + + + https://blog.pg999w.top/first-blog/ + 2018-07-18T12:00:00+08:00 + + + https://blog.pg999w.top/gh-issue-comments/ + 2018-12-20 + + + https://blog.pg999w.top/hello/ + 2018-07-18T10:00:00+08:00 + + + https://blog.pg999w.top/julia-cuda/ + 2022-01-22 + + + https://blog.pg999w.top/latex-math-formula/ + 2019-08-07 + + + https://blog.pg999w.top/movie-2019/ + 2019-12-28 + + + https://blog.pg999w.top/movie-2020/ + 2021-01-04 + + + https://blog.pg999w.top/movie-2021/ + 2022-02-06 + + + https://blog.pg999w.top/movie-2023/ + 2024-01-01 + + + https://blog.pg999w.top/nanoblog/ + + + https://blog.pg999w.top/nanoblog/constexpr-string/ + 2019-12-19 + + + https://blog.pg999w.top/nanoblog/dvorak-symphony-9/ + 2020-12-06 + + + https://blog.pg999w.top/nanoblog/movie-2019/ + 2019-12-28 + + + https://blog.pg999w.top/nanoblog/movie-2020/ + 2021-01-04 + + + https://blog.pg999w.top/nanoblog/page/1/ + + + https://blog.pg999w.top/nanoblog/page/2/ + + + https://blog.pg999w.top/nanoblog/perl-hex/ + 2020-06-22 + + + https://blog.pg999w.top/nanoblog/ros-docker/ + 2020-08-02 + + + https://blog.pg999w.top/nanoblog/tikz/ + 2019-12-22 + + + https://blog.pg999w.top/nix-smartcross/ + 2022-12-11 + + + https://blog.pg999w.top/page/1/ + + + https://blog.pg999w.top/page/2/ + + + https://blog.pg999w.top/page/3/ + + + https://blog.pg999w.top/page/4/ + + + https://blog.pg999w.top/page/5/ + + + https://blog.pg999w.top/page/6/ + + + https://blog.pg999w.top/page/7/ + + + https://blog.pg999w.top/perl-hex/ + 2020-06-22 + + + https://blog.pg999w.top/perl-replace/ + 2022-01-01 + + + https://blog.pg999w.top/ros-docker/ + 2020-08-02 + + + https://blog.pg999w.top/rust-cpp-object-management/ + 2023-11-14 + + + https://blog.pg999w.top/rust-musl-target/ + 2021-05-30 + + + https://blog.pg999w.top/tags/ + + + https://blog.pg999w.top/tags/acm/ + + + https://blog.pg999w.top/tags/cpp/ + + + https://blog.pg999w.top/tags/go/ + + + https://blog.pg999w.top/tags/journey/ + + + https://blog.pg999w.top/tags/julia/ + + + https://blog.pg999w.top/tags/latex/ + + + https://blog.pg999w.top/tags/linux/ + + + https://blog.pg999w.top/tags/meta/ + + + https://blog.pg999w.top/tags/movie/ + + + https://blog.pg999w.top/tags/music/ + + + https://blog.pg999w.top/tags/nix/ + + + https://blog.pg999w.top/tags/perl/ + + + https://blog.pg999w.top/tags/programming/ + + + https://blog.pg999w.top/tags/ros/ + + + https://blog.pg999w.top/tags/rust/ + + + https://blog.pg999w.top/tags/typography/ + + + https://blog.pg999w.top/tags/typst/ + + + https://blog.pg999w.top/tags/wikipedia/ + + + https://blog.pg999w.top/tikz/ + 2019-12-22 + + + https://blog.pg999w.top/too-many-channels/ + 2022-03-24 + + + https://blog.pg999w.top/typst-clreq/ + 2023-07-22 + + + https://blog.pg999w.top/typst-latin-modern/ + 2024-01-24 + + + https://blog.pg999w.top/why-utf16-sacks/ + 2018-12-15 + + + https://blog.pg999w.top/wiki-exemption/ + 2023-01-27 + + + https://blog.pg999w.top/you-should-use-neovim/ + 2019-04-19 + + diff --git a/tags/acm/index.html b/tags/acm/index.html new file mode 100644 index 0000000..b4b856c --- /dev/null +++ b/tags/acm/index.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

acm

+ +
+ 2018-09-20 + + ACM 错误集 + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/acm/rss.xml b/tags/acm/rss.xml new file mode 100644 index 0000000..33fb167 --- /dev/null +++ b/tags/acm/rss.xml @@ -0,0 +1,21 @@ + + + + pg999w's blog - acm + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Thu, 20 Sep 2018 20:26:00 +0800 + + ACM 错误集 + Thu, 20 Sep 2018 20:26:00 +0800 + Unknown + https://blog.pg999w.top/acm-error-set/ + https://blog.pg999w.top/acm-error-set/ + <p>以下是平时做题时造成不能一遍AC的原因。</p> + + + + diff --git a/tags/cpp/index.html b/tags/cpp/index.html new file mode 100644 index 0000000..39deb4f --- /dev/null +++ b/tags/cpp/index.html @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

cpp

+ +
+ 2023-11-14 + + Rust 和 C++ 的对象生命管理 + +
+ + + + + +
+ 2020-04-16 + + 从 C++ 的错误处理说起 + +
+ +
+ 2019-12-19 + + constexpr string + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/cpp/rss.xml b/tags/cpp/rss.xml new file mode 100644 index 0000000..b55427f --- /dev/null +++ b/tags/cpp/rss.xml @@ -0,0 +1,110 @@ + + + + pg999w's blog - cpp + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Tue, 14 Nov 2023 00:00:00 +0000 + + Rust 和 C++ 的对象生命管理 + Tue, 14 Nov 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/rust-cpp-object-management/ + https://blog.pg999w.top/rust-cpp-object-management/ + <p>Rust 和 C++ 的对象都是值语义,都采用了 RAII 惯用法。所以他们需要处理类似的对象生命周期问题:需要专门的代码来处理对象的初始化,复制和析构。下面进行一个比较,我们能够看到两种语言之间内在的对称性。</p> + + + + C++ 每三年才解决一点点问题 + Mon, 30 Nov 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/c-template-constraint/ + https://blog.pg999w.top/c-template-constraint/ + <p>或:怎样优雅地给 C++ 模板添加约束?</p> + + + + Docker 搭建 RoboMaster RoboRTS 框架构建环境 + Sun, 02 Aug 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/ros-docker/ + https://blog.pg999w.top/ros-docker/ + <p><a href="https://github.com/RoboMaster/RoboRTS">RoboRTS</a> 框架用于大疆的 RoboMaster ICRA 人工智能挑战赛。其构建环境基于 <a href="https://www.ros.org/">ROS</a>,在非 Ubuntu/CentOS 的 Linux 机器上面安装较为困难。于是我们采用基于 docker 的构建方案。</p> + + + + 从 C++ 的错误处理说起 + Thu, 16 Apr 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/c-p0709/ + https://blog.pg999w.top/c-p0709/ + <p><strong>错误处理</strong>是一个非常重要的软件工程问题。对软件中出现的非致命错误的不当处理,是几乎所有的灾难性系统故障的诱因。 编程语言往往需要提供一些用于错误处理的语言设施,这些设施反过来会影响项目中错误处理的方式。不同的语言错误处理方式不同。例如 Java 采用基于 try-throw-catch 语法的异常机制,而 Go 语言则选择手动检测函数返回的 <code>error</code> 对象。一个令人惊讶的事实是,C++ 到现在还没有一个被广泛接受的错误处理方式。</p> + + + + constexpr string + Thu, 19 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/constexpr-string/ + https://blog.pg999w.top/constexpr-string/ + <p>按照 <a href="https://akrzemi1.wordpress.com/2017/06/28/compile-time-string-concatenation/">Andrzej's C++ blog</a> 里这篇文章的思路,我实现了一个编译期的字符串拼接:</p> +<pre data-lang="c++" style="background-color:#eff1f5;color:#4f5b66;" class="language-c++ "><code class="language-c++" data-lang="c++"><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#b48ead;">class </span><span style="color:#d08770;">sstring </span><span style="color:#343d46;">{ +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">char</span><span style="color:#343d46;"> inner[N]; +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">() </span><span>= </span><span style="color:#b48ead;">default</span><span style="color:#343d46;">; +</span><span style="color:#343d46;"> +</span><span style="color:#b48ead;">public</span><span style="color:#343d46;">: +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">const char </span><span style="color:#343d46;">(</span><span>&amp;</span><span style="color:#343d46;">s)[N]) : </span><span style="color:#bf616a;">inner</span><span style="color:#343d46;">{} { +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> N; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i] </span><span>=</span><span style="color:#343d46;"> s[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">template</span><span style="color:#343d46;">&lt;</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> M&gt; +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">const</span><span style="color:#343d46;"> sstring&lt;M&gt; </span><span>&amp;</span><span style="color:#bf616a;">lhs</span><span style="color:#343d46;">, </span><span style="color:#b48ead;">const</span><span style="color:#343d46;"> sstring&lt;N </span><span>-</span><span style="color:#343d46;"> M&gt; </span><span>&amp;</span><span style="color:#bf616a;">rhs</span><span style="color:#343d46;">) : </span><span style="color:#bf616a;">inner</span><span style="color:#343d46;">{} { +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> M; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i] </span><span>=</span><span style="color:#343d46;"> lhs[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> N </span><span>-</span><span style="color:#343d46;"> M; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i </span><span>+</span><span style="color:#343d46;"> N] </span><span>=</span><span style="color:#343d46;"> rhs[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr char </span><span style="color:#8fa1b3;">operator[]</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int </span><span style="color:#bf616a;">i</span><span style="color:#343d46;">) </span><span style="color:#b48ead;">const </span><span style="color:#343d46;">{ +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">return</span><span style="color:#343d46;"> inner[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;">}</span><span>; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#bf616a;">sstring</span><span>(</span><span style="color:#b48ead;">char </span><span>(&amp;s)[N]) -&gt; </span><span style="color:#bf616a;">sstring</span><span>&lt;N&gt;; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> M, </span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#bf616a;">sstring</span><span>(</span><span style="color:#b48ead;">const</span><span> sstring&lt;M&gt; &amp;lhs, </span><span style="color:#b48ead;">const</span><span> sstring&lt;N&gt; &amp;rhs) -&gt; </span><span style="color:#bf616a;">sstring</span><span>&lt;N + M&gt;; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> M, </span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#b48ead;">constexpr auto </span><span style="color:#8fa1b3;">operator+</span><span>(</span><span style="color:#b48ead;">const</span><span> sstring&lt;M&gt; &amp;</span><span style="color:#bf616a;">lhs</span><span>, </span><span style="color:#b48ead;">const</span><span> sstring&lt;N&gt; &amp;</span><span style="color:#bf616a;">rhs</span><span>) { +</span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">sstring</span><span>(lhs, rhs); +</span><span>} +</span><span> +</span><span style="color:#b48ead;">constexpr</span><span> sstring s {&quot;</span><span style="color:#a3be8c;">123</span><span>&quot;}; +</span><span style="color:#b48ead;">constexpr</span><span> sstring q {&quot;</span><span style="color:#a3be8c;">456</span><span>&quot;}; +</span><span style="color:#b48ead;">constexpr</span><span> sstring r {s + q}; +</span></code></pre> +<ul> +<li><code>constexpr string</code> 有什么用?这至少在初始化全局静态变量时有用。<code>constexpr</code> 静态变量不会存在烦人的初始化顺序问题。</li> +<li>因为用到了 deducing guide,所以至少需要在 C++ 17 下编译。</li> +<li><code>std::string</code> 将在 C++ 20 支持 <code>constexpr</code>,不过编译器全部普及这个特性可能还要等好几年。 +<ul> +<li>C++ 现在有一种「<code>constexpr</code> Everything」的倾向。这是为了更好的实现元编程。这是好事。</li> +</ul> +</li> +<li><code>constexpr</code> 构造函数要求初始化每个每个子对象和非静态数据成员必须被初始化。奇怪的是,clang 可以通过未初始化的代码。</li> +</ul> + + + + diff --git a/tags/go/index.html b/tags/go/index.html new file mode 100644 index 0000000..9982d83 --- /dev/null +++ b/tags/go/index.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

go

+ + + +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/go/rss.xml b/tags/go/rss.xml new file mode 100644 index 0000000..0f9dfb9 --- /dev/null +++ b/tags/go/rss.xml @@ -0,0 +1,30 @@ + + + + pg999w's blog - go + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Thu, 24 Mar 2022 00:00:00 +0000 + + Too many channels in Rust but only one in Go + Thu, 24 Mar 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/too-many-channels/ + https://blog.pg999w.top/too-many-channels/ + <style type="text/css"> +em, strong { +color: purple; +} +img { +display: block; +margin: 0 auto; +} +</style> +<p>Channel 是异步编程 CSP 模型和 Actor 模型的重要组成部分,是一种用于消息同步的数据结构。Go 语言中的 <code>chan</code> 类型即是一种 channel 的实现。在使用 Rust 进行异步编程的时候也需要使用 channel。然而 Rust 中的 channel 似乎太多了。</p> + + + + diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..5ec2eb5 --- /dev/null +++ b/tags/index.html @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+ +
+ + + +
+ + + + + + + diff --git a/tags/journey/index.html b/tags/journey/index.html new file mode 100644 index 0000000..6b3a9bf --- /dev/null +++ b/tags/journey/index.html @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

journey

+ +
+ 2021-10-01 + + 保研经历总结 + +
+ +
+ 2018-08-13 + + 稻城游记 + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/journey/rss.xml b/tags/journey/rss.xml new file mode 100644 index 0000000..29781bc --- /dev/null +++ b/tags/journey/rss.xml @@ -0,0 +1,33 @@ + + + + pg999w's blog - journey + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Fri, 01 Oct 2021 00:00:00 +0000 + + 保研经历总结 + Fri, 01 Oct 2021 00:00:00 +0000 + Unknown + https://blog.pg999w.top/baoyan/ + https://blog.pg999w.top/baoyan/ + <p>2020年保研算是告一段落了。因为基地里面要组织给学弟学妹的分享活动,借此机会正好来写一篇总结。</p> + + + + 稻城游记 + Mon, 13 Aug 2018 00:00:00 +0000 + Unknown + https://blog.pg999w.top/daocheng-journey/ + https://blog.pg999w.top/daocheng-journey/ + <figure style="margin:0"> +<img alt="Photo of Yading" src="https://user-images.githubusercontent.com/12483662/44626880-0d964180-a957-11e8-8cc7-fae7b81e1f59.jpg"> +<figcaption>摄于2018年8月10日</figcaption> +</figure> + + + + diff --git a/tags/julia/index.html b/tags/julia/index.html new file mode 100644 index 0000000..7afcfef --- /dev/null +++ b/tags/julia/index.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

julia

+ +
+ 2022-01-22 + + 用 Julia 编写 CUDA 程序 + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/julia/rss.xml b/tags/julia/rss.xml new file mode 100644 index 0000000..877acd6 --- /dev/null +++ b/tags/julia/rss.xml @@ -0,0 +1,21 @@ + + + + pg999w's blog - julia + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Sat, 22 Jan 2022 00:00:00 +0000 + + 用 Julia 编写 CUDA 程序 + Sat, 22 Jan 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/julia-cuda/ + https://blog.pg999w.top/julia-cuda/ + <p>CUDA 本身是一个 C 库,而 CUDA kernel 则需要使用扩展的 C/C++ 语法。但 <a href="https://cuda.juliagpu.org/stable/">CUDA.jl</a> 让 Julia CUDA 编程成为可能。然而虽然 CUDA.jl 实现了绝大多数 CUDA 的功能,但其文档仍很不完善。本文补充了一些常见 CUDA 功能在 Julia 中的写法。本文假设读者预先具有 Julia,CUDA,以及 CUDA.jl 的基本知识。</p> + + + + diff --git a/tags/latex/index.html b/tags/latex/index.html new file mode 100644 index 0000000..ca48cc5 --- /dev/null +++ b/tags/latex/index.html @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

latex

+ +
+ 2019-12-22 + + Inverse clip in TikZ + +
+ +
+ 2019-08-07 + + \(\LaTeX\) 公式 + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/latex/rss.xml b/tags/latex/rss.xml new file mode 100644 index 0000000..e7020a6 --- /dev/null +++ b/tags/latex/rss.xml @@ -0,0 +1,61 @@ + + + + pg999w's blog - latex + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Sun, 22 Dec 2019 00:00:00 +0000 + + Inverse clip in TikZ + Sun, 22 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/tikz/ + https://blog.pg999w.top/tikz/ + <p><a href="https://en.wikipedia.org/wiki/PGF/TikZ">Ti<em>k</em>Z</a> 虽然强大,但是也过于复杂。下面尝试绘制下面的图形。</p> +<p><img src="../tikz.svg" alt="ellipse" /></p> +<p>首先绘制一个直立的图形,再全局旋转。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#b48ead;">\begin</span><span>{tikzpicture}[</span><span style="color:#bf616a;">rotate</span><span>=20,even odd rule] +</span></code></pre> +<p>绘制实线部分,一个椭圆,一个半椭圆和两条直线。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#96b5b4;">\coordinate</span><span> (move) at (0,3); +</span><span style="color:#96b5b4;">\draw</span><span> (move) ellipse [x radius=1,y radius=0.4]; +</span><span style="color:#96b5b4;">\draw</span><span> (-1,0) -- +(move); +</span><span style="color:#96b5b4;">\draw</span><span> (1,0) -- +(move); +</span><span style="color:#96b5b4;">\draw</span><span> (1,0) arc (0:-180:1 and 0.4); +</span><span style="color:#96b5b4;">\draw</span><span>[dashed] (1,0) arc (0:180:1 and 0.4); +</span></code></pre> +<p>接下来绘制带有遮挡关系的直线。这里需要使用一个辅助的样式 <code>invclip</code>,用于产生「剪除」的效果。<code>clip</code> 相当于一个蒙板,加上 <code>invclip</code> 后变为一个反向蒙板。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#96b5b4;">\tikzset</span><span>{ +</span><span> invclip/.style={ +</span><span> insert path={ (-3,-2) -- (-3,5) -- (3,5) -- (3,-2) -- (-3,-2) } +</span><span> } +</span><span>} +</span></code></pre> +<p><code>\foreach</code> 只运行两次。针对同一条直线,第一次 <code>\sty</code> 为 <code>dashed</code>,<code>\inv</code> 为 <code>{}</code>;第二次 <code>\sty</code> 为 <code>{}</code>,<code>\inv</code> 为 <code>invclip</code>。</p> +<pre data-lang="tex" style="background-color:#eff1f5;color:#4f5b66;" class="language-tex "><code class="language-tex" data-lang="tex"><span style="color:#96b5b4;">\foreach \sty</span><span>/</span><span style="color:#96b5b4;">\inv</span><span> in {dashed/{},{}/invclip} { +</span><span> </span><span style="color:#b48ead;">\begin</span><span>{scope} +</span><span> </span><span style="color:#96b5b4;">\path</span><span>[clip,</span><span style="color:#96b5b4;">\inv</span><span>] +</span><span> (-1,0) +</span><span> -- +(move) -- ($</span><span style="color:#d08770;">(</span><span style="color:#bf616a;">move</span><span style="color:#d08770;">)</span><span>+</span><span style="color:#d08770;">(1</span><span>,</span><span style="color:#d08770;">0)</span><span>$) +</span><span> -- (1,0) arc (0:-180:1 and 0.4); +</span><span> </span><span style="color:#96b5b4;">\draw</span><span>[</span><span style="color:#96b5b4;">\sty</span><span>,thick] (0,4) -- (0,-1); +</span><span> </span><span style="color:#b48ead;">\end</span><span>{scope} +</span><span>} +</span><span style="color:#b48ead;">\end</span><span>{tikzpicture} +</span></code></pre> + + + + \(\LaTeX\) 公式 + Wed, 07 Aug 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/latex-math-formula/ + https://blog.pg999w.top/latex-math-formula/ + <p>\(\LaTeX\) 是一款非常优秀的文档准备系统,它强大的数学排版功能举世闻名。由于 <a href="https://www.mathjax.org/">Mathjax</a> 的广泛采用,\(\LaTeX\) 数学公式也成为了 Web 技术上数学公式排版的事实标准。但 \(\LaTeX\) 的学习曲线陡峭,基本的命令难以轻松应对实际写作中遇到的复杂公式。本文选取并实现了 <a href="https://ctan.org/pkg/texbook">\(\textrm{The \TeX{}book}\)</a> 第 18 章末尾提供的 20 个 Chanllenge。以期为想要深入学习 \(\LaTeX\) 公式排版的读者提供参考。</p> + + + + diff --git a/tags/linux/index.html b/tags/linux/index.html new file mode 100644 index 0000000..18a9a5f --- /dev/null +++ b/tags/linux/index.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

linux

+ +
+ 2019-04-19 + + 你应该使用 Neovim + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/linux/rss.xml b/tags/linux/rss.xml new file mode 100644 index 0000000..65a7547 --- /dev/null +++ b/tags/linux/rss.xml @@ -0,0 +1,21 @@ + + + + pg999w's blog - linux + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Fri, 19 Apr 2019 00:00:00 +0000 + + 你应该使用 Neovim + Fri, 19 Apr 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/you-should-use-neovim/ + https://blog.pg999w.top/you-should-use-neovim/ + <p>本文旨在说服读者将自己的文本编辑工具从 Vim 转到 Neovim。</p> + + + + diff --git a/tags/meta/index.html b/tags/meta/index.html new file mode 100644 index 0000000..89e4890 --- /dev/null +++ b/tags/meta/index.html @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

meta

+ + + +
+ 2018-07-29 + + About Me + +
+ +
+ 2018-07-18 + + 关于这个博客 + +
+ +
+ 2018-07-18 + + Hello World + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/meta/rss.xml b/tags/meta/rss.xml new file mode 100644 index 0000000..592c425 --- /dev/null +++ b/tags/meta/rss.xml @@ -0,0 +1,47 @@ + + + + pg999w's blog - meta + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Thu, 20 Dec 2018 00:00:00 +0000 + + 使用基于 Github issue 的留言系统 + Thu, 20 Dec 2018 00:00:00 +0000 + Unknown + https://blog.pg999w.top/gh-issue-comments/ + https://blog.pg999w.top/gh-issue-comments/ + <p>流行的博客留言系统包括 Disqus 等,但是我并没有 Disqus 帐号,也并不想注册一个。考虑到该博客的受众应该都有 Github 帐号,采用基于 Github issue 的系统应该是合适的,而且还可以享受邮件提醒等功能。我选择了 <a href="https://utteranc.es">utteranc.es</a> 的方案。</p> + + + + About Me + Sun, 29 Jul 2018 00:00:00 +0000 + Unknown + https://blog.pg999w.top/about/ + https://blog.pg999w.top/about/ + <p>I'm a CS graduate student of <a href="https://www.sjtu.edu.cn/">SJTU</a>. My undergraduate degree was completed at <a href="http://www.nwpu.edu.cn">NWPU</a>.</p> + + + + 关于这个博客 + Wed, 18 Jul 2018 12:00:00 +0800 + Unknown + https://blog.pg999w.top/first-blog/ + https://blog.pg999w.top/first-blog/ + <p>除去第一个 <a href="https://blog.pg999w.top/hello/">Hello World</a> 页面,这是第一篇文章。 + + + Hello World + Wed, 18 Jul 2018 10:00:00 +0800 + Unknown + https://blog.pg999w.top/hello/ + https://blog.pg999w.top/hello/ + <p>Hello world!</p> + + + + diff --git a/tags/movie/index.html b/tags/movie/index.html new file mode 100644 index 0000000..09ed65b --- /dev/null +++ b/tags/movie/index.html @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

movie

+ +
+ 2024-01-01 + + 2023 个人年度电影 + +
+ +
+ 2022-02-06 + + 2021 个人年度电影 + +
+ +
+ 2021-01-04 + + 2020 个人年度电影 + +
+ +
+ 2019-12-28 + + 2019 个人年度电影 + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/movie/rss.xml b/tags/movie/rss.xml new file mode 100644 index 0000000..925d77b --- /dev/null +++ b/tags/movie/rss.xml @@ -0,0 +1,140 @@ + + + + pg999w's blog - movie + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Mon, 01 Jan 2024 00:00:00 +0000 + + 2023 个人年度电影 + Mon, 01 Jan 2024 00:00:00 +0000 + Unknown + https://blog.pg999w.top/movie-2023/ + https://blog.pg999w.top/movie-2023/ + <p>2022 年由于疫情和学业的各种原因,我观看的电影实在太少,以至于放弃了当年的年度电影评选。2023 年,在完全恢复了正常的生活秩序后,观影频次有所提高。本年我一共看了 40 部电影,其中有 9 部是动画片。</p> + + + + 2021 个人年度电影 + Sun, 06 Feb 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/movie-2021/ + https://blog.pg999w.top/movie-2021/ + <p>今年的年度电影规则和去年一样。虽然名单在元旦时就确定了,可是由于各种原因,这篇博客文章拖到了春节最后一天才写出来。值得一提的是所有入选电影全部是 2021 年新片。一方面当然是我接触新片更频繁了,另一方面也从侧面看出业界确实在复苏。</p> +<h2 id="2021-nian-du-dian-ying">2021 年度电影</h2> +<ul> +<li> +<p><strong>rank 1</strong> 《沙丘(Dune)》2021 丹尼斯·维伦纽瓦</p> +<p>如果说2021年还有什么电影值得人们非进电影院不可,那首先应当是《沙丘》。如果2021年还有什么在电影体现了一流的工业水准,那一定有《沙丘》。如果2021年有什么值得一提的科幻片,那只能是《沙丘》。</p> +</li> +<li> +<p><strong>rank 2</strong> 《花束般的恋爱(花束みたいな恋をした)》2021 土井裕泰</p> +<p>我们和剧中的主人公的境况当然千差万别,但是步入社会的大学生,为了现实的压力而终于放弃了曾经热爱的东西——这种恐惧确实传达给我了。</p> +</li> +<li> +<p><strong>rank 3</strong> 《酷爱电影的庞波小姐(映画大好きポンポさん)》2021 平尾隆之</p> +<p>我关注的影评人给这部电影打了不少低分。可是在我这里,尽管剧情确实有值得严肃的影评人诟病之处,但形式上的趣味压倒了一切。这部动画里一切外在的形式,包括突出的剪辑点,反复的插叙,以及精确控制的时间,无不体现了导演「剪辑使电影变得有趣」的理念。</p> +</li> +</ul> +<h2 id="2021-nian-du-yuan-sheng-yin-le">2021 年度原声音乐</h2> +<ul> +<li> +<p>《倒数时刻(Tick, Tick… boom!)》2021 艺术家:Janason Larson</p> +<p>选择音乐剧改编电影似乎有点作弊。不过就原作中的几首经典歌曲来说,比起原版的音乐剧,我更喜欢这张原声带的版本。</p> +</li> +</ul> +<h2 id="wang-nian-nian-du-dian-ying">往年年度电影</h2> +<ul> +<li><a href="https://blog.pg999w.top/nanoblog/movie-2020/">2020 个人年度电影</a></li> +<li><a href="https://blog.pg999w.top/nanoblog/movie-2019/">2019 个人年度电影</a></li> +</ul> + + + + 2020 个人年度电影 + Mon, 04 Jan 2021 00:00:00 +0000 + Unknown + https://blog.pg999w.top/movie-2020/ + https://blog.pg999w.top/movie-2020/ + <h2 id="gui-ze">规则</h2> +<p>今年是第二年自娱自乐的评选了。同样基本按照去年的规则来,但是稍有修订。入选的电影满足如下条件:</p> +<ol> +<li>是本年新观看的电影。</li> +<li>包含观看的资源片,不限于院线片。</li> +<li>仅以个人喜好程度排序。</li> +</ol> +<p>另设「年度原声音乐」一个名额,范围是今年听到的电影原声带专辑。</p> +<h2 id="2020-nian-du-dian-ying">2020 年度电影</h2> +<p>今年首次出现了院线片。第三名本来是波兰斯基的《我控诉(J'accuse)》,可是2020年的最后一天在影院看了《心灵奇旅》,大概是受了氛围的影响,我临时改变了主意。</p> +<p>本次入围前三名的有两部动画电影。但是令人担忧的是,高产优质作品的京都动画受火灾的影响,前途未卜;而皮克斯在当下流量为王的流媒体时代,已经开始受到资本的裹挟。在动画电影领域,2020年会成为接下来十年中最好的一年吗?我无法乐观地给出否定的回答。</p> +<ul> +<li> +<p><strong>rank 1</strong> 《利兹与青鸟(リズと青い鳥)》2018 山田尚子</p> +<p>这是我近年来看过的电影感和作者风格最强的长篇动画电影。影片的整个核心故事都在学校教学楼这个封闭空间内完成。然而在这个受限的空间中,从与脚步声配合的配乐,到精心安排的背景摆件,到处充满着细节。山田尚子在九十分钟的时间内,尽情地使用视听语言,探索到了人物的内心最深之处。</p> +</li> +<li> +<p><strong>rank 2</strong> 《爆裂鼓手(Whiplash)》2014 达米恩·查泽雷</p> +<p>我听说真正的爵士乐迷并不看好这个扭曲了爵士乐精神的片子。但是对于我这样不通爵士乐的人来说,那种癫狂的戏剧张力,反高潮的情节转折,使这部电影充满了力气。</p> +</li> +<li> +<p><strong>rank 3</strong> 《心灵奇旅(Soul)》2020 彼特·道格特</p> +<p>也许皮克斯已经不是曾经的那个皮克斯了,但是非凡的想象力与一流的动画工业的结合,至少现在还没有丢失。从生之彼岸的桥上面下坠的画面,久违地给人以极大的愉悦。</p> +</li> +</ul> +<h2 id="2020-nian-du-yuan-sheng-yin-le">2020 年度原声音乐</h2> +<ul> +<li> +<p>《「girls,dance,staircase」(电影『利兹与青鸟』原声带)》2019 艺术家:牛尾憲輔/松田彬人</p> +<p>牛尾憲輔自然是延续一直以来的配乐风格,而这次松田彬人为配合电影专门按古典交响曲格式创作的《利兹与青鸟》 +完整的四乐章,实在难得。</p> +</li> +</ul> +<h2 id="wang-nian-nian-du-dian-ying">往年年度电影</h2> +<ul> +<li><a href="https://blog.pg999w.top/nanoblog/movie-2019/">2019 个人年度电影</a></li> +</ul> + + + + 2019 个人年度电影 + Sat, 28 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/movie-2019/ + https://blog.pg999w.top/movie-2019/ + <h2 id="gui-ze">规则</h2> +<p>虽然是一个自娱自乐的东西,但是不以规矩,不成方圆,入选的电影满足如下条件:</p> +<ol> +<li>是本年新观看的电影。</li> +<li>包含观看的电影资源,不限于院线片。</li> +<li>仅以个人喜好程度排序,不单独考虑艺术价值,社会影响等因素。</li> +</ol> +<h2 id="2019-nian-du-dian-ying">2019 年度电影</h2> +<p>遗憾的是,我选出的三部电影全部是非院线片,这意味着这些电影基本都是在较差的观影条件下观看的。</p> +<ul> +<li> +<p><strong>rank 1</strong> 《寄生虫(기생충)》2019 奉俊昊</p> +<p>最为传统的剧作结构,达到了最好的效果。这部电影给了我长久以来都没有过的,被情节抓住的体验。</p> +</li> +<li> +<p><strong>rank 2</strong> 《宣告黎明的露之歌(夜明け告げるルーのうた)》2017 汤浅政明</p> +<p>没有什么特殊的理由,这部电影和我产生了某种共振。汤浅政明创造的这个异国故事,包含着一种奇妙的气氛,确实触动了我心中某个部分。</p> +</li> +<li> +<p><strong>rank 3</strong> 《登月第一人(First Man)》2018 达米恩·查泽雷</p> +<p>许多电影里都有太空飞船,但是这一部真实地展现了那种天空深处的危机四伏。舱内狭隘的视角,剧烈的抖动,死亡与濒死体验,都无比真实。</p> +</li> +</ul> +<h2 id="2019-nian-du-yuan-sheng-yin-le">2019 年度原声音乐</h2> +<ul> +<li> +<p>《冰雪奇缘2(Frozen II)》2019 艺术家:Robert Lopez/Kristen Anderson Lopez</p> +<p>电影没得洗,确实是迪士尼缺乏诚意的制作,但是原声音乐仍然是高水准。比起第一部,这部更加戏剧化的音乐我反而更喜欢。</p> +</li> +</ul> + + + + diff --git a/tags/music/index.html b/tags/music/index.html new file mode 100644 index 0000000..bab7fd4 --- /dev/null +++ b/tags/music/index.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+ +
+ + + +
+ + + + + + + diff --git a/tags/music/rss.xml b/tags/music/rss.xml new file mode 100644 index 0000000..da74555 --- /dev/null +++ b/tags/music/rss.xml @@ -0,0 +1,21 @@ + + + + pg999w's blog - music + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Sun, 06 Dec 2020 00:00:00 +0000 + + 赏析:德沃夏克,第九交响曲“自新世界”,作品95号 + Sun, 06 Dec 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/dvorak-symphony-9/ + https://blog.pg999w.top/dvorak-symphony-9/ + <p><strong>第二乐章 Largo “广板”,D♭大调</strong></p> + + + + diff --git a/tags/nix/index.html b/tags/nix/index.html new file mode 100644 index 0000000..5f09c52 --- /dev/null +++ b/tags/nix/index.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

nix

+ + + +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/nix/rss.xml b/tags/nix/rss.xml new file mode 100644 index 0000000..c923ced --- /dev/null +++ b/tags/nix/rss.xml @@ -0,0 +1,21 @@ + + + + pg999w's blog - nix + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Sun, 11 Dec 2022 00:00:00 +0000 + + 用 Nix 管理交叉编译 Rust 项目的环境 + Sun, 11 Dec 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nix-smartcross/ + https://blog.pg999w.top/nix-smartcross/ + <p>SmartCross 项目的介绍见<a href="https://blog.t123yh.xyz:2/index.php/archives/1077">这里</a>。其中的控制器组件用 Rust 写成,需要编译到 aarch64 平台。我尝试写了一个 <a href="https://nixos.org/">Nix</a> 表达式来管理该项目的环境。</p> + + + + diff --git a/tags/perl/index.html b/tags/perl/index.html new file mode 100644 index 0000000..fd4678c --- /dev/null +++ b/tags/perl/index.html @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

perl

+ +
+ 2022-01-01 + + 用 Perl 做查找替换 + +
+ +
+ 2020-06-22 + + 用 Perl 进制转换 + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/perl/rss.xml b/tags/perl/rss.xml new file mode 100644 index 0000000..278886d --- /dev/null +++ b/tags/perl/rss.xml @@ -0,0 +1,42 @@ + + + + pg999w's blog - perl + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Sat, 01 Jan 2022 00:00:00 +0000 + + 用 Perl 做查找替换 + Sat, 01 Jan 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/perl-replace/ + https://blog.pg999w.top/perl-replace/ + <p>现在需要把一篇文章中两个中文字符中的回车给删掉。这时候需要用到支持 Unicode 的正则表达式。这时候我们还是用最强大的字符处理语言 Perl 来搞。命令如下:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -CSAD -0p -i</span><span>.bak</span><span style="color:#bf616a;"> -e </span><span>&#39;</span><span style="color:#a3be8c;">s/(\p{category=Po}|\p{sc=Han})\n *(\p{sc=Han})/$1$2/gms</span><span>&#39; file.md +</span></code></pre> +<p>如果不涉及 Unicode 处理,可以不用加 <code>-CSAD</code>,如果处理不跨行,可以不用 <code>-0</code>,如果不需要备份文件,可以删去 <code>.bak</code>。</p> + + + + 用 Perl 进制转换 + Mon, 22 Jun 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/perl-hex/ + https://blog.pg999w.top/perl-hex/ + <p>现在需要把一堆十进制数转换为二进制数,在 Vim 里可以用 <code>:'&lt;,'&gt;!command</code> 来做转换。但是我发现常见的行处理程序 <code>awk</code> 根本搞不定进制转换,所以还是用最强大的字符处理语言 Perl 来搞。命令如下:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ne </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%032b\n&quot;, $_)</span><span>&#39; +</span></code></pre> +<p>如果输入是十六进制呢?加个 <code>hex</code> 函数就行:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ne </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%032b\n&quot;, hex($_))</span><span>&#39; +</span></code></pre> +<p>如果一行有多个字段,只想转换第二个呢?这就要用到 <code>-a</code> 参数,此时 <code>$_</code> 变为数组 <code>@F</code>:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ane </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%s\t%032b\n&quot;, @F[0], hex(@F[1]))</span><span>&#39; +</span></code></pre> +<p>最后注意在 Vim 中 <code>%</code> 符号需要转义。</p> + + + + diff --git a/tags/programming/index.html b/tags/programming/index.html new file mode 100644 index 0000000..564770a --- /dev/null +++ b/tags/programming/index.html @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

programming

+ +
+ 2023-11-14 + + Rust 和 C++ 的对象生命管理 + +
+ +
+ 2023-07-22 + + 为 Typst 添加中文排版支持 + +
+ + + +
+ 2022-01-22 + + 用 Julia 编写 CUDA 程序 + +
+ +
+ 2022-01-01 + + 用 Perl 做查找替换 + +
+ + + + + + + +
+ 2020-06-22 + + 用 Perl 进制转换 + +
+ +
+ 2020-04-16 + + 从 C++ 的错误处理说起 + +
+ +
+ 2019-12-19 + + constexpr string + +
+ +
+ 2019-01-19 + + Rust 的指针别名优化 + +
+ + + +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/programming/rss.xml b/tags/programming/rss.xml new file mode 100644 index 0000000..494d290 --- /dev/null +++ b/tags/programming/rss.xml @@ -0,0 +1,211 @@ + + + + pg999w's blog - programming + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Tue, 14 Nov 2023 00:00:00 +0000 + + Rust 和 C++ 的对象生命管理 + Tue, 14 Nov 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/rust-cpp-object-management/ + https://blog.pg999w.top/rust-cpp-object-management/ + <p>Rust 和 C++ 的对象都是值语义,都采用了 RAII 惯用法。所以他们需要处理类似的对象生命周期问题:需要专门的代码来处理对象的初始化,复制和析构。下面进行一个比较,我们能够看到两种语言之间内在的对称性。</p> + + + + 为 Typst 添加中文排版支持 + Sat, 22 Jul 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/typst-clreq/ + https://blog.pg999w.top/typst-clreq/ + <style> +img { +max-width: 300px; +display: block; +margin: auto; +} +</style> +<p>Typst 是一个 2023 年初开源的一个排版软件。类似于 LaTeX,它通过纯文本编写源代码,然后通过编译器将源代码转换为排版好的 PDF 文件。虽然目前 Typst 的生态还不如 LaTeX,但是比起 LaTeX,它有一些明显的优势:</p> + + + + Too many channels in Rust but only one in Go + Thu, 24 Mar 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/too-many-channels/ + https://blog.pg999w.top/too-many-channels/ + <style type="text/css"> +em, strong { +color: purple; +} +img { +display: block; +margin: 0 auto; +} +</style> +<p>Channel 是异步编程 CSP 模型和 Actor 模型的重要组成部分,是一种用于消息同步的数据结构。Go 语言中的 <code>chan</code> 类型即是一种 channel 的实现。在使用 Rust 进行异步编程的时候也需要使用 channel。然而 Rust 中的 channel 似乎太多了。</p> + + + + 用 Julia 编写 CUDA 程序 + Sat, 22 Jan 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/julia-cuda/ + https://blog.pg999w.top/julia-cuda/ + <p>CUDA 本身是一个 C 库,而 CUDA kernel 则需要使用扩展的 C/C++ 语法。但 <a href="https://cuda.juliagpu.org/stable/">CUDA.jl</a> 让 Julia CUDA 编程成为可能。然而虽然 CUDA.jl 实现了绝大多数 CUDA 的功能,但其文档仍很不完善。本文补充了一些常见 CUDA 功能在 Julia 中的写法。本文假设读者预先具有 Julia,CUDA,以及 CUDA.jl 的基本知识。</p> + + + + 用 Perl 做查找替换 + Sat, 01 Jan 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/perl-replace/ + https://blog.pg999w.top/perl-replace/ + <p>现在需要把一篇文章中两个中文字符中的回车给删掉。这时候需要用到支持 Unicode 的正则表达式。这时候我们还是用最强大的字符处理语言 Perl 来搞。命令如下:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -CSAD -0p -i</span><span>.bak</span><span style="color:#bf616a;"> -e </span><span>&#39;</span><span style="color:#a3be8c;">s/(\p{category=Po}|\p{sc=Han})\n *(\p{sc=Han})/$1$2/gms</span><span>&#39; file.md +</span></code></pre> +<p>如果不涉及 Unicode 处理,可以不用加 <code>-CSAD</code>,如果处理不跨行,可以不用 <code>-0</code>,如果不需要备份文件,可以删去 <code>.bak</code>。</p> + + + + Rust 编译到 musl target 的踩坑记录 + Sun, 30 May 2021 00:00:00 +0000 + Unknown + https://blog.pg999w.top/rust-musl-target/ + https://blog.pg999w.top/rust-musl-target/ + <p>Rust 在 x86_64-unknown-linux-gnu 目标下默认会动态链接到系统 C 运行时,而不同发行版之间的 libc 可能会有兼容性问题。如果想要把一次编译好的可执行文件放到不同的 Linux 发行版上面去跑,最好采用 x86_64-unknown-linux-musl 目标进行静态编译。</p> + + + + C++ 每三年才解决一点点问题 + Mon, 30 Nov 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/c-template-constraint/ + https://blog.pg999w.top/c-template-constraint/ + <p>或:怎样优雅地给 C++ 模板添加约束?</p> + + + + Docker 搭建 RoboMaster RoboRTS 框架构建环境 + Sun, 02 Aug 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/ros-docker/ + https://blog.pg999w.top/ros-docker/ + <p><a href="https://github.com/RoboMaster/RoboRTS">RoboRTS</a> 框架用于大疆的 RoboMaster ICRA 人工智能挑战赛。其构建环境基于 <a href="https://www.ros.org/">ROS</a>,在非 Ubuntu/CentOS 的 Linux 机器上面安装较为困难。于是我们采用基于 docker 的构建方案。</p> + + + + 用 Perl 进制转换 + Mon, 22 Jun 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/perl-hex/ + https://blog.pg999w.top/perl-hex/ + <p>现在需要把一堆十进制数转换为二进制数,在 Vim 里可以用 <code>:'&lt;,'&gt;!command</code> 来做转换。但是我发现常见的行处理程序 <code>awk</code> 根本搞不定进制转换,所以还是用最强大的字符处理语言 Perl 来搞。命令如下:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ne </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%032b\n&quot;, $_)</span><span>&#39; +</span></code></pre> +<p>如果输入是十六进制呢?加个 <code>hex</code> 函数就行:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ne </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%032b\n&quot;, hex($_))</span><span>&#39; +</span></code></pre> +<p>如果一行有多个字段,只想转换第二个呢?这就要用到 <code>-a</code> 参数,此时 <code>$_</code> 变为数组 <code>@F</code>:</p> +<pre data-lang="sh" style="background-color:#eff1f5;color:#4f5b66;" class="language-sh "><code class="language-sh" data-lang="sh"><span style="color:#bf616a;">perl -ane </span><span>&#39;</span><span style="color:#a3be8c;">printf(&quot;%s\t%032b\n&quot;, @F[0], hex(@F[1]))</span><span>&#39; +</span></code></pre> +<p>最后注意在 Vim 中 <code>%</code> 符号需要转义。</p> + + + + 从 C++ 的错误处理说起 + Thu, 16 Apr 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/c-p0709/ + https://blog.pg999w.top/c-p0709/ + <p><strong>错误处理</strong>是一个非常重要的软件工程问题。对软件中出现的非致命错误的不当处理,是几乎所有的灾难性系统故障的诱因。 编程语言往往需要提供一些用于错误处理的语言设施,这些设施反过来会影响项目中错误处理的方式。不同的语言错误处理方式不同。例如 Java 采用基于 try-throw-catch 语法的异常机制,而 Go 语言则选择手动检测函数返回的 <code>error</code> 对象。一个令人惊讶的事实是,C++ 到现在还没有一个被广泛接受的错误处理方式。</p> + + + + constexpr string + Thu, 19 Dec 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/constexpr-string/ + https://blog.pg999w.top/constexpr-string/ + <p>按照 <a href="https://akrzemi1.wordpress.com/2017/06/28/compile-time-string-concatenation/">Andrzej's C++ blog</a> 里这篇文章的思路,我实现了一个编译期的字符串拼接:</p> +<pre data-lang="c++" style="background-color:#eff1f5;color:#4f5b66;" class="language-c++ "><code class="language-c++" data-lang="c++"><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#b48ead;">class </span><span style="color:#d08770;">sstring </span><span style="color:#343d46;">{ +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">char</span><span style="color:#343d46;"> inner[N]; +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">() </span><span>= </span><span style="color:#b48ead;">default</span><span style="color:#343d46;">; +</span><span style="color:#343d46;"> +</span><span style="color:#b48ead;">public</span><span style="color:#343d46;">: +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">const char </span><span style="color:#343d46;">(</span><span>&amp;</span><span style="color:#343d46;">s)[N]) : </span><span style="color:#bf616a;">inner</span><span style="color:#343d46;">{} { +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> N; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i] </span><span>=</span><span style="color:#343d46;"> s[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">template</span><span style="color:#343d46;">&lt;</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> M&gt; +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr </span><span style="color:#8fa1b3;">sstring</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">const</span><span style="color:#343d46;"> sstring&lt;M&gt; </span><span>&amp;</span><span style="color:#bf616a;">lhs</span><span style="color:#343d46;">, </span><span style="color:#b48ead;">const</span><span style="color:#343d46;"> sstring&lt;N </span><span>-</span><span style="color:#343d46;"> M&gt; </span><span>&amp;</span><span style="color:#bf616a;">rhs</span><span style="color:#343d46;">) : </span><span style="color:#bf616a;">inner</span><span style="color:#343d46;">{} { +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> M; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i] </span><span>=</span><span style="color:#343d46;"> lhs[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">for </span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int</span><span style="color:#343d46;"> i </span><span>= </span><span style="color:#d08770;">0</span><span style="color:#343d46;">; i </span><span>&lt;</span><span style="color:#343d46;"> N </span><span>-</span><span style="color:#343d46;"> M; </span><span>++</span><span style="color:#343d46;">i) { +</span><span style="color:#343d46;"> inner[i </span><span>+</span><span style="color:#343d46;"> N] </span><span>=</span><span style="color:#343d46;"> rhs[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;"> +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">constexpr char </span><span style="color:#8fa1b3;">operator[]</span><span style="color:#343d46;">(</span><span style="color:#b48ead;">int </span><span style="color:#bf616a;">i</span><span style="color:#343d46;">) </span><span style="color:#b48ead;">const </span><span style="color:#343d46;">{ +</span><span style="color:#343d46;"> </span><span style="color:#b48ead;">return</span><span style="color:#343d46;"> inner[i]; +</span><span style="color:#343d46;"> } +</span><span style="color:#343d46;">}</span><span>; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#bf616a;">sstring</span><span>(</span><span style="color:#b48ead;">char </span><span>(&amp;s)[N]) -&gt; </span><span style="color:#bf616a;">sstring</span><span>&lt;N&gt;; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> M, </span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#bf616a;">sstring</span><span>(</span><span style="color:#b48ead;">const</span><span> sstring&lt;M&gt; &amp;lhs, </span><span style="color:#b48ead;">const</span><span> sstring&lt;N&gt; &amp;rhs) -&gt; </span><span style="color:#bf616a;">sstring</span><span>&lt;N + M&gt;; +</span><span> +</span><span style="color:#b48ead;">template</span><span>&lt;</span><span style="color:#b48ead;">int</span><span> M, </span><span style="color:#b48ead;">int</span><span> N&gt; +</span><span style="color:#b48ead;">constexpr auto </span><span style="color:#8fa1b3;">operator+</span><span>(</span><span style="color:#b48ead;">const</span><span> sstring&lt;M&gt; &amp;</span><span style="color:#bf616a;">lhs</span><span>, </span><span style="color:#b48ead;">const</span><span> sstring&lt;N&gt; &amp;</span><span style="color:#bf616a;">rhs</span><span>) { +</span><span> </span><span style="color:#b48ead;">return </span><span style="color:#bf616a;">sstring</span><span>(lhs, rhs); +</span><span>} +</span><span> +</span><span style="color:#b48ead;">constexpr</span><span> sstring s {&quot;</span><span style="color:#a3be8c;">123</span><span>&quot;}; +</span><span style="color:#b48ead;">constexpr</span><span> sstring q {&quot;</span><span style="color:#a3be8c;">456</span><span>&quot;}; +</span><span style="color:#b48ead;">constexpr</span><span> sstring r {s + q}; +</span></code></pre> +<ul> +<li><code>constexpr string</code> 有什么用?这至少在初始化全局静态变量时有用。<code>constexpr</code> 静态变量不会存在烦人的初始化顺序问题。</li> +<li>因为用到了 deducing guide,所以至少需要在 C++ 17 下编译。</li> +<li><code>std::string</code> 将在 C++ 20 支持 <code>constexpr</code>,不过编译器全部普及这个特性可能还要等好几年。 +<ul> +<li>C++ 现在有一种「<code>constexpr</code> Everything」的倾向。这是为了更好的实现元编程。这是好事。</li> +</ul> +</li> +<li><code>constexpr</code> 构造函数要求初始化每个每个子对象和非静态数据成员必须被初始化。奇怪的是,clang 可以通过未初始化的代码。</li> +</ul> + + + + Rust 的指针别名优化 + Sat, 19 Jan 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/csarpp-opt/ + https://blog.pg999w.top/csarpp-opt/ + <p>本文研究了基于 Rust 具有的所有权语义的一些优化。</p> + + + + 为什么编程语言总是应该使用UTF-8而不是UTF-16 + Sat, 15 Dec 2018 00:00:00 +0000 + Unknown + https://blog.pg999w.top/why-utf16-sacks/ + https://blog.pg999w.top/why-utf16-sacks/ + <p>前段时间研究字符编码的时候,看到了一个<a href="https://www.zhihu.com/question/35214880">知乎问题</a>,里面的回答基本上都概念不清,事实上,Unicode “字符”、 +“字符串”、“编码”等词语涉及到非常复杂的概念。而目前介绍这个主题的中文文章似乎较为稀少,于是有了这篇文章。</p> + + + + diff --git a/tags/ros/index.html b/tags/ros/index.html new file mode 100644 index 0000000..892576c --- /dev/null +++ b/tags/ros/index.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

ros

+ + + +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/ros/rss.xml b/tags/ros/rss.xml new file mode 100644 index 0000000..2e98715 --- /dev/null +++ b/tags/ros/rss.xml @@ -0,0 +1,21 @@ + + + + pg999w's blog - ros + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Sun, 02 Aug 2020 00:00:00 +0000 + + Docker 搭建 RoboMaster RoboRTS 框架构建环境 + Sun, 02 Aug 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/ros-docker/ + https://blog.pg999w.top/ros-docker/ + <p><a href="https://github.com/RoboMaster/RoboRTS">RoboRTS</a> 框架用于大疆的 RoboMaster ICRA 人工智能挑战赛。其构建环境基于 <a href="https://www.ros.org/">ROS</a>,在非 Ubuntu/CentOS 的 Linux 机器上面安装较为困难。于是我们采用基于 docker 的构建方案。</p> + + + + diff --git a/tags/rust/index.html b/tags/rust/index.html new file mode 100644 index 0000000..40475bc --- /dev/null +++ b/tags/rust/index.html @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

rust

+ +
+ 2023-11-14 + + Rust 和 C++ 的对象生命管理 + +
+ + + + + + + +
+ 2020-04-16 + + 从 C++ 的错误处理说起 + +
+ +
+ 2019-01-19 + + Rust 的指针别名优化 + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/rust/rss.xml b/tags/rust/rss.xml new file mode 100644 index 0000000..766bdb7 --- /dev/null +++ b/tags/rust/rss.xml @@ -0,0 +1,75 @@ + + + + pg999w's blog - rust + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Tue, 14 Nov 2023 00:00:00 +0000 + + Rust 和 C++ 的对象生命管理 + Tue, 14 Nov 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/rust-cpp-object-management/ + https://blog.pg999w.top/rust-cpp-object-management/ + <p>Rust 和 C++ 的对象都是值语义,都采用了 RAII 惯用法。所以他们需要处理类似的对象生命周期问题:需要专门的代码来处理对象的初始化,复制和析构。下面进行一个比较,我们能够看到两种语言之间内在的对称性。</p> + + + + 用 Nix 管理交叉编译 Rust 项目的环境 + Sun, 11 Dec 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/nix-smartcross/ + https://blog.pg999w.top/nix-smartcross/ + <p>SmartCross 项目的介绍见<a href="https://blog.t123yh.xyz:2/index.php/archives/1077">这里</a>。其中的控制器组件用 Rust 写成,需要编译到 aarch64 平台。我尝试写了一个 <a href="https://nixos.org/">Nix</a> 表达式来管理该项目的环境。</p> + + + + Too many channels in Rust but only one in Go + Thu, 24 Mar 2022 00:00:00 +0000 + Unknown + https://blog.pg999w.top/too-many-channels/ + https://blog.pg999w.top/too-many-channels/ + <style type="text/css"> +em, strong { +color: purple; +} +img { +display: block; +margin: 0 auto; +} +</style> +<p>Channel 是异步编程 CSP 模型和 Actor 模型的重要组成部分,是一种用于消息同步的数据结构。Go 语言中的 <code>chan</code> 类型即是一种 channel 的实现。在使用 Rust 进行异步编程的时候也需要使用 channel。然而 Rust 中的 channel 似乎太多了。</p> + + + + Rust 编译到 musl target 的踩坑记录 + Sun, 30 May 2021 00:00:00 +0000 + Unknown + https://blog.pg999w.top/rust-musl-target/ + https://blog.pg999w.top/rust-musl-target/ + <p>Rust 在 x86_64-unknown-linux-gnu 目标下默认会动态链接到系统 C 运行时,而不同发行版之间的 libc 可能会有兼容性问题。如果想要把一次编译好的可执行文件放到不同的 Linux 发行版上面去跑,最好采用 x86_64-unknown-linux-musl 目标进行静态编译。</p> + + + + 从 C++ 的错误处理说起 + Thu, 16 Apr 2020 00:00:00 +0000 + Unknown + https://blog.pg999w.top/c-p0709/ + https://blog.pg999w.top/c-p0709/ + <p><strong>错误处理</strong>是一个非常重要的软件工程问题。对软件中出现的非致命错误的不当处理,是几乎所有的灾难性系统故障的诱因。 编程语言往往需要提供一些用于错误处理的语言设施,这些设施反过来会影响项目中错误处理的方式。不同的语言错误处理方式不同。例如 Java 采用基于 try-throw-catch 语法的异常机制,而 Go 语言则选择手动检测函数返回的 <code>error</code> 对象。一个令人惊讶的事实是,C++ 到现在还没有一个被广泛接受的错误处理方式。</p> + + + + Rust 的指针别名优化 + Sat, 19 Jan 2019 00:00:00 +0000 + Unknown + https://blog.pg999w.top/csarpp-opt/ + https://blog.pg999w.top/csarpp-opt/ + <p>本文研究了基于 Rust 具有的所有权语义的一些优化。</p> + + + + diff --git a/tags/typography/index.html b/tags/typography/index.html new file mode 100644 index 0000000..4147f8d --- /dev/null +++ b/tags/typography/index.html @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

typography

+ + + +
+ 2023-07-22 + + 为 Typst 添加中文排版支持 + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/typography/rss.xml b/tags/typography/rss.xml new file mode 100644 index 0000000..aad45fb --- /dev/null +++ b/tags/typography/rss.xml @@ -0,0 +1,37 @@ + + + + pg999w's blog - typography + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Wed, 24 Jan 2024 00:00:00 +0000 + + 在 Typst 中使用 Latin Modern 家族 + Wed, 24 Jan 2024 00:00:00 +0000 + Unknown + https://blog.pg999w.top/typst-latin-modern/ + https://blog.pg999w.top/typst-latin-modern/ + <p>高德纳在开发 TeX 时,也设计了一套字体叫 Computer Modern,作为 TeX 的默认字体。然而当时字体是采用 METAFONT 制作的,和当今的字体标准 OpenType 并不兼容。Latin Modern 通过技术手段将 Computer Modern 转换到了 OpenType 格式,并且做了扩充和微调。所以我们在 Typst 中也可以调用 Latin Modern 字体。</p> + + + + 为 Typst 添加中文排版支持 + Sat, 22 Jul 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/typst-clreq/ + https://blog.pg999w.top/typst-clreq/ + <style> +img { +max-width: 300px; +display: block; +margin: auto; +} +</style> +<p>Typst 是一个 2023 年初开源的一个排版软件。类似于 LaTeX,它通过纯文本编写源代码,然后通过编译器将源代码转换为排版好的 PDF 文件。虽然目前 Typst 的生态还不如 LaTeX,但是比起 LaTeX,它有一些明显的优势:</p> + + + + diff --git a/tags/typst/index.html b/tags/typst/index.html new file mode 100644 index 0000000..66615d2 --- /dev/null +++ b/tags/typst/index.html @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

typst

+ + + +
+ 2023-07-22 + + 为 Typst 添加中文排版支持 + +
+ +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/typst/rss.xml b/tags/typst/rss.xml new file mode 100644 index 0000000..d90c2bc --- /dev/null +++ b/tags/typst/rss.xml @@ -0,0 +1,37 @@ + + + + pg999w's blog - typst + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Wed, 24 Jan 2024 00:00:00 +0000 + + 在 Typst 中使用 Latin Modern 家族 + Wed, 24 Jan 2024 00:00:00 +0000 + Unknown + https://blog.pg999w.top/typst-latin-modern/ + https://blog.pg999w.top/typst-latin-modern/ + <p>高德纳在开发 TeX 时,也设计了一套字体叫 Computer Modern,作为 TeX 的默认字体。然而当时字体是采用 METAFONT 制作的,和当今的字体标准 OpenType 并不兼容。Latin Modern 通过技术手段将 Computer Modern 转换到了 OpenType 格式,并且做了扩充和微调。所以我们在 Typst 中也可以调用 Latin Modern 字体。</p> + + + + 为 Typst 添加中文排版支持 + Sat, 22 Jul 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/typst-clreq/ + https://blog.pg999w.top/typst-clreq/ + <style> +img { +max-width: 300px; +display: block; +margin: auto; +} +</style> +<p>Typst 是一个 2023 年初开源的一个排版软件。类似于 LaTeX,它通过纯文本编写源代码,然后通过编译器将源代码转换为排版好的 PDF 文件。虽然目前 Typst 的生态还不如 LaTeX,但是比起 LaTeX,它有一些明显的优势:</p> + + + + diff --git a/tags/wikipedia/index.html b/tags/wikipedia/index.html new file mode 100644 index 0000000..5970b3f --- /dev/null +++ b/tags/wikipedia/index.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + pg999w's blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ +
+

wikipedia

+ + + +
+ +
+
+ + + +
+ + + + + + + diff --git a/tags/wikipedia/rss.xml b/tags/wikipedia/rss.xml new file mode 100644 index 0000000..7122edd --- /dev/null +++ b/tags/wikipedia/rss.xml @@ -0,0 +1,21 @@ + + + + pg999w's blog - wikipedia + https://blog.pg999w.top/ + My life, experience and knowledge + Zola + en + + Fri, 27 Jan 2023 00:00:00 +0000 + + 在中国大陆境内编辑维基百科 + Fri, 27 Jan 2023 00:00:00 +0000 + Unknown + https://blog.pg999w.top/wiki-exemption/ + https://blog.pg999w.top/wiki-exemption/ + <p>由于 GFW 的存在,在中国境内使用维基百科必须要透过代理进行访问。然而为了避免恶意破坏,维基百科禁止匿名代理服务器修改词条。如果你的代理不幸处于封禁的 IP 段,那么将会得到类似于这样的警告:</p> + + + + diff --git a/tikz/index.html b/tikz/index.html new file mode 100644 index 0000000..faa5ad1 --- /dev/null +++ b/tikz/index.html @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + pg999w's blog - Inverse clip in TikZ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ Inverse clip in TikZ +

+ +
+ +
+

TikZ 虽然强大,但是也过于复杂。下面尝试绘制下面的图形。

+

ellipse

+

首先绘制一个直立的图形,再全局旋转。

+
\begin{tikzpicture}[rotate=20,even odd rule]
+
+

绘制实线部分,一个椭圆,一个半椭圆和两条直线。

+
\coordinate (move) at (0,3);
+\draw (move) ellipse [x radius=1,y radius=0.4];
+\draw (-1,0) -- +(move);
+\draw (1,0) -- +(move);
+\draw (1,0) arc (0:-180:1 and 0.4);
+\draw[dashed] (1,0) arc (0:180:1 and 0.4);
+
+

接下来绘制带有遮挡关系的直线。这里需要使用一个辅助的样式 invclip,用于产生「剪除」的效果。clip 相当于一个蒙板,加上 invclip 后变为一个反向蒙板。

+
\tikzset{
+  invclip/.style={
+    insert path={ (-3,-2) -- (-3,5) -- (3,5) -- (3,-2) -- (-3,-2) }
+  }
+}
+
+

\foreach 只运行两次。针对同一条直线,第一次 \stydashed\inv{};第二次 \sty{}\invinvclip

+
\foreach \sty/\inv in {dashed/{},{}/invclip} {
+  \begin{scope}
+    \path[clip,\inv]
+      (-1,0)
+      -- +(move) -- ($(move)+(1,0)$)
+      -- (1,0) arc (0:-180:1 and 0.4);
+    \draw[\sty,thick] (0,4) -- (0,-1);
+  \end{scope}
+}
+\end{tikzpicture}
+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+Inverse clip in TikZPeng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/too-many-channels/broadcast.drawio b/too-many-channels/broadcast.drawio new file mode 100644 index 0000000..ea2a9d8 --- /dev/null +++ b/too-many-channels/broadcast.drawio @@ -0,0 +1 @@ +5VxLc+MoEP41Ps6WJECPY14ze5hsZSuHnTliCduayMKFcGzPr19kgW0hxVYylrDJKaIFWHzdX0N3UxmBu/n6G8OL2SNNSDbynGQ9Avcjz3Md3xF/SsmmkoSRVwmmLE1kp73gOf1N1EgpXaYJKWodOaUZTxd1YUzznMS8JsOM0VW924Rm9V9d4ClpCJ5jnDWl/6UJn8lVeMFe/jdJpzP1y64fVW/mWHWWKylmOKGrAxF4GIE7RimvnubrO5KV4ClcqnFf33i7+zBGct5lwNPTPz+Try/eFxy7+CZM/119n35xgfw4vlErJokAQDYp4zM6pTnOHvbS23jJXkk5qysajC7zZNtyRKt4ITyeyVf70d8pXUjhL8L5RuoZLzkVohmfZ/ItWaf8x8Hzz4Pn+7X8kW1joxo5Z5sfh41yjPMXUs39sG1rNy4bb7XxShhPhb6FqIKiXP+bEEtRQZcsJkdwDaWpYjYl/Bj+3s4SBIUInRPxjWIgIxnm6Wv9Q7C05emunxx6wxjeHHRY0DTnxcHMT6VAdFC0hHJGSUrfrZmOeKhmVK2DT9uLtub1DlOTkLzibCmX1TS9LBO8Lk1sNUs5eV7gLcgr4VrqZoKLRUX2Sboube92R65StdMMF0WbQcovEBon6+MqbmpEDoicGnJQtVd756Bc3uzALyhZmwoPYH8/qi68fAJLMr5N4V6pGJ2DYQ0KAVA3BEWh3RQV9eUoTclnYFNkI5tQaJpN6ArYZJBLrtOuTkNkuop9T63FLqr6gWmqelbAqpu+eRfoX74L/FBEcLZ44LSXlMHhydO/hzqbS78BAfKOOsZG/8CNDDjSwArGw+DSQojw8hl/vhDiNHujnkjZwiKzMYRrRxChE8r4FrrL+F0yod5Lp/Zd1T2xq36UhEeClNO7Krgotlq5F6tvtMx1GI9qFIxXjqvOFfMuGViJq/Gzo4pgLMPVvB/wT+PKZnQ+Xhance0G4yTNsjuaUbadHUwmxI/jshNn9IUcvEmCaOw45wHedTRH4TaB91qA93oDvkOQ2S/wCSbhpBV4Pw7JeHIm4N86zRgDvkN9sGfgEQkT2AZ86I2B7/dj8YFjGvgOUaANwJcT1Sze8cwCD6Bx4AdxNQBCzdVAw8Ab9/HDbK4A1i0etZwWhwX+k/h4GGqba4QMA2/exw/iaqBepXQMAw87FH9tcDW6xSPjwHdIUNngagJ1ZeVSAijoGQd+EFcTAA14zzTwHVJcNriawKsDb97HX8EVzAu/MQGlCk/WdpDX2Vz6vTEBVBAhrTA6UaVB6gjW3r+fKg20IzvrhfU4JmxxtYNmZ+Gl3ZH6+KXPs5dJkXahLepYJm1MBOGJiSqv0tvtCHgN12KOu3XTVXwYdXXrF2+edu4PdlwA0veHCBjeH5AdV751roSmq/ioQ1B/hbgGpqv46lRtGa6R6So+Mh6SD1RaczQH7DeBHzQkRx0CHxtyIQLpuocGhnMhyPi9lWEsHmnZv9B02hUZL60NY/EI1oGPTGf/kPHS2kDAI83iTee7/Q6HbBuBj0xX8f1PUlpDvu5qTAPf4ZhuBfDa5uo6xpFvHuQfn57vGuiLNXMtGsrSaS6eY7F2IrC6Vcm4G/linibJNnXISJH+xuPtVKUeZIZJzItuR+i+nGvJaVGlB92GDnKaE01hSkRzLrOKbngeDbmBVoODzV04aFEQ6E1Bzbt0j0+Pn1dBSL8IY1xBzYjsllGcxLjgn1ZLINJ2GL+ppd194T9Uk2ju/9dYlUDe/8c28PA/ \ No newline at end of file diff --git a/too-many-channels/broadcast.drawio.svg b/too-many-channels/broadcast.drawio.svg new file mode 100644 index 0000000..b63df5f --- /dev/null +++ b/too-many-channels/broadcast.drawio.svg @@ -0,0 +1,4 @@ + + + +
MPSC
MPSC
MPMC
MPMC
Broadcast
Broadcast
Text is not SVG - cannot display
\ No newline at end of file diff --git a/too-many-channels/index.html b/too-many-channels/index.html new file mode 100644 index 0000000..2d86080 --- /dev/null +++ b/too-many-channels/index.html @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + pg999w's blog - Too many channels in Rust but only one in Go + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ Too many channels in Rust but only one in Go +

+ +
+ +
+ +

Channel 是异步编程 CSP 模型1和 Actor 模型的重要组成部分,是一种用于消息同步的数据结构。Go 语言中的 chan 类型即是一种 channel 的实现。在使用 Rust 进行异步编程的时候也需要使用 channel。然而 Rust 中的 channel 似乎太多了。

+ +

首先 Rust 标准库 std::sync::mpsc 模块中就提供了两种 channel 实现 channelsync_channel。最流行的 Rust 异步运行时 tokio 也在 tokio::sync 模块中提供了其自己的 channel 实现,有四种之多。如果我们访问 Lib.rs 上的 Concurrency 分类,我们能轻易找到排名第 2 和第 8 的两个专门的 channel 库 crossbeam-channel 和 flume。这两个库分别有上千和上百的公开 crate 依赖。既然在 Go 语言当中,一种 channel 就够了,那在 Rust 中我们为什么需要这么多 channel 呢?

+

Go and rendezvous

+

实际上,channel 不止一种。Go 语言里使用 make(chan T) 创建的是 rendezvous channel,内部不使用缓冲区。如果通过一个 rendezvous channel 从一个 goroutine 向另一个 goroutine 发送消息,则会一直阻塞,直到对方接收为止。如果我们希望发送数据的 goroutine 不要阻塞,那么可以使用 make(chan T, size) 创建一个 buffered channel,这时将会创建一个大小为 size 的缓冲区。只要缓冲区不满,发送者就不必阻塞。

+

在 Go 语言中,并不需要有意避免阻塞一个 goroutine,调度器将会把当前的 CPU 资源分给其他可以继续执行的 goroutine。所以大多数时候简单的 rendezvous channel 就足够用了。但是如果由于种种原因,我们真的需要 channel 永远不要阻塞,这时即使是固定大小的 buffered channel 也不能满足要求,我们需要一个能自动扩容缓冲区的 unbounded buffered channel。Go 内置的 chan 类型没有提供这样的功能,好在我们可以将两个 renderzvous channel 和一个可扩容的环状缓冲区组合起来,实现一个 unbounded buffer。chanx 就是这样的一个实现2

+

+

查看 chanx 的源代码我们可以看到,其内部使用了两个 chan,并为每个 channel 都创建了一个新的 goroutine。这显然带来了不必要的开销。用一种 channel 来实现其他类型的 channel,这当然符合 Go 语言极简主义的哲学,然而其付出的性能代价是 Rust 所不能接受的。所以我们看到 Rust 标准库除了提供类似于 Go chanmpsc::sync_channel,还另外实现了 unbounded channel 即 mpsc::channel。其他 Rust 库的 channel 也基本都提供了 bounded 和 unbounded 变体。

+

值得注意的是在 Go 语言当中 rendezvous channel 常常和 select 语句搭配使用,而 Rust 标准库中的 select! 宏由于种种原因已经被移除了。好在第三方库的 channel 都实现了 select! 宏,想要在 Rust 中像 Go 一样用 rendezvous channel 和 select 编写程序的话,使用第三方库即可。

+

Can the sender awaits?

+

缓冲区的问题归根结底还是发送端 send 函数阻塞的问题。基本上我们有「unbounded channel —— 不阻塞线程;bounded channel —— 可能阻塞线程」这样一组对应关系。为了像 Go 语言一样即使没有缓冲区也不阻塞线程,我们需要利用 Rust 的 async 机制。支持 async 异步的 channel 将包含一个 async fn send(T)async fn recv(),调用时只会阻塞当前 task 而不会影响其他任务。

+

与 Go 语言不同,Rust 的 async 函数是所谓的「着色函数」,这意味这不能在非 async 环境中调用 async 函数。同时,基于 async 运行时的实现原理,也不能在 async 环境中直接调用同步的阻塞函数。这样实际上可以把函数分成三种类型:非阻塞函数(unblocked)、同步的阻塞函数(blocked)、以及 async 函数。他们的特性可以总结如下:

+ + + +
可以用在非 asnyc 环境可以用在 async 环境
不会阻塞unblockedunblocked
可能阻塞blockedasync
+

不同的 channel 实现,其 sendrecv 函数具有的阻塞性质可能会不同。我们需要根据需求选取。如果 channel 一边实现了 async 函数,另一边实现了非 async 函数,我们就可以利用该 channel 在没有锁的情况下实现 async 程序部分与非 async 部分的同步。

+

To be cloneable or not to be cloneable

+

一个典型的 Rust channel 会被这样创建:

+
let (tx, rx): (Sender, Receiver) = channel();
+
+

Sender 和 Receiver 在同一个线程被分别创建,然后再发到各自的线程执行工作。Rust 在语言层面上保证了多线程安全,在其中遇见多线程环境下使用的类型,自然会想要考察这两个对象相关的特性:

+
    +
  1. 它是否可以穿过线程边界?
  2. +
  3. 它是否能够被多个线程无锁地共享?
  4. +
  5. 它是否可以被低代价地克隆?
  6. +
+

对于第 1 个问题,简单地检查类型是否满足 Send 约束即可。答案显然是肯定的。作为 Rust 多线程安全的基石之一,绝大多数类型都实现了 Send。各 channel 库中的 Sender 和 Receiver 也不例外。3

+

而第 2 个问题则没有看起来的那么简单。虽然 Rust 中有 Sync 这个 trait,而且和 Send 一样,绝大多数类型也实现了 Sync,但是 T: Sync 只意味着不可变&T 能被多个线程共享。如果某个 Sender 的 send 函数或 Receiver 的 recv 函数要求拿到一个 &mut self,仅仅不可变的共享则完全没有意义。所以,要想肯定地回答问题 2,需要同时满足两个条件:所考察对象满足 Sync 约束;其 send / recv 函数只要求不可变的 &self 引用。std::sync::mpsc::Receiver 即不满足前一个条件,而 tokio::sync::mpsc::Receiver 则不满足后一个条件。

+

即使对象能够以 &T 的形式在多个线程中无锁共享,在多数情况下,为了解决生命周期的问题,我们仍然需要在外面套上 Arc 才能达到目的。

+
let (tx, rx) = channel();
+let tx_arc = Arc::new(tx);
+let tx_clone = tx_arc.clone();
+std::process::spawn(|| tx_clone.send(something()));
+
+

这时自然会想到,如果 tx 自身能够 clone,那么我们就不必再套一个 Arc 了。这正是我们要考察的第 3 个特性。如果 Sender 实现了 Clone 约束,我们可以直接通过克隆来在多个线程之间分享:

+
let (tx, rx) = channel();
+let tx_clone = tx.clone();
+std::process::spawn(|| tx_clone.send(something()));
+
+

虽然性质 2 和性质 3 并不全等,然而实现了性质 2 的 channel 类型实际上也都实现了性质 3。一般而言,在考察 channel 类型的线程同步特性时,只需要考察它是否实现 Clone 即可。

+

回过头来考察 Go 语言的 channel,我们发现其 chan 类型既可以做 Sender,也可以做 Receiver。chan 也可以安全地被克隆,在多个线程中共享。这实际上一个 MPMC channel4。而在 Rust 中,通过限制 Receiver: !Clone,我们可以得到一个 MPSC channel。注意虽然 MPMC channel 有多个出口,但任意的消息只能从其中一个出口出去。如果我们需要一个「广播」性质的 channel,我们可以实现一个 MPSC channel 数组,每个消息分别向各个出口发送,也可以直接使用 tokio::sync::broadcast 中的 channel。

+

+

Mix all together

+

前面讨论了缓冲区大小、send / recv 函数的阻塞性、以及 Clone trait。在不同的场合下,我们需要选取具有不同特性组合的 channel。现在是时候画一张大表把它们全部列出来了。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Channel constructor Buffer size Cloneable send recv
std::mpsc
channel Sender unblocked blocked
sync_channel 0~n Sender blocked blocked
crossbeam-channel
unbounded

Sender, Receiver
unblocked
blocked
bounded 0~n Sender, Receiver blocked blocked
tokio::sync
mpsc::unbounded_channel
Sender unblocked async
mpsc::channel 1~n Sender async async
broadcast 1~n Sender unblocked async
flume unbounded

Sender, Receiver
unblocked
blocked/async
bounded 0~n Sender, Receiver blocked/async blocked/async
+

recv 不可能做到完全不阻塞,但是 Receiver 一般都提供一个不阻塞的 try_recv 函数。

+

tokio::sync::broadcast 里的 Receiver 虽然不能 clone,但是可以直接通过 Sender::subscribe 得到。

+

Epilogue

+

本文所讨论的 channel 概念源自异步编程模型 CSP 和 Actor,这两种模型都是语言无关的数学抽象。我们看到同样的概念可以同时用在不同的语言之中。在这样的基础上,Go 语言中基于 CSP 的异步编程模型可以直接迁移到 Rust 当中去。虽然这两种语言各自具有鲜明的特点,Go 语言奉行极简主义,而 Rust 则拥抱复杂性以换取安全和更好的性能,但一个熟悉 Go 异步编程的程序员,应当发现能用同样的心智模型在 Rust 编写异步程序。

+

Footnotes

+
1 +

Communicating Sequential Processes,交谈循序程序,又译为通信顺序进程、交换消息的循序程序

+
+
2 +

实际上 chanx 使用了一定大小的 channel buffer,不过如果移除这个 buffer,程序仍然能正常工作。

+
+
3 +

T: !Send 的类型在多线程环境中寸步难行。注意到 std::sync::MutexSendSync 实现

+
impl<T: ?Sized + Send> Send for Mutex<T> {}
+impl<T: ?Sized + Send> Sync for Mutex<T> {}
+
+

如果一个类型 T: !Send,我们甚至没法通过加锁实现跨线程共享。

+
+
4 +

Multiple producer multiple consumer.

+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+Too many channels in Rust but only one in GoPeng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/too-many-channels/rendezvous.drawio b/too-many-channels/rendezvous.drawio new file mode 100644 index 0000000..c66c966 --- /dev/null +++ b/too-many-channels/rendezvous.drawio @@ -0,0 +1 @@ +7Vvfc6o4FP5reLwdCD/Ex2p7tw93Zzrtzux2X3ZSiJJtJGyIVfvXbwKJCEHFXlE7VzvTIYcQ4Dvf+XJyopY7ni1/YzBLfqcxIhaw46Xl3lkABKEr/kvDqjR4w0FpmDIclyanMjzjD6SMtrLOcYzyWkdOKeE4qxsjmqYo4jUbZIwu6t0mlNTvmsEpMgzPESSm9U8c86S0hmBQ2R8Qnib6zk4wLM/MoO6s3iRPYEwXGyb33nLHjFJeHs2WY0QkdhqX8rrvW86uH4yhlHe5IErIP1OUPMfD7w9/z1K2elk8fVPeeYdkrl5YPSxfaQRQLABRTcp4Qqc0heS+so4YnacxkrexRavq84PSTBgdYfwXcb5S3oVzToUp4TOizuZviEeJapQPIO+69UWVKadzFqEdb6cJA9kU8R39wNodgsaIzhBnK3EdQwRy/F5/DqgINV33qzAXBwr2A1zgGC7IkYCTGY6ow7xIMEfPGSwAWIjY2wvpO2IcLXeDaoKgL3AVjVUcO4FqL6qoGChTshEQnt0TbMCAjaEICW9dGHBucGHADc8R4QIstvpLXn8DfN1+UeMVjbtlrbVSrZo7ojl7L57iyDrhd9SJ8Jw64X9JnQDnpnvwRXXi7MDpDOxcQlFTiRt/j06gJealvgx81XzZOFVdJRsnFZewo7gE5xSX0IiS1/lksjNGnNPHyJrsKkZcx4yRYUuMuL3FiJm8MSnKH+90nhvYiRfndYAgwdNUHEcCAAG2O5LwYLH6uFUnZjiOy8BCOf6Ar8VQkroZxSkvXscfWf6dHEvEUl6GVYE9Z/QNjSmhYty7lKZylAkmpGE6glu8QSPFGZhuCVvcAnpzi5kclnQWxP1VnQJanBKc1Ckd1ppijZzJQ6HFZDViMHqTkrlPZuqadGzR8ZsZDTCBBC1AOv0h6Z0GyQnB2UNPqAa+c+PXVaMl4TktrmDYHVdehv0meG3Y5mLa5yrZkQoR0ZRDnEpZKZMOSgjMclxpSJRgEv+AKzrn+ja6dXxqh/vXpp7b4oKwLxfoWOvsgidZUhsllOEPCS3RmV0D93yBZwSm6AHBuGEa0Vjng7xIUuURQROuDl8p53SmGkyBYLf6LmY0+0PneXa7tNfniJF437HMbsVsAcai7VTtYgLJRA49pqmYOARt5LAI5nyB8s/RYQftTZLsIYEf9sUBM6kylyKaBOLtOIbkCUUcptMuIakqxRvTdouXDmMCFbBPSFHcTURugNKmcyaCm4qNzuCnnOXaR3eWGqyC8ODRIBGJUgq5iCUxf+QGA9bP+ROkOGDOuwpDv8KwqrNgD1kC0JdOmBWx9KoTtXg5WCd2OetwnWiOdgqdMCsZV504r044LQumVrb0llS25PX3wApDa3h/FYxa4BwuGDuc9gnBaIx2AsHwzMRClz7tonLzqxSM/CBorshdM3DbtiB6W5B75gxvgYBwRfyaX4L/5lSf+FYieCs6OCBbVifF0ZQXAVfWOdRgr0yf0BbxuOUdtPkrkOBTYrCnTBM2yzTA9g1StNURe6u5e+aO3nFIMU+vtPg8LYYmLdpq/v3RYrA/8UNpfCu/E1ZhE8M8Wdc7N9wm7Y+Qy9mnsADbXSOuvwcGrOZ+59apWXat7S/q7Up9vLHJuX23suZvC7h3Q/lnHXW3UgXX3t3KcotlZ7rgt7hf2zrvaqo7PMpgqPjnNTYCQbMgWb6nuqpi1v6B/MZAJRDGQEfLPjqUnC+ItZ/dnJetR8SwgKsofduXHAKgawhs0cgThYDfZG5jsd05BAbDG88fVp9mQNx4gV19vJPGh99hP+CC4kN/CeXSKK1n3SunL4HT5v7GJXNaa/660U3zT8Jrr2u60qG6ceX1Vl6LZvU7hbJ79WMP9/5/ \ No newline at end of file diff --git a/too-many-channels/rendezvous.drawio.svg b/too-many-channels/rendezvous.drawio.svg new file mode 100644 index 0000000..70f985c --- /dev/null +++ b/too-many-channels/rendezvous.drawio.svg @@ -0,0 +1,4 @@ + + + +
sender
sender
receiver
receiver
sender
sender
receiver
receiver
buffer
buffer
rendezvous
rendezvous
buffered
buffered
0
0
n
n
buffer size
buffer size
bounded
bounded
unbounded
unbounded
Text is not SVG - cannot display
\ No newline at end of file diff --git a/typst-clreq/adjustment-1_1684332682734_0.png b/typst-clreq/adjustment-1_1684332682734_0.png new file mode 100644 index 0000000..f3fae3e Binary files /dev/null and b/typst-clreq/adjustment-1_1684332682734_0.png differ diff --git a/typst-clreq/adjustment-2_1684333173332_0.png b/typst-clreq/adjustment-2_1684333173332_0.png new file mode 100644 index 0000000..5eb9bf6 Binary files /dev/null and b/typst-clreq/adjustment-2_1684333173332_0.png differ diff --git a/typst-clreq/bad_1684331737139_0.png b/typst-clreq/bad_1684331737139_0.png new file mode 100644 index 0000000..5cd5024 Binary files /dev/null and b/typst-clreq/bad_1684331737139_0.png differ diff --git a/typst-clreq/compress_1684333418552_0.png b/typst-clreq/compress_1684333418552_0.png new file mode 100644 index 0000000..2152a0c Binary files /dev/null and b/typst-clreq/compress_1684333418552_0.png differ diff --git a/typst-clreq/index.html b/typst-clreq/index.html new file mode 100644 index 0000000..496d36c --- /dev/null +++ b/typst-clreq/index.html @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + pg999w's blog - 为 Typst 添加中文排版支持 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 为 Typst 添加中文排版支持 +

+ +
+ +
+ +

Typst 是一个 2023 年初开源的一个排版软件。类似于 LaTeX,它通过纯文本编写源代码,然后通过编译器将源代码转换为排版好的 PDF 文件。虽然目前 Typst 的生态还不如 LaTeX,但是比起 LaTeX,它有一些明显的优势:

+ +
    +
  • Typst 支持高效的增量编译,可以实现媲美 Markdown 的实时渲染更新。
  • +
  • Typst 被设计为一种函数式编程语言,支持变量作用域,高阶函数等现代特性。这使得编程和调试比 LaTeX 轻松很多。
  • +
  • Typst 被构建为单个可执行文件,其包管理器也开箱即用1。使用上手几乎没有障碍。
  • +
+

Typst 原生支持 Unicode,但是由于其作者不懂中文,0.1 版本的 Typst 对中文排版的支持非常差。笔者于 2023 年的四到五月间提交了一系列 PR,使得 Typst 的中文支持初步达到了可用的水平。本文是对实现功能及思路的简要介绍。为了在软件中实现中文排版,我们首先需要知道中文排版的规则,再将软件中缺失的部分补齐。

+

古典活字印刷的原则

+

早期的中文排版使用的是活字印刷技术。传统的活字书籍文本中没有标点,方块字紧密地纵横排列在矩形版心之中。今天中文排版的密排原则,正是源自于活字印刷。

+ +
+ +
+清乾隆年间木活字《武英殿聚珍版从书》书页 +
+
+
+

排版原则:汉字密排

+

汉字依行排列文字,原则上文字外框彼此紧贴配置,称作密排

+
+

在方块字上面做密排带来了两个效果。一方面我们自然获得了两端对齐,另一方面一行的长度正好是汉字宽度的整数倍。这两个特征作为中文排版的基本规则被沿袭了下来。

+
+

排版原则:两端对齐

+

也称为头尾对齐。

+
+
+

排版原则:行长是字号的整数倍

+
+

Typst 中的实现

+

原始版本

+

我们来看一下 Typst 的中文排版能不能实现上面提到的三条原则。密排,用西文排版的术语来考虑就是字符间距为 0,这是默认的配置。而两端对齐和行长都可以通过 #set 规则来设置。使用 0.1.0 之前的 Typst,我们尝试对样例进行排版,规定每行排17个字。排出来的效果如下:

+

bad.png

+

需要注意的是,这是 Typst 0.1 版本,这个时候的排版代码完全是由不懂中文的原作者写的。这个排版效果已经出人意料地好了。唯一的问题是,第一行多出了两个空格,两端对齐的原则被违反了。这是因为引入了西式标点的现代中文需要遵守一些禁止规则,我们称为标点禁则

+
+

排版原则:标点禁则

+

点号,右夹注符号不能出现在行头;左夹注符号不能出现在行尾2

+
+

如果我们机械地按照一行 17 个字来进行排版,那么将得到以下效果:

+
孔雀最早见于《山海经》的《海内经》
+:「有孔雀」。东汉杨孚著《异物志》
+记载……
+
+

这将使得冒号被错误地放在一行的开头。实际上,如果每个字符的宽度都确定了,那么标点禁则和两端对齐的原则没有办法同时得到满足。

+

标点禁则是一个在全球各种语言都存在的现象,Unicode 专门有规范附件 UAX #14 规定了断行算法,以确保各语言的标点禁则得到了遵守。Typst 的作者虽然不懂中文,但是他们在 Typst 中实现了 UAX #14,于是标点禁则被自动地支持了。这就导致了两端不对齐的排版。

+

挤进推出

+

应该如何做出同时满足两端对齐和标点禁则的排版方式呢?中文排版中有「挤进推出」的方法,也就是通过挤压的方式把从下一行的字「挤进」本行,或者通过拉伸的方式将本行最后一个字「推出」到下一行。日本开发者 Alex Sayers 就通过一个 PR 实现了这种排版3。下图即合并了 PR #542 之后的排版效果。注意到下面的段落中第一行被挤进了 18 个字,而第三行被拉成了只有 16 个字。

+

justify.png

+

遗憾的是,这个排版存在着严重的缺陷:

+
    +
  • 汉字自然排列时已经是密排了,如果再压缩将会使笔画交叉,造成非常不良的排版。事实上, 原则上汉字之间可以拉伸,但不能压缩
  • +
  • 采用了拉伸调整的第三行,我们发现其行尾「足」字还是没有和边框对齐。当然这只是实现上的 bug,这个调整本身是没有问题的。
  • +
+

正确的推出实现

+

为了解决这个问题,笔者实现了 PR #701。其实现的效果如下。由于改进了 Typst 内部用于断行的动态规划算法,这个版本的断行和之前的不太一样。注意到第一行被拉伸到只有 16 个字之后,其他行的字符都成功放到了纵横网格之中。

+

adjustment-1.png

+

这样一来算是成功实现了满足两端对齐和标点禁则的排版。而字间距的略微扩大,作为一个代价,也相对易于接受。不过实际上在实现标点挤压之后,大多数排版并不需要强行拉伸字间距。

+

标点挤压

+

到刚才为止,我们的中文段落排版只有一种段落调整的方式,那就是均匀调整段落中每一个字符之间的间距,来实现「挤进推出」的效果。进一步地,由于字符之间的间距默认已经是 0 了,我们为了避免不良排版,只用了「推出」的方式,即均匀地在一行中所有相邻的两个字中间,插入等量的空白。

+

实际上中文排版中是可以「挤进」而保持良好排版的,那就是不调整汉字,而是使用标点的调整空间来进行调整。

+
+ +
+W3C 文档《中文排版需求》中关于标点调整空间的描述 +
+
+

笔者的 PR #836 引入了标点挤压。我们发现这样能使得段落变得更加紧凑,整个文本排进了四行之中。而美中不足在于行头行尾的标点有大量空白,造成了段落两边参差不齐的观感。这个问题在下一个 PR 中得到了修复。

+

adjustment-2.png

+

更多的标点挤压

+

在之前的实现中,标点挤压是作为一种解决标点禁则问题的手段,「挤进推出」中的「挤进」来实现的。实际上标点挤压还有一个功能是解决标点空白过大的问题。例如冒号后面跟一个左引号,如果不进行挤压,将会产生超过一个汉字宽的空白,这样段落显得松散。所以即使没有标点禁则的问题,我们往往也会对标点进行挤压。

+

事实上,标点挤压主要有两个基本规则,我们称为默认挤压

+
    +
  • 连续的两个标点,原则上压缩掉半个字宽的空间。
  • +
  • 行头与行末的标点,原则上压缩掉头(尾)的半个字宽的空白。
  • +
+

在默认挤压的基础上,我们再对挤压量进行微调,以解决标点禁则的问题。PR #954 实现了这样的调整。

+

compress.png

+

最终我们得到了较为满意的排版。这个排版同时满足了两端对齐,文字密排和标点禁则。而且排布紧凑,灰度均匀。实际上,使用专业的中文排版软件 Adobe InDesign 进行排版,采用推荐的配置4,达到的效果和 Typst 相似。当然排版没有标准答案,中文排版中也存在着多种风格,InDesign 有数量繁多的配置项,可以制作出各种风格的排版。由于笔者时间有限,在 Typst 的实现中没有任何配置项。许多排版风格,例如开明式标点,标点悬挂,孤字控制等,都没有实现。换言之 Typst 的中文排版目前提供的是一个「基本正确」的默认风格实现。不过这对于多数普通用户而言已经够用了。

+
1 +

截止到本文完成时,Typst 的包管理功能还处于预览状态。

+
+
2 +

这实际上是一个近似的说法,详细的规则见 https://w3c.github.io/clreq/#prohibition_rules_for_line_start_end

+
+
3 +

日文中同时有汉字,平假名和片假名三种书写系统,它们都是方块字,且采取密排原则。由于日文和中文的历史渊源,两种语言的排版原则存在大量相同之处。本文中提到的几乎所有排版问题都同时适用于两种语言。

+
+
4 +

即「简体中文避头尾」+「所有行尾 1/2 个字宽」+「Adobe CJK 单行书写器」+「两端对齐」+「避头尾间断类型:确定调整量优先级」。详见 The Type 的相关文章

+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+为 Typst 添加中文排版支持Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/typst-clreq/justify_1684332030763_0.png b/typst-clreq/justify_1684332030763_0.png new file mode 100644 index 0000000..9a0c362 Binary files /dev/null and b/typst-clreq/justify_1684332030763_0.png differ diff --git a/typst-latin-modern/index.html b/typst-latin-modern/index.html new file mode 100644 index 0000000..e9524c0 --- /dev/null +++ b/typst-latin-modern/index.html @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + pg999w's blog - 在 Typst 中使用 Latin Modern 家族 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ 在 Typst 中使用 Latin Modern 家族 +

+ +
+ +
+

高德纳在开发 TeX 时,也设计了一套字体叫 Computer Modern,作为 TeX 的默认字体。然而当时字体是采用 METAFONT 制作的,和当今的字体标准 OpenType 并不兼容。Latin Modern 通过技术手段将 Computer Modern 转换到了 OpenType 格式,并且做了扩充和微调。所以我们在 Typst 中也可以调用 Latin Modern 字体。

+ +

下面是一个在 Typst 中使用 Latin Modern 字体的 demo,显示了 Latin Modern 字体家族的全部 72 种字体。

+

在这里查看源码

+

今年 Typst 应该就要支持 HTML 导出功能,届时可以将 PDF 文件换成 HTML。

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+在 Typst 中使用 Latin Modern 家族Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/typst-latin-modern/lm.pdf b/typst-latin-modern/lm.pdf new file mode 100644 index 0000000..52db034 Binary files /dev/null and b/typst-latin-modern/lm.pdf differ diff --git a/typst-latin-modern/lm.typ b/typst-latin-modern/lm.typ new file mode 100644 index 0000000..e4078d1 --- /dev/null +++ b/typst-latin-modern/lm.typ @@ -0,0 +1,212 @@ +#let sample-text = "Sample Text" + +#show table: box // Make it unbreakable + +#let xy-table(xtitle, xargs, ytitle, yargs, disable: (), additional: ()) = { + table(columns: xtitle.len() + 1, + ..xtitle + .enumerate() + .map(((i, text)) => table.cell(x: i + 1, y: 0, text)), + ..ytitle + .enumerate() + .map(((i, text)) => table.cell(x: 0, y: i + 1, text)), + ..for row in array.range(ytitle.len()) { + for col in array.range(xtitle.len()) { + if not disable.contains((row, col)) { + (table.cell(x: col + 1, y: row + 1, + text(..xargs.at(col), ..yargs.at(row), sample-text)),) + } + } + }, + ..additional, + ) +} + +#let disable-list(matrix) = for row in array.range(matrix.len()) { + for col in array.range(matrix.at(row).len()) { + if matrix.at(row).at(col) == 0 { + ((row, col),) + } + } +} + +#let family-opsz(style, sz) = { + let suffix = if sz == 10 { "" } else { " " + str(sz) } + "Latin Modern " + style + suffix +} + +#align(center, text(font: "Latin Modern Roman 12", weight: "bold", size: 20pt)[The Latin Modern Families]) + +#set text(font: "Latin Modern Mono", size: 10pt) + += Latin Modern Mono (20 fonts) + +== Optical Size + +#table(columns: (auto, auto), + ..for opsz in (8, 9, 10, 12) { + (str(opsz) + "pt", text(font: family-opsz("Mono", opsz), size: opsz * 1pt, sample-text)) + } +) + +== Italic + +#text(style: "italic", sample-text) + +== Other Styles + +#let repeat(elem, times) = { + for _ in array.range(times) { + (elem,) + } +} + +#let mono-table = xy-table( + ([Light], [Regular], [Bold]), + ((weight: 300), (), (weight: 700)), + ([Normal], [Cond], [Caps], [Prop]), + ( + (), + (stretch: 65%), + (font: "Latin Modern Mono Caps"), + (font: "Latin Modern Mono Prop") + ), + disable: ((1, 1), (1, 2), (2, 0), (2, 2)) +) + +=== Upright + +#mono-table + +=== Oblique + +#{ + set text(style: "oblique") + mono-table +} + +#pagebreak() +#set text(font: "Latin Modern Roman") + += Latin Modern Roman (34 fonts) + +== Optical Size, Italic, and Bold + +#{ + let matrix = ( + (1, 0, 0, 1, 0, 0), + (1, 0, 0, 1, 0, 0), + (1, 1, 0, 1, 0, 0), + (1, 1, 1, 1, 0, 0), + (1, 1, 1, 1, 0, 0), + (1, 1, 1, 1, 1, 1), + (1, 1, 1, 1, 0, 0), + (1, 0, 1, 0, 0, 0), + ) + let opsz-list = (5, 6, 7, 8, 9, 10, 12, 17) + xy-table( + ([Normal], [Italic], [Slanted], [Bold], [Bold Italic], [Bold Slanted]), + ( + (), (style: "italic"), (style: "oblique"), + (weight: "bold"), (weight: "bold", style: "italic"), (weight: "bold", style: "oblique") + ), + opsz-list.map(sz => str(sz) + "pt"), + opsz-list.map(sz => (font: family-opsz("Roman", sz), size: sz * 1pt)), + disable: disable-list(matrix), + additional: (table.cell(x: 0, y: 0, [opsz]),) + ) +} + +== Other Styles + +#xy-table( + ([Normal], [Oblique]), + ((), (style: "oblique")), + ([Caps], [Demi], [Dunhill], [Unslanted]), + ( + (font: "Latin Modern Roman Caps"), + (weight: 600), + (font: "Latin Modern Roman Dunhill"), + (font: "Latin Modern Roman Unslanted") + ), + disable: ((3, 1),) +) + +#pagebreak() +#set text(font: "Latin Modern Sans") + += Latin Modern Sans (18 fonts) + +== Optical Size, Bold + +#{ + let opsz-list = (8, 9, 10, 12, 17) + xy-table( + ([Normal], [Oblique]), + ((), (style: "oblique")), + opsz-list.map(sz => str(sz) + "pt"), + opsz-list.map(sz => (font: family-opsz("Sans", sz), size: sz * 1pt)), + additional: (table.cell(x: 0, y: 0, [opsz]),) + ) +} + +== Other Styles + +#xy-table( + ([Normal], [Oblique]), + ((), (style: "oblique")), + ([Bold], [Demi Cond], [Quotation], [Quotation Bold]), + ( + (weight: "bold"), + (weight: "semibold"), + (font: "Latin Modern Sans Quotation"), + (font: "Latin Modern Sans Quotation", weight: "bold") + ), +) + +#pagebreak() +#set text(font: "Latin Modern Serif") + += How to use these variants in Typst + +== Optical Size + +The default optical size is 10pt. +All optical sizes other than 10pt are available as separate font families. + +```typst +#set text(font: original-family + " 12") +``` + +== Italic, Oblique(Slanted) + +```typst +#set text(style: "italic") +#set text(style: "oblique") +``` + +== Light, Demi, Bold + +```typst +#set text(weight: "light") +#set text(weight: "semibold") +#set text(weight: "bold") +``` + +== Cond + +```typst +#set text(stretch: 66%) +``` + +== Other Styles + +All other styles are available as separate font families. + +```typst +#set text(font: original-family + " Caps") // For Mono and Roman +#set text(font: "Latin Modern Mono Prop") +#set text(font: "Latin Modern Roman Dunhill") +#set text(font: "Latin Modern Roman Unslanted") +#set text(font: "Latin Modern Sans Quotation") +``` diff --git a/unicode.dot b/unicode.dot new file mode 100644 index 0000000..12a79b9 --- /dev/null +++ b/unicode.dot @@ -0,0 +1,16 @@ +digraph { + rankdir=BT + compound=true + codeunit [label="Code Unit"] + abschar [label="Abstract Character"] + codepo [label="Code Point"] + grcl [label="Grapheme cluster"] + + codeunit -> codepo [label="one or many"] + subgraph cluster_encchar { + rankdir=LR + style=filled + codepo -> abschar [dir=both,label="Encoded Character"] + } + codepo -> grcl [label="one or many",ltail=cluster_encchar] +} diff --git a/unicode.svg b/unicode.svg new file mode 100644 index 0000000..7994d2a --- /dev/null +++ b/unicode.svg @@ -0,0 +1,63 @@ + + + + + + +%3 + + +cluster_encchar + + + + +codeunit + +Code Unit + + + +codepo + +Code Point + + + +codeunit->codepo + + +one or many + + + +abschar + +Abstract Character + + + +codepo->abschar + + + +Encoded Character + + + +grcl + +Grapheme cluster + + + +codepo->grcl + + +one or many + + + diff --git a/why-utf16-sacks/index.html b/why-utf16-sacks/index.html new file mode 100644 index 0000000..edd4e23 --- /dev/null +++ b/why-utf16-sacks/index.html @@ -0,0 +1,379 @@ + + + + + + + + + + + + + + + pg999w's blog - 为什么编程语言总是应该使用UTF-8而不是UTF-16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 为什么编程语言总是应该使用UTF-8而不是UTF-16 +

+ +
+ +
+

1前段时间研究字符编码的时候,看到了一个知乎问题,里面的回答基本上都概念不清,事实上,Unicode “字符”、 +“字符串”、“编码”等词语涉及到非常复杂的概念。而目前介绍这个主题的中文文章似乎较为稀少,于是有了这篇文章。

+ +

本文不仅讨论 UTF-8 与 UTF-16 ,还涉及了 Unicode 编码在应用于编程语言中的许多方面。我会尽量在保证准性的情况下,使文章简短,易懂。但我不是字符编码方面的专家,如果有遗漏错误之处,敬请在评论区中指出。

+

Unicode 的字符概念是怎样构建的

+
+

抽象字符 (Abstract character)

+

一个用于组织、控制或表示文字数据的信息单元。例如,英文字母表的第一个大 +写字母是一个抽象字符,它可以用“A”或“𝑨”表示,但它不是“a”或“B”。任何人能想到的字符,都 +是抽象字符。例如, 精灵语 tengwar 里的字母 ungwe 是一个抽象的字符,虽 +然它尚不能用 Unicode 表示。

+
+
+

码位 (Code point)

+

Unicode 编码空间的任何数值。例如 U+3243F。

+
+

抽象字符是 Unicode 编码的起点。建立抽象字符与码位的映射,就是编码的过程。

+
+

已编码字符 (Encoded character)

+

码位和抽象字符之间的映射。例如,U+1F428 是一个代表抽象字符 🐨 考拉的已编码字符。

+
+

但需要注意这种映射是既不是双射,也不是单射,更不是满射:

+
    +
  • 代理字符 (surrogate),非字符 (noncharacter) 和未分配的码位完全不对应抽象字符。
  • +
  • 一些抽象字符可以由不同的码位进行编码;U+03A9 希腊大写字母 OmegaU+2126 欧姆符号 都对应于同一个抽象字符“Ω”,必须同等对待。
  • +
  • 一些抽象字符无法用单个码位进行编码,要用已编码字符的序列才能表示它们。例如,表示抽象字符“ю́ 带锐音符的西里尔小写字母 yu”的唯一方法是使用 <U+044E 西里尔小写字母 yu, U+0301 组合锐音符> 这个序列。
  • +
+

此外,一些抽象字符不仅有单码位表示方法,还有多码位表示方法。抽象字符 ǵ +可以用单一码位 U+01F5 带锐音符的拉丁小写字母 g 编码,或者用 <U+0067 拉丁小写字母 g, U+0301 组合锐音符> 这个序列。

+

Unicode 之所以要做如此复杂的规定,是因为世界各国的文字系统迥异,直接建立抽象字符到码位的一一映射是不切实际的。而且仅仅编制了码位仍然不够,例如,在 Unicode 中规定了由一或若干个码位构成的字素群的概念:

+
+

字素群 (Grapheme cluster)

+

“应该放在一起”的已编码的文本。例如它们可适用于光标移动和选择。

+
+

然后每一个码位又会在计算机中表示为若干个编码单元。

+
+

编码单元 (Code Unit)

+

可以表示一段已编码文本的最小比特组合。例如,UTF-8、UTF-16 和 UTF-32 分 +别使用 8 比特、16 比特和 32 比特编码单元。比如文本“💣”(U+1F4A3)可 +以编码成四个 UTF-8 编码单元“f0 9f 92 a3”,两个 UTF-16 编码单元“3dd8 a3dc”,一个 UTF-32 编码单元“0001f4a3”。

+
+

这些概念的层次结构如下图所示: +层次结构

+

此外还有用户观感字符 (User-perceived character)字模 (Glyph)等。但与日常编程关系不大。

+

编程语言中的实现

+

字符上节提到的概念都可以是“字符”。所以不同的编程语言对此的处理不尽相同,例如,C#当中的char类型表示一个 UTF-16 编码单元,而 Rust 的 char 类型则表示一个 Unicode 标量

+

(Unicode Scalar Value,代理字符以外的所有码位)。C/C++ 中的 char 类型只表示一个固定长度的整型数据。Go 中的字符表示一个 +码位,为了突出这一性质,它甚至起了一个专门的名字 rune 来表示字符。

+

字符串字符串的编码一般采用 UTF-8 或 UTF-16 实现,采用 UTF-16 编码的语言的索引操作一般返回 编码单元。(而不是大多数人预想中的“字符”概念)

+

但 Python3 比较特殊,它严格区分了“文本”和“数据”,一个str类型并不与特定的编码相关联,而是一个由码位构成的序列。2 +这样就可以保证s[i]总是返回正确的 第i个 码位,而 +len(s)总是返回正确的码位个数。

+

编程语言应当如何支持 Unicode

+

字符的意义

+

字符类型在几乎所有编程语言中都是一个基本类型,一般可以对其进行语义上的操作,例如判断是否为大写字母等。从这个意义上讲,让一个字符变量储存一个编码单元的方案,在逻辑上是不正确的,因为编码单元只是一个和编码相关的概念,并不代表实际的字符。

+

同时,如果一个语言使用字符表示编码单元,又使用了 UTF-16 编码 +(不幸的是, C# 就是这样的语言),那么它的字符将有可能存放损坏的 Unicode 数据。而如果使字符表示一个 码位 (或 标量),则没有这个问题。

+

字符串需要具有什么功能

+

前面提到,Python3 通过专门的设计,保证了对字符串中码位的正确计数和索引。但对码位进行正确计数的操作,在国际化的语境下,几乎没有用处,例如文本

+

Приве́т नमस्ते שָׁלוֹם

+

3中包含了 22 +个码位,但“22”这个数字几乎没有任何用处。相反,这些可能会有用:

+
    +
  • 字素群个数:16。如果你尝试用鼠标选择这个文本,就能感受到。
  • +
  • 编码长度。在 UTF-8 下,这个值是 48byte。
  • +
  • 屏幕上显示的 宽度。这由所使用的字体决定。
  • +
+

结论

+

为什么 UTF-8 不比 UTF-16 差

+
    +
  1. UTF-8 字符串的索引不一定能返回正确的码位, 但 UTF-16 字符串也不能。而且使用 UTF-8 的语言往往注意到了这一点
  2. +
  3. 由于大量纯 ASCII 控制字符(HTML,配置文件等)的存在,UTF-16 在储存空间上并没有什么优势
  4. +
  5. 变长字符编码对常用的操作并没有什么影响。每一个非 ASCII 字符在 UTF-8 +中的编码下,每个字节的值都大于 127,而一个码位编码的起始字节永远不会与其他码位的尾随字节相同。这意味着搜索操作可以以和 ASCII 完全相同的方式进行。4
  6. +
+

为什么 UTF-8 比 UTF-16 好

+
    +
  1. UTF-8 完全兼容 ASCII,正如上一节第 3 点所说的那样。
  2. +
  3. UTF-8 没有字节序的问题。
  4. +
+

另外,使用 UTF-8 的语言(如 Rust 和 Go)比其他语言更多的注意到了 +Unicode 相关的细节。使用 UTF-8,你的程序将能够更能轻松适应世界各国人民输入的各种文本而不至于使程序出错。

+

总之,UTF-8 具有各种好处,而人们长期以来认为 UTF-16 具有的好处都不成立。

+
+

Footnotes

+
1 +

本文大量参考了《UTF-8 遍地开花》

+
+
2 +

虽然文档中没有说明,但可以由此推测 Python 内部字符串编码是 UTF-32。

+
+
3 +

这是一个相当复杂的文本,包含了 Combining character,RTL文本等中国人不熟悉,但在某些地区常见的元素。

+
+
4 +

在 Linux 下运行命令man utf-8就可以获得简明的关于 UTF-8 编码的描述。

+
+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+为什么编程语言总是应该使用UTF-8而不是UTF-16Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/wiki-exemption/blocked.jpg b/wiki-exemption/blocked.jpg new file mode 100644 index 0000000..5c6104f Binary files /dev/null and b/wiki-exemption/blocked.jpg differ diff --git a/wiki-exemption/index.html b/wiki-exemption/index.html new file mode 100644 index 0000000..198804a --- /dev/null +++ b/wiki-exemption/index.html @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + pg999w's blog - 在中国大陆境内编辑维基百科 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + + +
+ +
+

+ 在中国大陆境内编辑维基百科 +

+ +
+ +
+

由于 GFW 的存在,在中国境内使用维基百科必须要透过代理进行访问。然而为了避免恶意破坏,维基百科禁止匿名代理服务器修改词条。如果你的代理不幸处于封禁的 IP 段,那么将会得到类似于这样的警告:

+ +

+

这时就需要注册维基百科的账号,为自己的账号申请维基百科的 IP 封禁豁免。虽然可以尝试向封禁信息所给出的 stewards@wikimedia.org 提交申请,但实际上那只能解除全域封禁。各个语言的维基百科是分别管理封禁列表的,被称为本地封禁。维基百科介绍了IP封禁豁免的类型及作用,可以得知本地封禁豁免才是实际有用的。我们需要分别向中文和英文维基百科提出封禁豁免的申请。

+

显然由于 GFW 的存在,豁免申请在中文维基更为常见。中文维基贴心地给出了教程,照做即可。而英文维基则需要找到 IPECPROXY 的链接,通过这里面提到的邮件进行申请。申请时只需要说清楚自己的用户名,并说明自己是中国大陆用户,不得不使用代理访问维基百科即可。

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+在中国大陆境内编辑维基百科Peng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + + diff --git a/you-should-use-neovim/index.html b/you-should-use-neovim/index.html new file mode 100644 index 0000000..850544e --- /dev/null +++ b/you-should-use-neovim/index.html @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + pg999w's blog - 你应该使用 Neovim + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ + + + + +
+
+ + + +
+

Contents

+
+ +
+
+ + +
+ +
+

+ 你应该使用 Neovim +

+ +
+ +
+

本文旨在说服读者将自己的文本编辑工具从 Vim 转到 Neovim。

+ +

Neovim 是 Vim 编辑器的一个分支。项目开始的时候还是 2014 年,为了解决 Vim 存在的一些长期存在的问题,巴西程序员 Thiago de Arruda Padilha(@tarruda) +启动了这个项目。

+

我从高一起开始使用 Vim,至今也有快有三年了。2017 年年末,经过一番比较,我就转到了 Neovim 。但不久前在与学长的讨论中,我才发现 Neovim 比 Vim 更好的地方不是三言两语就能说清楚的。根据我在 Vim 8.0 刚发布时的印象(我就是在那时转到 Neovim +的),Neovim 和 Vim 的一个最明显的区别就是 Neovim 有一个内置的真正终端,同时它又是一个真正的缓冲区,可以使用跳转到文件、搜索等功能。但是这个相当棒的功能在 Vim 8.1 中已经被实现了。事实上,当初 Neovim 的许多优秀功能,例如异步通信等都已经在 Vim 8 上得到了实现。截止到本文写作之时,Vim 的这两个分支在主要功能上已经没有什么大的区别了。(见下文)

+

然而如果我们不从功能,而从项目的维度上去考虑,我们会发现 Vim 8 的推出,很大程度上是受了 Neovim 的影响,前面提到的异步通信和内置终端,都是在 Neovim 中首先实现的。换言之,在编辑器的开发上面,Neovim 是领先于 Vim 的。这与项目组织方式有关。虽然 Vim 也在 2014 年启用了官方 Github 仓库,但 Vim 的主要开发和讨论还是在邮件列表上。这当然有其历史原因,但 Neovim 完全拥抱 Github 的 +开发方式显然更友好。另外,Neovim 长期在 Google Summer of Code 的列表中,每年都有大学生为 Neovim 项目添加有趣的功能。例如在 2018 年的 GSoC 中, +Utkarsh Maheshwari 对 UI 子系统底层做了修改,使得插件可以创建自定义的浮动窗口。下图是插件 coc.nvim 的效果。

+

coc插件在 Neovim 中的效果

+

基于上述的原因,Neovim 在很多小地方都比背着历史包袱的 Vim 要做得好。例如

+
    +
  1. Neovim 不考虑 Vi 兼容的问题,所以有一个比较好的默认配置,在全新安装的情况下也可以很好地工作;
  2. +
  3. Neovim 遵守 XDG Base Directory 规范
  4. +
  5. Neovim 支持一些现代终端模拟器的功能,例如它可以在插入模式和普通模式下使用不同形状的光标,可以识别粘贴行为并自动进入 paste 模式等。
  6. +
+

另外,Neovim 作为 Vim 的竞争者,同时加快了 Vim 和 Neovim 的发展。如果没有 Neovim 项目的压力,Vim 8 还不知道要等到什么时间才会发布,我们这些用户也只能一直使用几十年前就已经存在的老式 Vim。长远来看,使用 Neovim,增加它的用户量和影响力,是有利于所有人的。

+
+

2022 年 5 月更新

+

时过境迁,如今 Vim 的最新版本是 8.2.4609,而 Neovim 也发布了 0.7 版本。现在来看,虽然 Vim 已经补足了许多功能,但是其开发仍然比 Neovim 要滞后。具体可见如下几点:

+
    +
  • 如今 Neovim 的 Lua 配置已经走上正轨,可以使用 Lua 完成几乎所有配置(无需调用 vim.cmd)。而 Vim9 的正式发布还不知要等到什么时候。
  • +
  • Neovim 中的 Virtual Text 可以让插件在编辑框中显示不能编辑的虚拟文本,这使得像 VS Code 那样的 Inlay hint 成为可能。另外 GitHub 和 OpenAI 开发的 AI 代码补全软件 Copilot 的 Vim 插件也因为使用了 virtual text 功能而成为了 Neovim 独占。而 Vim 的开发者到现在也不太愿意添加这一功能。
  • +
  • Neovim 中提供了良好的 Tree-sitter 支持,而 Vim 则要在 Vim9 之后再考虑这个功能……
  • +
+
+

参考网站

+

[1] https://neovim.io/

+

[2] https://github.com/vim/vim

+

[3] https://zhuanlan.zhihu.com/p/21364426

+ +
+ + + + + + + + + +
+ + 知识共享许可协议 + +
+你应该使用 NeovimPeng Guanwen 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。 + +
+ + + + + +
+ + +
+
+ + + +
+ + + + + + +