c语言奇淫技巧之lambda表达式

何为奇淫技巧?

百科解释:指过于奇巧,让人着迷,却又无益的技艺与制品。

c语言奇淫技巧是指,利用c语言的一些细小的语法特性,来实现一些令人着迷的、眼花缭乱的、看似语言本身不支持的功能。比如:实现c语言封装、继承、多态、lambda表达式、动态包含等。在开发中巧妙运用这些技巧,会让你的程序组织起来更加合理。过于玩弄这些技巧则会令你舍本逐末、忽略了项目中其他更重要的事情。

研究奇淫技巧,是一个充满趣味的过程,也是不断深入语言的过程。通过理解这些奇淫技巧,你会发现你曾经理解的一些基本的语言概念其实并不是真的理解。

c语言实现lambda表达式的原理

我们通常用的c语言语法,是c语言标准规定的语法。然而不同的c编译器在实现标准语法的同时,可能会扩展一些其他的特定语法。这些语法一般都是用在底层的一些库、工具中,如果你经常阅读c语言库,一定能发现它们的踪影。
c语言实现lambda表达式,是基于gcc的一个扩展。而检查你当前的c编译器是否支持这个扩展,其实很简单。

运行下面的程序,输出1则证明你的编译器支持,如果报错则不支持。

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main(){

int a = ({1;});

printf("%d\n",a);

return 0;
}

实现lambda表达式

为了便于大家理解,这里采用递进的方式,渐进式修改代码,方便大家明白。

块级作用域

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int main(){

({
char* s = "hello";
printf("%s\n",s);
});

return 0;
}

上面的运行后输出hello,说明 ({});可以作为一个块级作用域,里面可以运行代码片段。

返回值

令人惊异的是,这个块级作用域还能对外返回一个值。当然,返回的方式不是return,而是直接写返回值,并带上一个分号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main(){

int a = ({
char* s = "hello";
printf("%s\n",s);
10;
});

printf("%d\n",a);

return 0;
}

匿名函数

既然这种方法可以返回一个值,那么我们返回一个函数呢?毕竟函数名就是函数的首地址,答案是肯定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main(){

({
void hello(){
printf("hello\n");
}
hello;
})();


return 0;
}

宏改写匿名函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

#define $(rt,fb) ({ \
rt this fb; \
this; \
})

int main(){

$(void,(){
printf("hello\n");
})();


return 0;
}

这样看是不是有点眼熟呢,不过还是有些美中不足。我们希望这个匿名函数不仅仅只是自执行,还能保存到一个变量中。

用函数指针类型接收匿名函数对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

#define $(rt,fb) ({ \
rt this fb; \
this; \
})

int main(){

//因为匿名函数没有返回值,没有参数,所以函数指针可以定义为:void (*hello)()
void (*hello)() = $(void,(){
printf("hello\n");
});

hello();

return 0;
}

宏改写函数指针定义

上面虽然解决了保存匿名函数的问题,但是确带来的另一个问题,函数指针的定义比较冗长、复杂。因此,可以通过宏定义的方式改写。

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
#include <stdio.h>

#define $(rt,fb) ({ \
rt this fb; \
this; \
})

#define Type(t) typeof(({t _t;_t;}))
#define FType(rt,fp) typeof(({rt (*_this)fp;_this;}))
#define Consumer(t) FType(void,(t))

int main(){

Consumer() hello = $(void,(){
printf("hello\n");
});

hello();

Consumer(int) a = $(void,(int n){
printf("num is %d\n",n);
});

a(10);

Consumer(char*) b = $(void,(char* name){
printf("hello %s\n",name);
});

b("zhangsan");

return 0;
}

lambda表达式库

到这里,我们就可以写一个lambda表达式的头文件了,用到lambda表达式的地方只需要包含这个头文件就可以了。

lambda.h

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
#ifndef _LAMBDA_H_H_
#define _LAMBDA_H_H_

#define Type(t) typeof(({t _t;_t;}))
#define FType(rt,fp) typeof(({rt (*_this)fp;_this;}))

#define lambda(rt,fb) ({ \
rt this fb; \
this; \
})

#define $ lambda

#define Executor FType(void,())

#define Consumer(t) FType(void,(t))
#define BiConsumer(t1,t2) FType(void,(t1,t2))
#define TiConsumer(t1,t2,t3) FType(void,(t1,t2,t3))

#define Supplier(rt) FType(rt,())

#define Function(rt,t) FType(rt,(t))
#define BiFunction(rt,t1,t2) FType(rt,(t1,t2))
#define TiFunction(rt,t1,t2,t3) FType(rt,(t1,t2,t3))

#define UnaryOperator(t) Function(t,t)
#define BinaryOperator(t) BiFunction(t,t,t)

#endif

type.h

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#ifndef _TYPE_H_H_
#define _TYPE_H_H_

#include "lambda.h"

#define Bool short
#define TRUE 1
#define FALSE 0

#define Int int
#define String char*

//-------------------------------------------------
#define IntConsumer Consumer(int)

//-------------------------------------------------
#define IntBiConsumer BiConsumer(int,int)

//-------------------------------------------------
#define IntTiConsumer TiConsumer(int,int,int)

//-------------------------------------------------
#define IntSupplier Supplier(int)
#define StringSupplier Supplier(String)

//-------------------------------------------------
#define IntFunction(rt) Function(rt,int)

//-------------------------------------------------
#define ToIntFunction(t) Function(int,t)

//-------------------------------------------------
#define IntBiFunction(rt) BiFunction(rt,int,int)

//-------------------------------------------------
#define ToIntBiFunction(t1,t2) BiFunction(int,t1,t2)

//-------------------------------------------------
#define IntTiFunction(rt) TiFunction(rt,int,int,int)

//-------------------------------------------------
#define ToIntTiFunction(t1,t2,t3) TiFunction(int,t1,t2,t3)

//-------------------------------------------------
#define IntUnaryOperator UnaryOperator(int)

//-------------------------------------------------
#define IntBinaryOperator BinaryOperator(int)

//-------------------------------------------------
#define Comparator(t) BiFunction(int,t,t)

//-------------------------------------------------
#define Not(t) Function(BOOL,t)

//-------------------------------------------------
#define And(t) BiFunction(BOOL,t,t)

//-------------------------------------------------
#define Or(t) BiFunction(BOOL,t,t)

//-------------------------------------------------
#define Equal(t) BiFunction(BOOL,t,t)

//-------------------------------------------------
#define Test(t) Function(BOOL,t)


#define Template(T,body) typeof(({ \
typedef struct body _TYPE; \
_TYPE _tmp; \
_tmp; \
}))

#define BiTemplate(T1,T2,body) typeof(({ \
typedef struct body _TYPE; \
_TYPE _tmp; \
_tmp; \
}))

#define TiTemplate(T1,T2,T3,body) typeof(({ \
typedef struct body _TYPE; \
_TYPE _tmp; \
_tmp; \
}))

#define Data(T) Template(T,{ \
T data; \
})

#endif

测试

这里为了方便写了另一个头文件,如下:

util.h

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
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef _UTIL_H_H_
#define _UTIL_H_H_

#include <stdio.h>
#include <stdlib.h>
#include "type.h"

#define Block FType(void,())
#define Code(c) do{ \
c \
}while(0)

#define PrintString(s) printf("%s",s)
#define PrintStringLn(s) printf("%s\n",s)

#define PrintInt(d) printf("%d",d)
#define PrintIntLn(d) printf("%d\n",d)

#define PrintBool(b) Code( \
if(b == TRUE){ \
printf("true"); \
}else{ \
printf("false"); \
} \
)

#define PrintBoolLn(b) Code( \
if(b == TRUE){ \
printf("true\n"); \
}else{ \
printf("false\n"); \
} \
)

#define Malloc(t,n) (t)malloc(sizeof(t) * n)

String Concat(String s1,String s2){
Int len = strlen(s1) + strlen(s2) + 1;
String res = Malloc(String,len);

sprintf(res,"%s%s",s1,s2);

return res;
}

#endif

下面是对于lambda表达式库的一些测试程序

测试1

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
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include "lambda.h"
#include "util.h"
#include "type.h"

int main(){

Block hello1 = $(void,(){
printf("hello\n");
});

hello1();

Consumer(char*) hello2 = $(void,(char *name){
printf("hello %s\n",name);
});

hello2("zhangsan");

BiConsumer(char*,int) hello3 = $(void,(char *name,int age){
printf("hello %s %d\n",name,age);
});

hello3("lisi",22);

Function(int,int) fact = $(int,(int n){
int res = 1;

for(int i = 1;i <= n;i++){
res *= i;
}

return res;
});

printf("%d\n",fact(5));

BiFunction(int,int,int) sum = $(int,(int a,int b){
return a + b;
});

printf("%d\n",sum(4,7));

return 0;
}

输出结果如下:

1
2
3
4
5
hello
hello zhangsan
hello lisi 22
120
11

测试2

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
#include <stdio.h>
#include "lambda.h"
#include "util.h"
#include "type.h"

int main(){

Code(

StringSupplier helloSupplier = $(String,(){
return "hello ";
});

Consumer(String) out = $(void,(String s){
PrintStringLn(s);
});

String name = "zhangsan";
out(Concat(helloSupplier(),name));

);


return 0;
}

输出结果如下:

1
hello zhangsan

你的鼓励,是我前进的动力!