编译时代码

按照V语言作者的说法,为了保持V语言的简单,不会加入像C语言那样的预处理器,而是通过编译时代码来实现类似的功能。

编译时就是在编译阶段,根据编译时代码,动态生成代码。编译时代码使用最多的场景就是动态生成指定平台的代码,其他平台的代码不会生成。

V语言中编译时代码以$开头。

条件编译

条件编译是编译时代码最主要的应用场景:根据编译时条件,动态生成指定平台的代码,其他平台的代码不会生成,在C语言中使用了预处理器来实现。

目前的条件编译有2种主要方式:

1.根据源文件名后缀来实现条件编译

2.根据代码中的$if来实现条件编译

按源文件后缀名进行条件编译

源文件后缀包含了2个维度的条件编译:

  • 通用源文件

    后缀名
    编译条件

    只有 .v

    所有操作系统,所有后端都参与编译

  • 操作系统(os)

    后缀名
    编译条件

    _default

    默认的,表示所有操作系统都参与编译,比如file_default.c.v.同个目录中,如果同时存在默认的和平台特有的,平台特有的文件会参与编译,默认的被忽略

    _nix

    linux,unix,darwin,solaris下才会参与编译,或者说是非windows

    _macos或 _darwin

    mac下才会参与编译

    _linux

    linux下才会参与编译

    _solaris

    solaris下才会参与编译

    _plan9

    plan9下才会参与编译

    _windows

    windows下才会参与编译

    _android

    android平台下才会参与编译

    _ios

    ios平台下才会参与编译

    _bare

    编译成裸机(metal)环境运行代码,才会参与编译,不进行排列组合

  • 编译器后端(backend)

    后缀名
    编译器后端

    .c

    C语言为编译器后端时才会参与编译

    .js

    js语言为编译器后端时才会参与编译

    .native

    直接生成x64机器码时才会参与编译

    以上2个维度排列组合:

    file.v	#所有操作系统,所有编译后端都参与编译
    file.c.v	#所有操作系统,C编译后端才参与编译,不会被特定平台覆盖,而是都编译
    file.js.v	#所有操作系统,js编译后端才参与编译,不会被特定平台覆盖,而是都编译
    file.native.v #所有操作系统,native编译后端才参与编译
    
    file_default.c.v #同目录如果存在特定平台的C后端文件,此文件会被忽略,不参与编译
    file_linux.c.v
    file_macos.c.v
    file_windows.c.v
    file_windows.js.v
    file_windows.x64.v
    ...

    正常情况下,在同一个模块中函数是不允许重复定义的,但是可以在.c.v或.js.v重复定义.v已经定义过的函数,.c.v或.js.v的同名函数会优先被执行,也就是覆盖了.v定义的函数。

    这样的好处是可以在.v定义通用版本的函数,在.c.v定义针对C后端的函数,在.js.v定义针对js后端的函数。

    mymodule/wrapper.v

    module mymodule
    
    pub fn value() int {
    	return 666
    }

    mymodule/wrapper.c.v

    module mymodule
    
    pub fn value() int {
    	return 123
    }

    mymodule/wrapper.js.v

    module mymodule
    
    pub fn value() int {
    	return 456
    }

    主模块的main.v

    module main
    
    import mymodule
    
    fn main() {
    	println(mymodule.value())	//C编译器后端输出123
    }

条件编译选项

内置条件编译选项

以下内置的条件编译选项,可以在代码中使用:

OS
Compilers
Platforms
Other

windows, linux, macos,plan9

gcc, tinyc

amd64, aarch64

debug, prod, test

mac, darwin, ios,

clang, mingw

x64, x32

js, glibc, prealloc

android,mach, dragonflytermux

msvc

little_endian

no_bounds_checking

gnu, hpux, haiku, qnx

cplusplus

big_endian

freestanding

solaris, linux_or_macos

判断是否使用了-cg,进入调试模式

判断是否在测试代码中执行

判断平台是32位还是64位

判断平台使用的字节序是小字节序,还是大字节序

判断是否-prod生产编译

自定义编译选项

除了内置的条件编译选项,也可以识别自定义条件编译选项。

使用-d或-define来自定义编译选项,并且可以在代码中接收选项的传入值。

要特别注意的是:自定义条件编译选项名后面一定要加一个问号。

编译时,传递自定义条件编译变量:

同时使用多个自定义编译选项:

编译时反射

$for用来实现反射的效果,目前只实现了结构体的反射,可以在运行时获得某一个结构体所有字段和方法的信息。

遍历结构体字段,返回字段信息数组:[]FieldData

遍历结构体方法,返回方法信息数组:[]FunctionData

编译时动态字段赋值

编译时获取结构体字段信息

可以在$for循环中获取结构体字段的信息:

可获取的字段的信息,查看vlib/builtin.v源文件中的FieldData结构体:

获取结构体选项类型字段值

编译时动态调用方法

编译时判断泛型类型

可以使用编译时判断泛型的具体类型:

编译时全局变量

内置了开发和测试时需要的几个编译时全局变量,方便编译和测试使用:

编译时获取环境变量

可以使用$env()编译时函数获取环境变量。运行时代码中os.get_env()函数也可以实现相同的效果。

比较特别的是$env()也可以在#flay和#include等C宏中使用,让C宏定义更灵活。

编译时获取pkgconfig配置文件

可以使用$pkgconfig()编译时函数,来判断配置文件是否存在。

具体代码参考:集成C代码库章节

编译时嵌入静态文件

可以使用$embed_file()编译时函数,把各种类型的文件在编译时嵌入到二进制文件中,更方便编译打包成单一可执行文件,方便部署,目前vweb框架中有使用到。

$embed_file()函数返回的类型为EmbedFileData,详细的使用可参考:vlib/v/embed_file/embed_file.v源文件。

如果没有特别指定,使用-prod进行生产编译时,$embed_file()函数会使用zlib对嵌入二进制的静态文件进行压缩。

编译时模板渲染

V语言内置了一个简单的txt和html模板,可以通过$tmpl编译时函数,进行渲染。

a.txt 模板文件内容

编译时解析v.mod文件

在编译阶段,如果需要解析v.mod的文件内容,可以导入v.mod模块,解析v.mod文件的内容。

编译时错误与警告

V语言内置2个编译时函数:compile_error和compile_warn,专门用来抛出编译时的错误和警告。

最后更新于

这有帮助吗?