编译时反射
编译时就是编译器生成C代码时,只会生成指定平台的代码,其他平台的C代码不会生成.
有别于运行时代码,不管是那个平台的代码都会生成对应的C代码.
在V语言中所有的编译时代码都是以$开头的.
动态获取字段和方法
$for用来实现反射的效果,目前只实现了结构体的反射,可以在运行时获得某一个结构体所有字段和方法的信息
遍历结构体字段,返回字段信息数组:[]FieldData
1. FieldData {
2. name: 'a' //字段名称
3. attrs: [] //字段注解
4. is_pub: false //是否公共
5. is_mut: false //是否可变
6. typ: 18 //字段类型
7. }
遍历结构体方法,返回方法信息数组:[]FunctionData
1. FunctionData {
2. name: 'int_method1' //方法名称
3. attrs: [] //方法注解
4. args: [] //方法参数
5. return_type: 7 //方法返回类型
6. typ: 0 //未知
7. }
1. struct App {
2. a string
3. b string
4. mut:
5. c int
6. d f32
7. pub:
8. e f32
9. f u64
10. pub mut:
11. g string
12. h byte
13. }
15. ['foo/bar/three']
16. fn (mut app App) run() {
17. }
19. ['attr2']
20. fn (mut app App) method2() {
21. }
23. fn (mut app App) int_method1() int {
24. return 0
25. }
27. fn (mut app App) int_method2() int {
28. return 1
29. }
31. fn (mut app App) string_arg(x string) {
32. }
34. fn no_lines(s string) string {
35. return s.replace('\n', ' ')
36. }
38. fn f1() {
39. println(@FN)
40. methods := ['run', 'method2', 'int_method1', 'int_method2', 'string_arg']
41. $for method in App.methods { //遍历结构体所有方法
42. println(' method: $method.name | ' + no_lines('$method'))
43. assert method.name in methods
44. }
45. }
47. fn f2() {
48. println(@FN)
49. $for method in App.methods { //遍历结构体所有方法
50. println(' method: ' + no_lines('$method'))
51. $if method.typ is fn () {
52. assert method.name in ['run', 'method2']
53. }
54. $if method.return_type is int {
55. assert method.name in ['int_method1', 'int_method2']
56. }
57. $if method.args[0].typ is string {
58. assert method.name == 'string_arg'
59. }
60. }
61. }
63. fn main() {
64. println(@FN)
65. $for field in App.fields { //遍历结构体所有字段
66. println(' field: $field.name | ' + no_lines('$field'))
67. $if field.typ is string {
68. assert field.name in ['a', 'b', 'g']
69. }
70. $if field.typ is f32 {
71. assert field.name in ['d', 'e']
72. }
73. if field.is_mut {
74. assert field.name in ['c', 'd', 'g', 'h']
75. }
76. if field.is_pub {
77. assert field.name in ['e', 'f', 'g', 'h']
78. }
79. if field.is_pub && field.is_mut {
80. assert field.name in ['g', 'h']
81. }
82. }
83. f1()
84. f2()
85. }
动态字段赋值
1. struct Foo {
2. immutable int
3. mut:
4. test string
5. name string
6. }
8. fn comptime_field_selector_read<T>() []string {
9. mut t := T{}
10. t.name = '2'
11. t.test = '1'
12. mut value_list := []string{}
13. $for f in T.fields {
14. $if f.typ is string {
15. value_list << t.$f.name
16. }
17. }
18. return value_list
19. }
21. fn test_comptime_field_selector_read() {
22. assert comptime_field_selector_read<Foo>() == ['1', '2']
23. }
25. fn comptime_field_selector_write<T>() T {
26. mut t := T{}
27. $for f in T.fields {
28. $if f.typ is string {
29. t.$f.name = '1'
30. }
31. $if f.typ is int {
32. t.$f.name = 1
33. }
34. }
35. return t
36. }
38. fn test_comptime_field_selector_write() {
39. res := comptime_field_selector_write<Foo>()
40. assert res.immutable == 1
41. assert res.test == '1'
42. assert res.name == '1'
43. }
45. struct Foo2 {
46. f Foo
47. }
49. fn nested_with_parentheses<T>() T {
50. mut t := T{}
51. $for f in T.fields {
52. $if f.typ is Foo {
53. t.$(f.name).test = '1'
54. }
55. }
56. return t
57. }
59. fn test_nested_with_parentheses() {
60. res := nested_with_parentheses<Foo2>()
61. assert res.f.test == '1'
62. }
动态调用方法
1. struct TestStruct {}
3. fn (t TestStruct) one_arg(a string) {
4. println(a)
5. }
7. fn (t TestStruct) two_args(a string, b int) {
8. println('$a:$b')
9. }
11. fn main() {
12. t := TestStruct{}
13. $for method in TestStruct.methods { // 获取结构体的所有方法
14. if method.name == 'two_args' {
15. t.$method('hello', 42) // 动态调用方法
16. }
17. }
18. }
1. struct MyStruct {}
3. struct TestStruct {}
5. fn (t TestStruct) three_args(m MyStruct, arg1 string, arg2 int) {
6. println('$arg1,$arg2')
7. }
9. fn main() {
10. t := TestStruct{}
11. m := MyStruct{}
12. args := ['one' '2'] //数组参数
13. $for method in TestStruct.methods {
14. if method.name == 'three_args' {
15. t.$method(m, args) //动态调用方法时,也可以传递一个数组,会自动解构展开
16. }
17. }
18. }
编译时预置的全局变量
1. module main
3. fn main() {
4. println('module: ${@MOD}') //当前模块
5. println('fn: ${@FN}') //当前函数
6. println('sturct: ${@STRUCT}') //当前结构体
7. println('method: ${@METHOD}') //当前方法
8. println('vexe: ${@VEXE}') //当前V编译器命令行可执行文件
9. println('vexeroot: ${@VEXEROOT}') //当前V编译器命令行所在的目录
10. println('file: ${@FILE}') //当前源代码文件名
11. println('line: ${@LINE}') //当前代码所在的行
12. println('column: ${@COLUMN}') //当前代码在当前行中的列数
13. println('vhash: ${@VHASH}') //当前V命令行编译时的hash
14. println('vmod_file: ${@VMOD_FILE}') //当前文件所处项目的v.mod文件内容
15. println('vmodroot: ${@VMODROOT}') //当前文件所处项目的v.mod文件所在的目录
16. }
编译时获取环境变量
可以使用$env函数获取环境变量
运行时代码中os.get_env函数也可以实现相同的效果
比较特别的是,$env也可以在#flay和#include等C宏中使用,让C宏的定义更灵活
1. module main
3. //可以在C宏语句中使用,让C宏的定义更灵活
4. #flag linux -I $env('JAVA_HOME')/include
6. fn main() {
7. compile_time_env := $env('PATH')
8. println(compile_time_env)
9. }
编译时嵌入文件
可以使用$embed_file编译时函数,把各种类型的文件在编译时嵌入到二进制文件中,更方便编译打包成单一可执行文件,方便部署,目前vweb框架中有使用到.
1. module main
3. import os
5. fn main() {
6. mut embedded_file := $embed_file('v.png')
7. mut fw := os.create('exported.png') or { panic(err) }
8. fw.write_bytes(embedded_file.data(), embedded_file.len)
9. fw.close()
10. }
编译模板
V内置了一个简单的txt和html模板,可以通过$tmpl编译时函数,进行渲染
1. fn build() string {
2. name := 'Peter'
3. age := 25
4. numbers := [1, 2, 3]
5. return $tmpl('1.txt') //读取模板文件,然后把变量替换到模板中对应的同名变量
6. }
8. fn main() {
9. println(build())
10. }
1.txt模板文件内容
1. name: @name //@开头的都是模板文件中需要替换的同名变量
3. age: @age
5. numbers: @numbers
7. @for number in numbers //也可以遍历数组类型变量
8. @number
9. @end
