在本章中,我们将研究各种非常有用且使用越来越广泛的非 OOP 结构。

λ函数

lambda 函数是一个匿名函数,它可以自由引用包含它的函数中的局部变量(包括参数变量)。 除了编译器负责为 lambda 函数生成内部名称以外,Lambda 的实现与 Pascal 的嵌套函数一样。 有几种不同的方法可以实现 lambda 函数(更多相关信息,请参阅 Wikipedia on Nested Functions)。

1
2
3
4
5
int foo(int a)
{
auto function = [a](int x) { return x + a; };
return function(10);
}

这里的问题是 lambda 函数引用了调用者的一个局部变量 a。 这可以通过将局部变量作为隐式参数传递给 lambda 函数来轻松解决:

1
2
3
4
5
6
7
8
9
define internal i32 @lambda(i32 %a, i32 %x) {
%1 = add i32 %a, %x
ret i32 %1
}

define i32 @foo(i32 %a) {
%1 = call i32 @lambda(i32 %a, i32 10)
ret i32 %1
}

或者,如果 lambda 函数使用多个变量,您可以将它们包装在一个结构中,然后将该结构体的指针传递给 lambda 函数:

1
2
3
4
5
6
7
8
extern int integer_parse();

int foo(int a, int b)
{
int c = integer_parse();
auto function = [a, b, c](int x) { return (a + b - c) * x; };
return function(10);
}

转换为llvm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
; ModuleID = 'lambda_func_1_cleaned.ll'
source_filename = "lambda_func_1_cleaned.ll"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

%lambda_args = type { i32, i32, i32 }

declare i32 @integer_parse()

define i32 @lambda(%lambda_args* %args, i32 %x) {
%1 = getelementptr %lambda_args, %lambda_args* %args, i32 0, i32 0
%a = load i32, i32* %1
%2 = getelementptr %lambda_args, %lambda_args* %args, i32 0, i32 1
%b = load i32, i32* %2
%3 = getelementptr %lambda_args, %lambda_args* %args, i32 0, i32 2
%c = load i32, i32* %3
%4 = add i32 %a, %b
%5 = sub i32 %4, %c
%6 = mul i32 %5, %x
ret i32 %6
}

define i32 @foo(i32 %a, i32 %b) {
%args = alloca %lambda_args
%1 = getelementptr %lambda_args, %lambda_args* %args, i32 0, i32 0
store i32 %a, i32* %1
%2 = getelementptr %lambda_args, %lambda_args* %args, i32 0, i32 1
store i32 %b, i32* %2
%c = call i32 @integer_parse()
%3 = getelementptr %lambda_args, %lambda_args* %args, i32 0, i32 2
store i32 %c, i32* %3
%4 = call i32 @lambda(%lambda_args* %args, i32 10)
ret i32 %4
}

显然,你可以做一些灵活的变化:

  • 您可以将所有隐式参数作为显式参数传递。
  • 您可以在结构中将所有隐式参数作为显式参数传递。
  • 您可以传入一个指向调用者帧的指针,并让 lambda 函数从输入帧中提取参数和局部变量。

生成器

生成器是一个函数,它以某种方式重复生成一个值,使得函数的状态在函数的重复调用中保持不变; 这包括函数在产生值时的局部偏移量。

实现生成器最直接的方法是将其所有状态变量(参数、局部变量和返回值)包装到一个 ad-hoc 结构中,然后将该结构的地址传递给生成器。

待续…