V语言11写文档
类似go.从文档串中自动生成.函数/类型/常数的文档必须放在其上1行.
//清理所有
fn clearall() {
}
注释,必须以自己名字开头.多行用多个单行注释.注释最好是现在时.
模块简介,应是模块名后的第1个注释.v doc net.http来生成文档.
工具
v fmt 文件.v格式化文件.一次运行,非常便宜,v fmt -w(每次保存时运行)约30毫秒.
拉代码时,v fmt -w file.v.
用v -profile profile.txt run file.v来检查性能.产生性能文件供你分析.有四列:调用次数|总时间|平均时间|函数名.
sort -n -k3 profile.txt|tail这样来按第3列排序.
用秒表显式测试:
import time
fn main() {
sw := time.new_stopwatch({})
println("你好")
println("花费: ${sw.elapsed().nanoseconds()}ns")
}
高级主题
运行时转存表达式
用dump(expr)转存表达式.
fn factorial(n u32) u32 {
if dump(n <= 1) {
return dump(1)
}
return dump(n * factorial(n - 1))//转存
}
fn main() {
println(factorial(5))
}
这样:v run factorial.v.转存会跟踪源位置,表达式,表达式的值.
内存不安全代码
写低级但高效代码,V要求标记出不安全的代码.
序号 | 可能不安全动作 |
|---|---|
1 | 指针运算 |
2 | 指针索引 |
3 | 从不匹配类型转换为指针 |
4 | 调用某些free,strlen和strncmp等C函数 |
用unsafe来包装它.
//分配未初化2字节并返回到它的引用
mut p := unsafe { malloc(2) }
p[0] = `h` //指针索引,仅在`不安全`块允许
unsafe {
p[0] = `h` // OK
p[1] = `i`
}
p++ //指针算术,仅在`不安全`块允许
unsafe {
p++ // OK
}
assert *p == `i`
内存安全的表达式不要放在不安全块中,这样用不安全的原因,就很清晰.不安全块代码中不应有安全代码.
你怀疑程序违反内存安全,可以检查不安全块行为.
正在进行中.
带引用字段构
带引用字段要求显式给引用字段赋初值,除非构已定义初值.
未来不支持零值引用/空针.现在在注意置0是不安全的,会导致恐慌.
struct Node {
a &Node
b &Node = 0 //自动初化为0,使用时小心
}
//必须初化引用字段,除非如b一样,已声明
// Zero (0)没问题,但要小心,是空针.
foo := Node{
a: 0
}
bar := Node{
a: &foo
}
baz := Node{
a: 0
b: 0
}
qux := Node{
a: &foo
b: &bar
}
println(baz)
println(qux)
sizeof和__offsetof.前者sizeof(Type)按字节给定大小,
后者__offsetof(Struct, field_name)像这样给定字段偏移.
struct Foo {
a int
b int
}
assert sizeof(Foo) == 8
assert __offsetof(Foo, a) == 0
assert __offsetof(Foo, b) == 4
从V调用C:
#flag -lsqlite3
#include "sqlite3.h"
...C系接口...
//https://www.sqlite.org/quickstart.html,示例
fn my_callback(arg voidptr, howmany int, cvalues &&char, cnames &&char) int {
unsafe {
for i in 0 .. howmany {
print("| ${cstring_to_vstring(cnames[i])}: ${cstring_to_vstring(cvalues[i]):20} ")
}
}
println("|")
return 0
}
fn main() {
db := &C.sqlite3(0)
C.sqlite3_open(c"users.db", &db)
query := "select count(*) from users"
stmt := &C.sqlite3_stmt(0)
C.sqlite3_prepare_v2(db, &char(query.str), -1, &stmt, 0)
C.sqlite3_step(stmt)
nr_users := C.sqlite3_column_int(stmt, 0)
C.sqlite3_finalize(stmt)
println("数据库中有$nr_users用户.")
//
error_msg := &char(0)
query_all_users := "select * from users"
rc := C.sqlite3_exec(db, &char(query_all_users.str), my_callback, voidptr(7), &error_msg)
if rc != C.SQLITE_OK {
eprintln(unsafe { cstring_to_vstring(error_msg) })
C.sqlite3_free(error_msg)
}
C.sqlite3_close(db)
}
flag,传递C编译标志.在V文件开头.
标志 | 意思 |
|---|---|
-I | 包含路径 |
-l | 库名 |
-L | C库搜索路径 |
-D | 编译时变量 |
不同目标用不同标志:可用有:linux,darwin,freebsd,及windows
每个标志,必须单独一行:
#flag linux -lsdl2
#flag linux -Ivig
#flag linux -DCIMGUI_DEFINE_ENUMS_AND_STRUCTS=1
#flag linux -DIMGUI_DISABLE_OBSOLETE_FUNCTIONS=1
#flag linux -DIMGUI_IMPL_API=
控制台中,你可用-cflags来传递自定义标志,用-cc来改编译器,如-cc gcc-9 -cflags -fsanitize=thread.
可在终端中定义VFLAGS来存储-cc与-cflags标志,避免重复定义他们.
属性
在函数/构/枚声明前,作用于该声明.
//调用时,过时警告
[deprecated]
fn old_function() {
}
//自定义消息
[deprecated: "用新 new_function() 替代"]
fn legacy_function() {}
//会内联该函数
[inline]
fn inlined_function() {
}
//构在堆中分配只能用作引用
[heap]
struct Window {
}
//如果标志为假,则不编译
// 用`v -d flag`
[if debug]
fn foo() {
}
fn bar() {
foo() //未传递`-d debug`,则不调用
}
//函数返回前,垃集不释放内存
[keep_args_alive]
fn C.my_external_function(voidptr, int, voidptr) int
//必须在不安全块,
[unsafe]
fn risky_business() {
//检查代码及前条件
unsafe {
// 不检查,指针算术,访问联,调用`不安全`
// 最好最小化不安全块代码
}
//仍检查这里
}
//本函数,手动释放,程序员负责
[manualfree]
fn custom_allocations() {
}
//C互操作,C方式
[typedef]
struct C.Foo {
}
// 传递Win32 API回调
[windows_stdcall]
fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int)
//导入gg,ui图形库时,图窗占优势,不创建控制台
//有效禁止`打印行`语句,显式创建控制台,仅在`主`前有效.
[console]
fn main() {
}
允许goto跳至标签.要求不安全,要尽量少用.
交叉编译
v -os windows ./v -os linux .目前不支持macOS.
热加载
module main
import time
import os
[live]
fn print_message() {
println("运行本程序时修改")
}
fn main() {
for {
print_message()
time.sleep(500 * time.millisecond)
}
}
v -live message.v.目前,运行程序时不能修改类型.
转换C到V
#include "stdio.h"
int main() {
for (int i = 0; i < 10; i++) {
printf("你好,世界\n");
}
return 0;
}//转成下面
fn main() {
for i := 0; i < 10; i++ {
println("你好,世界")
}
}
C库中生成包装器:v wrapper c_code/libsodium/src/libsodium.优秀C代码,可转译至V.
序号 | 好处 |
|---|---|
1 | 用1种语言开发代码,更安全,更容易 |
2 | 交叉编译更简单 |
3 | 不需要各种标志 |
内联汇编
a := 100
b := 20
mut c := 0
asm amd64 {
mov eax, a
add eax, b
mov c, eax
; =r (c) as c // output
; r (a) as a // input
r (b) as b
}
println("a: $a") // 100
println("b: $b") // 20
println("c: $c") // 120
有限操作符重载:
struct Vec {
x int
y int
}
fn (a Vec) str() string {
return "{$a.x, $a.y}"
}
fn (a Vec) + (b Vec) Vec {
return Vec{a.x + b.x, a.y + b.y}
}
fn (a Vec) - (b Vec) Vec {
return Vec{a.x - b.x, a.y - b.y}
}
fn main() {
a := Vec{2, 3}
b := Vec{4, 5}
mut c := Vec{1, 2}
println(a + b) // "{6, 8}"
println(a - b) // "{-2, -2}"
c += a
println(c) // "{3, 5}"
}
用来改进可读性,为了安全性/维护性,限制:
序号 | 限制 |
|---|---|
1 | 仅能重载+,-,*,/,%,<,>,==,!=,<=,>= |
2 | 编译器重载==/!=,但你可覆盖 |
3 | 禁止符号函数中调用其他函数 |
4 | 符号函数,不能修改参数 |
5 | 用</==时,必须返回极. |
6 | 定义了==和<后,自动生成!=,>,<=及>= |
7 | 两边参数类型必须相同 |
8 | 定义操作符时,自动生成*=,+=,/=,且必须返回相同类型 |
编译时反射
允许你对任何数据格式创建序化器.V有编译时if/for.
// TODO: 未完全实现
struct User {
name string
age int
}
// T应仅传递构名
fn decode<T>(data string) T {
mut result := T{}
// 编译时`for`循环
// T.fields给出字段元数据类型数组
$for field in T.fields {
$if field.typ is string {
// $(string_expr)产生标识符
result.$(field.name) = get_string(data, field.name)
} $else $if field.typ is int {
result.$(field.name) = get_int(data, field.name)
}
}
return result
}
// `decode<User>` 生成:
fn decode_User(data string) User {
mut result := User{}
result.name = get_string(data, "name")
result.age = get_int(data, "age")
return result
}
优化
用-prod编译时,如果给定提示,还可进一步优化:
属性 | 意思 |
|---|---|
[inline] | 内联 |
[direct_array_access] | 转为C数组操作来直接访问,省略检查边界,但不安全. |
if _likely_(bool expression) { | 该分支很可能真 |
if _unlikely_(bool expression) { | 很可能假 |
编译时伪变量,编译时替换,类似宏.
伪变量 | 意思 |
|---|---|
@FN | 当前函数名 |
@METHOD | 用ReceiverType.MethodName来替换,方法 |
@MOD | 模块名 |
@STRUCT | 构名 |
@FILE | 源码文件路径 |
@LINE | 行号,串表示 |
@COLUMN | 列号 |
@VEXE | V编译器路径 |
@VEXEROOT | V编译器路径根, |
@VHASH | V编译器的哈希 |
@VMOD_FILE | 最近v.mod内容 |
@VMODROOT | 最近v.mod根目录 |
调试/记录/跟踪时有用:
eprintln("file: " + @FILE + " | line: " + @LINE + " | fn: " + @MOD + "." + @FN)
//或嵌入版本号
import v.vmod
vm := vmod.decode( @VMOD_FILE ) or { panic(err.msg) }
eprintln("$vm.name $vm.version\n $vm.description")
//后面为环境特殊变量
浙公网安备 33010602011771号