什么是“thunk”?

165

我在编程中(特别是在C++领域)见过它的使用,但我不知道它是什么。可能它是一个设计模式,但我可能错了。能否给出一个好的thunk的例子?


13
顺便提一下,thunk 有时也被称为“trampoline”(在通用情况下,可能不适用于 C++ 领域)。 - Michael Burr
@MichaelBurr,我所见过“trampoline”这个术语的唯一上下文是Detours,在那种情况下,一个trampoline不是thunk。 - Sam Hobbs
1
术语是一种没有具体定义的东西,因此其定义会有所不同。 - Sam Hobbs
12个回答

175

Thunk通常指的是一小段代码,作为函数调用时运行并完成一些小任务,然后跳转到另一个位置(通常是一个函数),而不是返回给调用者。假设JUMP目标是一个普通函数,当它返回时,它将返回给thunk的调用者。

thunk可以用于有效地实现许多有用的功能。

  • 协议转换-- 当从使用一种调用约定的代码调用使用不同调用约定的代码时,可以使用thunk适当地转换参数。这仅在返回约定兼容的情况下才有效,但通常如此。

  • 虚函数处理-- 在C++中调用多重继承基类的虚函数时,需要修复this指针以使其指向正确的位置。可以使用thunk来完成此操作。

  • 动态闭包-- 当构建动态闭包时,闭包函数需要能够获取创建它的上下文。可以构建一个小的thunk(通常在堆栈上)来设置上下文信息到某个寄存器中,然后跳转到实现闭包函数的静态代码。这里的thunk实际上为函数提供了一个或多个隐藏的额外参数,这些参数并没有由调用方提供。


21
这是最好的解释,因为它解释了thunk究竟是什么,而不是通常在实现不同事物的典型用例中通常做什么。其他回答过于关注这些特定的实现,而忽略了这个概念的一般性想法。 - SasQ
不确定其他编译器如何,但Visual Studio特别喜欢thunks。据我所知,它使用:adjustor thunks(用于调整“this”),默认/复制构造函数闭包(用于更好地将用户提供的带有默认参数的CRT集成到默认值中,主要用于DLL导出或构建数组),vcall thunks(确保指向成员函数的指针与虚拟函数正常工作),vtordisp thunks(适用于从虚拟基类继承和覆盖虚拟函数,并且还具有用户提供的构造函数和/或析构函数的类),本机包装器(用于调用托管的C++/CLI)。 - Justin Time - Reinstate Monica
此程序涉及到一些常见的Microsoft thunk,包括从本地ISO C++代码中导出函数以及所谓的“返回用户定义类型(UDT)”(似乎是用于调整运算符返回的用户定义类型的占位符,但我不确定如何生成它;我认为它已经过时了)。可能还有其他的一些,但可以肯定的是,Microsoft thunk是无处不在的;笛卡尔会为此感到自豪。 - Justin Time - Reinstate Monica

88

在计算机科学中,“thunk”这个词至少有三个相关的含义。一个“thunk”可能是:

  • 执行延迟计算的一段代码(类似于闭包)
  • 某些虚拟函数表实现的特性(类似于包装函数)
  • 将机器数据从一种系统特定形式映射到另一种形式,通常是为了兼容性的原因。

我通常看到它被用在第三个上下文中。

http://en.wikipedia.org/wiki/Thunk


3
有趣;我通常听到第二种形式,但我猜这取决于你更经常从事什么样的工作。 - Michael Mrozek
具体而言,这与自动生成非常短的机器码块有关 - 即使第一种情况通常只是为预编译实现函数提供上下文。 - Simon Buchan
我认为 #1 指的是 Chris Dodds回答,但不是那么宽泛:它不仅包括延迟,而且包括任何“东西”。因此,在今天的时代,我猜测大多数人(网站开发人员)会认为 #1 是常见用法。 - Ben Butterworth

29
该术语“thunk”最初指的是Royal Radar Establishment在其Algol60编译器中使用的按名称传递机制。通常,它是指在引用表面上静态对象时引发动态行为的任何方式。这个术语是由Brian Wichmann发明的,当被要求解释按名称传递时,他说:“好吧,你去加载内存中的值,然后突然 - thunk - 你在评估一个表达式。”
thunks已经被放入硬件中(比如KDF9、Burroughs大型机)。有几种软件实现它们的方法,都非常依赖于机器、语言和编译器。
该术语已经被推广到超出按名称传递范畴,包括任何表面上或名义上静态数据引用引发动态行为的情况。相关术语包括“trampoline”和“future”。

3
谢谢您提供这个词源。我讨厌编程术语,它们的定义似乎是在表格中任意查找。 - Ross Rogers
“从内存中加载值,然后突然间——咚——你正在评估一个表达式。”这句话出自哪里? - Shafik Yaghmour

17

8
这个问题已经在SO上提出过了,参见: 什么是“thunk”,在Scheme中或通常的情况下使用? 从我所知道的来看,它类似于lambda语句,其中您可能不想在需要评估之前返回值;或者它也可以与属性getter进行比较,其设计通过执行一些代码以返回值而具有变量形式的接口形式,但同时具有多态行为,可以通过继承或交换函数指针来进行交换,该函数指针将在运行时基于编译时或环境特性进行评估和返回值。

8

在使用中存在相当大的差异。几乎普遍情况下,thunk是一个(至少在概念上)非常小而简单的函数。它通常是某种适配器,为您提供与某些数据、另一个函数等正确的接口,但至少被视为几乎不做其他事情。

这几乎像一种语法糖,只是(至少通常使用时)语法糖应该使事物看起来符合人类读者的期望,而thunk是为了使某些东西看起来符合编译器想要看到的方式。


2
听起来对我来说像是语法糖的反义词 :) - Laserallan
2
编译器的语法糖吗?几乎是,但又不完全像是语法糖。 - Duncan
2
也许是一个语法纠正器? - Justin Time - Reinstate Monica

6
我很失望地发现,在与我自己历史上的实际使用相匹配的“计算机科学”通用定义中,找不到这个术语的定义。我能记得的第一个真实的遭遇是在OS/2时代和16-32位转换中才被称为“thunking”。今天,“thunking”的应用就像讽刺用法一样。
我的粗略理解是,thunk是一个什么都不做或者只是路由到某些基本边界的存根例程,就像上述历史案例中一样。
所以,感觉就像从一个环境掉落到另一个环境中,(作为隐喻或比喻)发出“thunk”的声音。

2
有趣的提示。我也在想这个词的实际词源,我想象人们玩“丛林传话”,其中流中的一个人默默地(在许多情况下是不知情的)改变了信息。 - SasQ

5

我会去查一下,但我认为thunking是32位处理器运行传统16位代码的过程。

以前我常用它作为类比,说明当你和笨人说话时,必须限制讲话速度和使用词语。

是的,在维基百科链接中有提到(关于32位部分,而不是我的nerdalogy)。

https://en.wikipedia.org/wiki/Thunk

许多关于互操作性的文献都与各种Wintel平台有关,包括MS-DOS,OS/2,Windows和.NET,以及从16位到32位内存寻址的过渡。随着客户从一个平台迁移到另一个平台,thunks对于支持为旧平台编写的遗留软件至关重要。

2
我所知道的“thunk”最早的使用是在50年代末,指的是Algol60中函数调用时按名称传递参数的评估。Algol最初是一种规范语言,而不是编程语言,如何在计算机上实现按名称传递参数存在一些问题。
解决方案是传递本质上是lambda的入口点。当被调用者评估参数时,控制跌落 - thunk! - 到调用者的上下文中,在那里评估lambda并将其结果成为被调用者参数的值。
在标记硬件(例如Burroughs机器)中,评估是隐式的:参数可以作为数据值传递,就像普通的按值传递,或者通过thunk进行按名称传递,具有不同的标记在参数元数据中。加载操作硬件检查标记,并返回简单值或自动调用lambda thunk。

0
《新黑客词典》的早期版本声称thunk是一个不带参数的函数,它是一个特别棘手问题的简单深夜解决方案,"thunk"被认为是"think"的过去式,因为他们应该很久以前就想到了这个解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接