From 0510a69beabaaa256bf3ae141e67d11038c5a01e Mon Sep 17 00:00:00 2001 From: kahsa Date: Wed, 2 Nov 2022 22:33:29 +0900 Subject: [PATCH] doc: categorize the advanced topic section (#16276) --- doc/docs.md | 1419 ++++++++++++++++++++++++++------------------------- 1 file changed, 715 insertions(+), 704 deletions(-) diff --git a/doc/docs.md b/doc/docs.md index c647305030..fc4de3b7f7 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -137,31 +137,38 @@ To do so, run the command `v up`. * [Publish package](#publish-package) * [Advanced Topics](#advanced-topics) * [Attributes](#attributes) + * [Conditional compilation](#conditional-compilation) + * [Compile time pseudo variables](#compile-time-pseudo-variables) + * [Compile-time reflection](#compile-time-reflection) + * [Compile time code](#compile-time-code) + * [Environment specific files](#environment-specific-files) * [Memory-unsafe code](#memory-unsafe-code) * [Structs with reference fields](#structs-with-reference-fields) * [sizeof and __offsetof](#sizeof-and-__offsetof) - * [Calling C from V](#calling-c-from-v) - * [Calling V from C](#calling-v-from-c) - * [Export to shared library](#export-to-shared-library) + * [Limited operator overloading](#limited-operator-overloading) + * [Performance tuning](#performance-tuning) * [Atomics](#atomics) * [Global Variables](#global-variables) - * [Passing C compilation flags](#passing-c-compilation-flags) - * [#pkgconfig](#pkgconfig) - * [Including C code](#including-c-code) - * [C types](#c-types) - * [C Declarations](#c-declarations) - * [Debugging](#debugging) - * [Conditional compilation](#conditional-compilation) - * [Compile time pseudo variables](#compile-time-pseudo-variables) - * [Performance tuning](#performance-tuning) - * [Compile-time reflection](#compile-time-reflection) - * [Limited operator overloading](#limited-operator-overloading) - * [Inline assembly](#inline-assembly) - * [Translating C to V](#translating-c-to-v) - * [Hot code reloading](#hot-code-reloading) * [Cross compilation](#cross-compilation) - * [Cross-platform shell scripts in V](#cross-platform-shell-scripts-in-v) - * [Vsh scripts with no extension](#vsh-scripts-with-no-extension) + * [Debugging](#debugging) + * [C Backend binaries Default](#c-backend-binaries-default) + * [Native Backend binaries](#native-backend-binaries) + * [Javascript Backend](#javascript-backend) + * [V and C](#v-and-c) + * [Calling C from V](#calling-c-from-v) + * [Calling V from C](#calling-v-from-c) + * [Passing C compilation flags](#passing-c-compilation-flags) + * [#pkgconfig](#pkgconfig) + * [Including C code](#including-c-code) + * [C types](#c-types) + * [C Declarations](#c-declarations) + * [Export to shared library](#export-to-shared-library) + * [Translating C to V](#translating-c-to-v) + * [Other V Features](#other-v-features) + * [Inline assembly](#inline-assembly) + * [Hot code reloading](#hot-code-reloading) + * [Cross-platform shell scripts in V](#cross-platform-shell-scripts-in-v) + * [Vsh scripts with no extension](#vsh-scripts-with-no-extension) * [Appendices](#appendices) * [Keywords](#appendix-i-keywords) * [Operators](#appendix-ii-operators) @@ -4967,6 +4974,7 @@ Package are up to date. to allow for a better search experience. # Advanced Topics + ## Attributes V has several attributes that modify the behavior of functions and structs. @@ -5131,572 +5139,67 @@ fn main() { } ``` -## Memory-unsafe code - -Sometimes for efficiency you may want to write low-level code that can potentially -corrupt memory or be vulnerable to security exploits. V supports writing such code, -but not by default. - -V requires that any potentially memory-unsafe operations are marked intentionally. -Marking them also indicates to anyone reading the code that there could be -memory-safety violations if there was a mistake. - -Examples of potentially memory-unsafe operations are: - -* Pointer arithmetic -* Pointer indexing -* Conversion to pointer from an incompatible type -* Calling certain C functions, e.g. `free`, `strlen` and `strncmp`. - -To mark potentially memory-unsafe operations, enclose them in an `unsafe` block: - -```v wip -// allocate 2 uninitialized bytes & return a reference to them -mut p := unsafe { malloc(2) } -p[0] = `h` // Error: pointer indexing is only allowed in `unsafe` blocks -unsafe { - p[0] = `h` // OK - p[1] = `i` -} -p++ // Error: pointer arithmetic is only allowed in `unsafe` blocks -unsafe { - p++ // OK -} -assert *p == `i` -``` - -Best practice is to avoid putting memory-safe expressions inside an `unsafe` block, -so that the reason for using `unsafe` is as clear as possible. Generally any code -you think is memory-safe should not be inside an `unsafe` block, so the compiler -can verify it. - -If you suspect your program does violate memory-safety, you have a head start on -finding the cause: look at the `unsafe` blocks (and how they interact with -surrounding code). - -* Note: This is work in progress. - -## Structs with reference fields - -Structs with references require explicitly setting the initial value to a -reference value unless the struct already defines its own initial value. - -Zero-value references, or nil pointers, will **NOT** be supported in the future, -for now data structures such as Linked Lists or Binary Trees that rely on reference -fields that can use the value `0`, understanding that it is unsafe, and that it can -cause a panic. - -```v -struct Node { - a &Node - b &Node = 0 // Auto-initialized to nil, use with caution! -} - -// Reference fields must be initialized unless an initial value is declared. -// Zero (0) is OK but use with caution, it's a nil pointer. -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 and __offsetof - -* `sizeof(Type)` gives the size of a type in bytes. -* `__offsetof(Struct, field_name)` gives the offset in bytes of a struct field. - -```v -struct Foo { - a int - b int -} - -assert sizeof(Foo) == 8 -assert __offsetof(Foo, a) == 0 -assert __offsetof(Foo, b) == 4 -``` - -## Calling C from V - -**Example** -```v -#flag -lsqlite3 -#include "sqlite3.h" -// See also the example from https://www.sqlite.org/quickstart.html -struct C.sqlite3 { -} - -struct C.sqlite3_stmt { -} - -type FnSqlite3Callback = fn (voidptr, int, &&char, &&char) int - -fn C.sqlite3_open(&char, &&C.sqlite3) int - -fn C.sqlite3_close(&C.sqlite3) int - -fn C.sqlite3_column_int(stmt &C.sqlite3_stmt, n int) int - -// ... you can also just define the type of parameter and leave out the C. prefix -fn C.sqlite3_prepare_v2(&C.sqlite3, &char, int, &&C.sqlite3_stmt, &&char) int - -fn C.sqlite3_step(&C.sqlite3_stmt) - -fn C.sqlite3_finalize(&C.sqlite3_stmt) - -fn C.sqlite3_exec(db &C.sqlite3, sql &char, cb FnSqlite3Callback, cb_arg voidptr, emsg &&char) int - -fn C.sqlite3_free(voidptr) - -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) // this means `sqlite3* db = 0` - // passing a string literal to a C function call results in a C string, not a V string - C.sqlite3_open(c'users.db', &db) - // C.sqlite3_open(db_path.str, &db) - query := 'select count(*) from users' - stmt := &C.sqlite3_stmt(0) - // NB: you can also use the `.str` field of a V string, - // to get its C style zero terminated representation - 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('There are $nr_users users in the database.') - // - 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) -} -``` - -## Calling V from C - -Since V can compile to C, calling V code from C is very easy, once you know how. - -Use `v -o file.c your_file.v` to generate a C file, corresponding to the V code. - -More details in [call_v_from_c example](../examples/call_v_from_c). - -## Export to shared library - -By default all V functions have the following naming scheme in C: `[module name]__[fn_name]`. - -For example, `fn foo() {}` in module `bar` will result in `bar__foo()`. - -To use a custom export name, use the `[export]` attribute: - -``` -[export: 'my_custom_c_name'] -fn foo() { -} -``` - - -## Atomics - -V has no special support for atomics, yet, nevertheless it's possible to treat variables as atomics -by calling C functions from V. The standard C11 atomic functions like `atomic_store()` are usually -defined with the help of macros and C compiler magic to provide a kind of *overloaded C functions*. -Since V does not support overloading functions by intention there are wrapper functions defined in -C headers named `atomic.h` that are part of the V compiler infrastructure. - -There are dedicated wrappers for all unsigned integer types and for pointers. -(`byte` is not fully supported on Windows) – the function names include the type name -as suffix. e.g. `C.atomic_load_ptr()` or `C.atomic_fetch_add_u64()`. - -To use these functions the C header for the used OS has to be included and the functions -that are intended to be used have to be declared. Example: - -```v globals -$if windows { - #include "@VEXEROOT/thirdparty/stdatomic/win/atomic.h" -} $else { - #include "@VEXEROOT/thirdparty/stdatomic/nix/atomic.h" -} - -// declare functions we want to use - V does not parse the C header -fn C.atomic_store_u32(&u32, u32) -fn C.atomic_load_u32(&u32) u32 -fn C.atomic_compare_exchange_weak_u32(&u32, &u32, u32) bool -fn C.atomic_compare_exchange_strong_u32(&u32, &u32, u32) bool - -const num_iterations = 10000000 - -// see section "Global Variables" below -__global ( - atom u32 // ordinary variable but used as atomic -) - -fn change() int { - mut races_won_by_change := 0 - for { - mut cmp := u32(17) // addressable value to compare with and to store the found value - // atomic version of `if atom == 17 { atom = 23 races_won_by_change++ } else { cmp = atom }` - if C.atomic_compare_exchange_strong_u32(&atom, &cmp, 23) { - races_won_by_change++ - } else { - if cmp == 31 { - break - } - cmp = 17 // re-assign because overwritten with value of atom - } - } - return races_won_by_change -} - -fn main() { - C.atomic_store_u32(&atom, 17) - t := go change() - mut races_won_by_main := 0 - mut cmp17 := u32(17) - mut cmp23 := u32(23) - for i in 0 .. num_iterations { - // atomic version of `if atom == 17 { atom = 23 races_won_by_main++ }` - if C.atomic_compare_exchange_strong_u32(&atom, &cmp17, 23) { - races_won_by_main++ - } else { - cmp17 = 17 - } - desir := if i == num_iterations - 1 { u32(31) } else { u32(17) } - // atomic version of `for atom != 23 {} atom = desir` - for !C.atomic_compare_exchange_weak_u32(&atom, &cmp23, desir) { - cmp23 = 23 - } - } - races_won_by_change := t.wait() - atom_new := C.atomic_load_u32(&atom) - println('atom: $atom_new, #exchanges: ${races_won_by_main + races_won_by_change}') - // prints `atom: 31, #exchanges: 10000000`) - println('races won by\n- `main()`: $races_won_by_main\n- `change()`: $races_won_by_change') -} -``` - -In this example both `main()` and the spawned thread `change()` try to replace a value of `17` -in the global `atom` with a value of `23`. The replacement in the opposite direction is -done exactly 10000000 times. The last replacement will be with `31` which makes the spawned -thread finish. - -It is not predictable how many replacements occur in which thread, but the sum will always -be 10000000. (With the non-atomic commands from the comments the value will be higher or the program -will hang – dependent on the compiler optimization used.) - -## Global Variables - -By default V does not allow global variables. However, in low level applications they have their -place so their usage can be enabled with the compiler flag `-enable-globals`. -Declarations of global variables must be surrounded with a `__global ( ... )` -specification – as in the example [above](#atomics). - -An initializer for global variables must be explicitly converted to the -desired target type. If no initializer is given a default initialization is done. -Some objects like semaphores and mutexes require an explicit initialization *in place*, i.e. -not with a value returned from a function call but with a method call by reference. -A separate `init()` function can be used for this purpose – it will be called before `main()`: - -```v globals -import sync - -__global ( - sem sync.Semaphore // needs initialization in `init()` - mtx sync.RwMutex // needs initialization in `init()` - f1 = f64(34.0625) // explicily initialized - shmap shared map[string]f64 // initialized as empty `shared` map - f2 f64 // initialized to `0.0` -) - -fn init() { - sem.init(0) - mtx.init() -} -``` -Be aware that in multi threaded applications the access to global variables is subject -to race conditions. There are several approaches to deal with these: - -- use `shared` types for the variable declarations and use `lock` blocks for access. - This is most appropriate for larger objects like structs, arrays or maps. -- handle primitive data types as "atomics" using special C-functions (see [above](#atomics)). -- use explicit synchronization primitives like mutexes to control access. The compiler - cannot really help in this case, so you have to know what you are doing. -- don't care – this approach is possible but makes only sense if the exact values - of global variables do not really matter. An example can be found in the `rand` module - where global variables are used to generate (non cryptographic) pseudo random numbers. - In this case data races lead to random numbers in different threads becoming somewhat - correlated, which is acceptable considering the performance penalty that using - synchonization primitives would represent. - -## Passing C compilation flags - -Add `#flag` directives to the top of your V files to provide C compilation flags like: - -- `-I` for adding C include files search paths -- `-l` for adding C library names that you want to get linked -- `-L` for adding C library files search paths -- `-D` for setting compile time variables - -You can (optionally) use different flags for different targets. -Currently the `linux`, `darwin` , `freebsd`, and `windows` flags are supported. - -NB: Each flag must go on its own line (for now) - -```v oksyntax -#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= -``` - -In the console build command, you can use: -* `-cflags` to pass custom flags to the backend C compiler. -* `-cc` to change the default C backend compiler. -* For example: `-cc gcc-9 -cflags -fsanitize=thread`. - -You can define a `VFLAGS` environment variable in your terminal to store your `-cc` -and `-cflags` settings, rather than including them in the build command each time. - -## #pkgconfig - -Add `#pkgconfig` directive is used to tell the compiler which modules should be used for compiling -and linking using the pkg-config files provided by the respective dependencies. - -As long as backticks can't be used in `#flag` and spawning processes is not desirable for security -and portability reasons, V uses its own pkgconfig library that is compatible with the standard -freedesktop one. - -If no flags are passed it will add `--cflags` and `--libs`, both lines below do the same: - -```v oksyntax -#pkgconfig r_core -#pkgconfig --cflags --libs r_core -``` - -The `.pc` files are looked up into a hardcoded list of default pkg-config paths, the user can add -extra paths by using the `PKG_CONFIG_PATH` environment variable. Multiple modules can be passed. - -To check the existence of a pkg-config use `$pkgconfig('pkg')` as a compile time "if" condition to -check if a pkg-config exists. If it exists the branch will be created. Use `$else` or `$else $if` -to handle other cases. - -```v ignore -$if $pkgconfig('mysqlclient') { - #pkgconfig mysqlclient -} $else $if $pkgconfig('mariadb') { - #pkgconfig mariadb -} -``` - -## Including C code - -You can also include C code directly in your V module. -For example, let's say that your C code is located in a folder named 'c' inside your module folder. -Then: - -* Put a v.mod file inside the toplevel folder of your module (if you -created your module with `v new` you already have v.mod file). For -example: -```v ignore -Module { - name: 'mymodule', - description: 'My nice module wraps a simple C library.', - version: '0.0.1' - dependencies: [] -} -``` - - -* Add these lines to the top of your module: -```v oksyntax -#flag -I @VMODROOT/c -#flag @VMODROOT/c/implementation.o -#include "header.h" -``` -NB: @VMODROOT will be replaced by V with the *nearest parent folder, where there is a v.mod file*. -Any .v file beside or below the folder where the v.mod file is, -can use `#flag @VMODROOT/abc` to refer to this folder. -The @VMODROOT folder is also *prepended* to the module lookup path, -so you can *import* other modules under your @VMODROOT, by just naming them. - -The instructions above will make V look for an compiled .o file in -your module `folder/c/implementation.o`. -If V finds it, the .o file will get linked to the main executable, that used the module. -If it does not find it, V assumes that there is a `@VMODROOT/c/implementation.c` file, -and tries to compile it to a .o file, then will use that. - -This allows you to have C code, that is contained in a V module, so that its distribution is easier. -You can see a complete minimal example for using C code in a V wrapper module here: -[project_with_c_code](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code). -Another example, demonstrating passing structs from C to V and back again: -[interoperate between C to V to C](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code_2). - -## C types - -Ordinary zero terminated C strings can be converted to V strings with -`unsafe { &char(cstring).vstring() }` or if you know their length already with -`unsafe { &char(cstring).vstring_with_len(len) }`. - -NB: The .vstring() and .vstring_with_len() methods do NOT create a copy of the `cstring`, -so you should NOT free it after calling the method `.vstring()`. -If you need to make a copy of the C string (some libc APIs like `getenv` pretty much require that, -since they return pointers to internal libc memory), you can use `cstring_to_vstring(cstring)`. - -On Windows, C APIs often return so called `wide` strings (utf16 encoding). -These can be converted to V strings with `string_from_wide(&u16(cwidestring))` . - -V has these types for easier interoperability with C: - -- `voidptr` for C's `void*`, -- `&byte` for C's `byte*` and -- `&char` for C's `char*`. -- `&&char` for C's `char**` - -To cast a `voidptr` to a V reference, use `user := &User(user_void_ptr)`. - -`voidptr` can also be dereferenced into a V struct through casting: `user := User(user_void_ptr)`. - -[an example of a module that calls C code from V](https://github.com/vlang/v/blob/master/vlib/v/tests/project_with_c_code/mod1/wrapper.v) - -## C Declarations - -C identifiers are accessed with the `C` prefix similarly to how module-specific -identifiers are accessed. Functions must be redeclared in V before they can be used. -Any C types may be used behind the `C` prefix, but types must be redeclared in V in -order to access type members. - -To redeclare complex types, such as in the following C code: - -```c -struct SomeCStruct { - uint8_t implTraits; - uint16_t memPoolData; - union { - struct { - void* data; - size_t size; - }; - - DataView view; - }; -}; -``` - -members of sub-data-structures may be directly declared in the containing struct as below: - -```v -struct C.SomeCStruct { - implTraits byte - memPoolData u16 - // These members are part of sub data structures that can't currently be represented in V. - // Declaring them directly like this is sufficient for access. - // union { - // struct { - data voidptr - size usize - // } - view C.DataView - // } -} -``` - -The existence of the data members is made known to V, and they may be used without -re-creating the original structure exactly. - -Alternatively, you may [embed](#embedded-structs) the sub-data-structures to maintain -a parallel code structure. - -## Debugging - -### C Backend binaries (Default) - -To debug issues in the generated binary (flag: `-b c`), you can pass these flags: - -- `-g` - produces a less optimized executable with more debug information in it. - V will enforce line numbers from the .v files in the stacktraces, that the - executable will produce on panic. It is usually better to pass -g, unless - you are writing low level code, in which case use the next option `-cg`. -- `-cg` - produces a less optimized executable with more debug information in it. - The executable will use C source line numbers in this case. It is frequently - used in combination with `-keepc`, so that you can inspect the generated - C program in case of panic, or so that your debugger (`gdb`, `lldb` etc.) - can show you the generated C source code. -- `-showcc` - prints the C command that is used to build the program. -- `-show-c-output` - prints the output, that your C compiler produced - while compiling your program. -- `-keepc` - do not delete the generated C source code file after a successful - compilation. Also keep using the same file path, so it is more stable, - and easier to keep opened in an editor/IDE. - -For best debugging experience if you are writing a low level wrapper for an existing -C library, you can pass several of these flags at the same time: -`v -keepc -cg -showcc yourprogram.v`, then just run your debugger (gdb/lldb) or IDE -on the produced executable `yourprogram`. - -If you just want to inspect the generated C code, -without further compilation, you can also use the `-o` flag (e.g. `-o file.c`). -This will make V produce the `file.c` then stop. - -If you want to see the generated C source code for *just* a single C function, -for example `main`, you can use: `-printfn main -o file.c`. - -To debug the V executable itself you need to compile from src with `./v -g -o v cmd/v`. - -You can debug tests with for example `v -g -keepc prog_test.v`. The `-keepc` flag is needed, -so that the executable is not deleted, after it was created and ran. - -To see a detailed list of all flags that V supports, -use `v help`, `v help build` and `v help build-c`. - -**Commandline Debugging** - -1. compile your binary with debugging info `v -g hello.v` -2. debug with [lldb](https://lldb.llvm.org) or [GDB](https://www.gnu.org/software/gdb/) e.g. `lldb hello` - -[Troubleshooting (debugging) executables created with V in GDB](https://github.com/vlang/v/wiki/Troubleshooting-(debugging)-executables-created-with-V-in-GDB) - -**Visual debugging Setup:** -* [Visual Studio Code](vscode.md) - -### Native Backend binaries - -Currently there is no debugging support for binaries, created by the -native backend (flag: `-b native`). - -### Javascript Backend - -To debug the generated Javascript output you can activate source maps: -`v -b js -sourcemap hello.v -o hello.js` - -For all supported options check the latest help: -`v help build-js` - ## Conditional compilation +### Compile time pseudo variables + +V also gives your code access to a set of pseudo string variables, +that are substituted at compile time: + +- `@FN` => replaced with the name of the current V function +- `@METHOD` => replaced with ReceiverType.MethodName +- `@MOD` => replaced with the name of the current V module +- `@STRUCT` => replaced with the name of the current V struct +- `@FILE` => replaced with the absolute path of the V source file +- `@LINE` => replaced with the V line number where it appears (as a string). +- `@FILE_LINE` => like `@FILE:@LINE`, but the file part is a relative path +- `@COLUMN` => replaced with the column where it appears (as a string). +- `@VEXE` => replaced with the path to the V compiler +- `@VEXEROOT` => will be substituted with the *folder*, + where the V executable is (as a string). +- `@VHASH` => replaced with the shortened commit hash of the V compiler (as a string). +- `@VMOD_FILE` => replaced with the contents of the nearest v.mod file (as a string). +- `@VMODROOT` => will be substituted with the *folder*, + where the nearest v.mod file is (as a string). + +That allows you to do the following example, useful while debugging/logging/tracing your code: +```v +eprintln('file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN) +``` + +Another example, is if you want to embed the version/name from v.mod *inside* your executable: +```v ignore +import v.vmod +vm := vmod.decode( @VMOD_FILE ) or { panic(err) } +eprintln('$vm.name $vm.version\n $vm.description') +``` + +### Compile-time reflection + +Having built-in JSON support is nice, but V also allows you to create efficient +serializers for any data format. V has compile-time `if` and `for` constructs: + +```v +struct User { + name string + age int +} + +fn main() { + $for field in User.fields { + $if field.typ is string { + println('$field.name is of type string') + } + } +} + +// Output: +// name is of type string +``` + +See [`examples/compiletime/reflection.v`](/examples/compiletime/reflection.v) +for a more complete example. + ### Compile time code `$` is used as a prefix for compile-time operations. @@ -5940,96 +5443,102 @@ conditional blocks inside it, i.e. `$if linux {}` etc. - `_notd_customflag.v` => similar to _d_customflag.v, but will be used *only* if you do NOT pass `-d customflag` to V. -## Compile time pseudo variables +## Memory-unsafe code -V also gives your code access to a set of pseudo string variables, -that are substituted at compile time: +Sometimes for efficiency you may want to write low-level code that can potentially +corrupt memory or be vulnerable to security exploits. V supports writing such code, +but not by default. -- `@FN` => replaced with the name of the current V function -- `@METHOD` => replaced with ReceiverType.MethodName -- `@MOD` => replaced with the name of the current V module -- `@STRUCT` => replaced with the name of the current V struct -- `@FILE` => replaced with the absolute path of the V source file -- `@LINE` => replaced with the V line number where it appears (as a string). -- `@FILE_LINE` => like `@FILE:@LINE`, but the file part is a relative path -- `@COLUMN` => replaced with the column where it appears (as a string). -- `@VEXE` => replaced with the path to the V compiler -- `@VEXEROOT` => will be substituted with the *folder*, - where the V executable is (as a string). -- `@VHASH` => replaced with the shortened commit hash of the V compiler (as a string). -- `@VMOD_FILE` => replaced with the contents of the nearest v.mod file (as a string). -- `@VMODROOT` => will be substituted with the *folder*, - where the nearest v.mod file is (as a string). +V requires that any potentially memory-unsafe operations are marked intentionally. +Marking them also indicates to anyone reading the code that there could be +memory-safety violations if there was a mistake. -That allows you to do the following example, useful while debugging/logging/tracing your code: -```v -eprintln('file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN) +Examples of potentially memory-unsafe operations are: + +* Pointer arithmetic +* Pointer indexing +* Conversion to pointer from an incompatible type +* Calling certain C functions, e.g. `free`, `strlen` and `strncmp`. + +To mark potentially memory-unsafe operations, enclose them in an `unsafe` block: + +```v wip +// allocate 2 uninitialized bytes & return a reference to them +mut p := unsafe { malloc(2) } +p[0] = `h` // Error: pointer indexing is only allowed in `unsafe` blocks +unsafe { + p[0] = `h` // OK + p[1] = `i` +} +p++ // Error: pointer arithmetic is only allowed in `unsafe` blocks +unsafe { + p++ // OK +} +assert *p == `i` ``` -Another example, is if you want to embed the version/name from v.mod *inside* your executable: -```v ignore -import v.vmod -vm := vmod.decode( @VMOD_FILE ) or { panic(err) } -eprintln('$vm.name $vm.version\n $vm.description') -``` +Best practice is to avoid putting memory-safe expressions inside an `unsafe` block, +so that the reason for using `unsafe` is as clear as possible. Generally any code +you think is memory-safe should not be inside an `unsafe` block, so the compiler +can verify it. -## Performance tuning +If you suspect your program does violate memory-safety, you have a head start on +finding the cause: look at the `unsafe` blocks (and how they interact with +surrounding code). -The generated C code is usually fast enough, when you compile your code -with `-prod`. There are some situations though, where you may want to give -additional hints to the compiler, so that it can further optimize some -blocks of code. +* Note: This is work in progress. -NB: These are *rarely* needed, and should not be used, unless you -*profile your code*, and then see that there are significant benefits for them. -To cite gcc's documentation: "programmers are notoriously bad at predicting -how their programs actually perform". +## Structs with reference fields -`[inline]` - you can tag functions with `[inline]`, so the C compiler will -try to inline them, which in some cases, may be beneficial for performance, -but may impact the size of your executable. +Structs with references require explicitly setting the initial value to a +reference value unless the struct already defines its own initial value. -`[direct_array_access]` - in functions tagged with `[direct_array_access]` -the compiler will translate array operations directly into C array operations - -omitting bounds checking. This may save a lot of time in a function that iterates -over an array but at the cost of making the function unsafe - unless -the boundaries will be checked by the user. - -`if _likely_(bool expression) {` this hints the C compiler, that the passed -boolean expression is very likely to be true, so it can generate assembly -code, with less chance of branch misprediction. In the JS backend, -that does nothing. - -`if _unlikely_(bool expression) {` similar to `_likely_(x)`, but it hints that -the boolean expression is highly improbable. In the JS backend, that does nothing. - - - -## Compile-time reflection - -Having built-in JSON support is nice, but V also allows you to create efficient -serializers for any data format. V has compile-time `if` and `for` constructs: +Zero-value references, or nil pointers, will **NOT** be supported in the future, +for now data structures such as Linked Lists or Binary Trees that rely on reference +fields that can use the value `0`, understanding that it is unsafe, and that it can +cause a panic. ```v -struct User { - name string - age int +struct Node { + a &Node + b &Node = 0 // Auto-initialized to nil, use with caution! } -fn main() { - $for field in User.fields { - $if field.typ is string { - println('$field.name is of type string') - } - } +// Reference fields must be initialized unless an initial value is declared. +// Zero (0) is OK but use with caution, it's a nil pointer. +foo := Node{ + a: 0 } - -// Output: -// name is of type string +bar := Node{ + a: &foo +} +baz := Node{ + a: 0 + b: 0 +} +qux := Node{ + a: &foo + b: &bar +} +println(baz) +println(qux) ``` -See [`examples/compiletime/reflection.v`](/examples/compiletime/reflection.v) -for a more complete example. +## sizeof and __offsetof + +* `sizeof(Type)` gives the size of a type in bytes. +* `__offsetof(Struct, field_name)` gives the offset in bytes of a struct field. + +```v +struct Foo { + a int + b int +} + +assert sizeof(Foo) == 8 +assert __offsetof(Foo, a) == 0 +assert __offsetof(Foo, b) == 4 +``` ## Limited operator overloading @@ -6080,28 +5589,534 @@ To improve safety and maintainability, operator overloading is limited: - Assignment operators (`*=`, `+=`, `/=`, etc) are auto generated when the corresponding operators are defined and operands are of the same type. -## Inline assembly - -```v ignore -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 +## Performance tuning + +The generated C code is usually fast enough, when you compile your code +with `-prod`. There are some situations though, where you may want to give +additional hints to the compiler, so that it can further optimize some +blocks of code. + +NB: These are *rarely* needed, and should not be used, unless you +*profile your code*, and then see that there are significant benefits for them. +To cite gcc's documentation: "programmers are notoriously bad at predicting +how their programs actually perform". + +`[inline]` - you can tag functions with `[inline]`, so the C compiler will +try to inline them, which in some cases, may be beneficial for performance, +but may impact the size of your executable. + +`[direct_array_access]` - in functions tagged with `[direct_array_access]` +the compiler will translate array operations directly into C array operations - +omitting bounds checking. This may save a lot of time in a function that iterates +over an array but at the cost of making the function unsafe - unless +the boundaries will be checked by the user. + +`if _likely_(bool expression) {` this hints the C compiler, that the passed +boolean expression is very likely to be true, so it can generate assembly +code, with less chance of branch misprediction. In the JS backend, +that does nothing. + +`if _unlikely_(bool expression) {` similar to `_likely_(x)`, but it hints that +the boolean expression is highly improbable. In the JS backend, that does nothing. + + + +## Atomics + +V has no special support for atomics, yet, nevertheless it's possible to treat variables as atomics +by calling C functions from V. The standard C11 atomic functions like `atomic_store()` are usually +defined with the help of macros and C compiler magic to provide a kind of *overloaded C functions*. +Since V does not support overloading functions by intention there are wrapper functions defined in +C headers named `atomic.h` that are part of the V compiler infrastructure. + +There are dedicated wrappers for all unsigned integer types and for pointers. +(`byte` is not fully supported on Windows) – the function names include the type name +as suffix. e.g. `C.atomic_load_ptr()` or `C.atomic_fetch_add_u64()`. + +To use these functions the C header for the used OS has to be included and the functions +that are intended to be used have to be declared. Example: + +```v globals +$if windows { + #include "@VEXEROOT/thirdparty/stdatomic/win/atomic.h" +} $else { + #include "@VEXEROOT/thirdparty/stdatomic/nix/atomic.h" +} + +// declare functions we want to use - V does not parse the C header +fn C.atomic_store_u32(&u32, u32) +fn C.atomic_load_u32(&u32) u32 +fn C.atomic_compare_exchange_weak_u32(&u32, &u32, u32) bool +fn C.atomic_compare_exchange_strong_u32(&u32, &u32, u32) bool + +const num_iterations = 10000000 + +// see section "Global Variables" below +__global ( + atom u32 // ordinary variable but used as atomic +) + +fn change() int { + mut races_won_by_change := 0 + for { + mut cmp := u32(17) // addressable value to compare with and to store the found value + // atomic version of `if atom == 17 { atom = 23 races_won_by_change++ } else { cmp = atom }` + if C.atomic_compare_exchange_strong_u32(&atom, &cmp, 23) { + races_won_by_change++ + } else { + if cmp == 31 { + break + } + cmp = 17 // re-assign because overwritten with value of atom + } + } + return races_won_by_change +} + +fn main() { + C.atomic_store_u32(&atom, 17) + t := go change() + mut races_won_by_main := 0 + mut cmp17 := u32(17) + mut cmp23 := u32(23) + for i in 0 .. num_iterations { + // atomic version of `if atom == 17 { atom = 23 races_won_by_main++ }` + if C.atomic_compare_exchange_strong_u32(&atom, &cmp17, 23) { + races_won_by_main++ + } else { + cmp17 = 17 + } + desir := if i == num_iterations - 1 { u32(31) } else { u32(17) } + // atomic version of `for atom != 23 {} atom = desir` + for !C.atomic_compare_exchange_weak_u32(&atom, &cmp23, desir) { + cmp23 = 23 + } + } + races_won_by_change := t.wait() + atom_new := C.atomic_load_u32(&atom) + println('atom: $atom_new, #exchanges: ${races_won_by_main + races_won_by_change}') + // prints `atom: 31, #exchanges: 10000000`) + println('races won by\n- `main()`: $races_won_by_main\n- `change()`: $races_won_by_change') } -println('a: $a') // 100 -println('b: $b') // 20 -println('c: $c') // 120 ``` -For more examples, see [github.com/vlang/v/tree/master/vlib/v/tests/assembly/asm_test.amd64.v](https://github.com/vlang/v/tree/master/vlib/v/tests/assembly/asm_test.amd64.v) +In this example both `main()` and the spawned thread `change()` try to replace a value of `17` +in the global `atom` with a value of `23`. The replacement in the opposite direction is +done exactly 10000000 times. The last replacement will be with `31` which makes the spawned +thread finish. -## Translating C to V +It is not predictable how many replacements occur in which thread, but the sum will always +be 10000000. (With the non-atomic commands from the comments the value will be higher or the program +will hang – dependent on the compiler optimization used.) + +## Global Variables + +By default V does not allow global variables. However, in low level applications they have their +place so their usage can be enabled with the compiler flag `-enable-globals`. +Declarations of global variables must be surrounded with a `__global ( ... )` +specification – as in the example [above](#atomics). + +An initializer for global variables must be explicitly converted to the +desired target type. If no initializer is given a default initialization is done. +Some objects like semaphores and mutexes require an explicit initialization *in place*, i.e. +not with a value returned from a function call but with a method call by reference. +A separate `init()` function can be used for this purpose – it will be called before `main()`: + +```v globals +import sync + +__global ( + sem sync.Semaphore // needs initialization in `init()` + mtx sync.RwMutex // needs initialization in `init()` + f1 = f64(34.0625) // explicily initialized + shmap shared map[string]f64 // initialized as empty `shared` map + f2 f64 // initialized to `0.0` +) + +fn init() { + sem.init(0) + mtx.init() +} +``` +Be aware that in multi threaded applications the access to global variables is subject +to race conditions. There are several approaches to deal with these: + +- use `shared` types for the variable declarations and use `lock` blocks for access. + This is most appropriate for larger objects like structs, arrays or maps. +- handle primitive data types as "atomics" using special C-functions (see [above](#atomics)). +- use explicit synchronization primitives like mutexes to control access. The compiler + cannot really help in this case, so you have to know what you are doing. +- don't care – this approach is possible but makes only sense if the exact values + of global variables do not really matter. An example can be found in the `rand` module + where global variables are used to generate (non cryptographic) pseudo random numbers. + In this case data races lead to random numbers in different threads becoming somewhat + correlated, which is acceptable considering the performance penalty that using + synchonization primitives would represent. + +## Cross compilation + +To cross compile your project simply run + +```shell +v -os windows . +``` + +or + +```shell +v -os linux . +``` +NB: Cross-compiling a windows binary on a linux machine requires the GNU C compiler for +MinGW-w64 (targeting Win64) to first be installed. + +```shell +sudo apt-get install gcc-mingw-w64-x86-64 +``` +(Cross compiling for macOS is temporarily not possible.) + +If you don't have any C dependencies, that's all you need to do. This works even +when compiling GUI apps using the `ui` module or graphical apps using `gg`. + +You will need to install Clang, LLD linker, and download a zip file with +libraries and include files for Windows and Linux. V will provide you with a link. + +## Debugging + +### C Backend binaries (Default) + +To debug issues in the generated binary (flag: `-b c`), you can pass these flags: + +- `-g` - produces a less optimized executable with more debug information in it. + V will enforce line numbers from the .v files in the stacktraces, that the + executable will produce on panic. It is usually better to pass -g, unless + you are writing low level code, in which case use the next option `-cg`. +- `-cg` - produces a less optimized executable with more debug information in it. + The executable will use C source line numbers in this case. It is frequently + used in combination with `-keepc`, so that you can inspect the generated + C program in case of panic, or so that your debugger (`gdb`, `lldb` etc.) + can show you the generated C source code. +- `-showcc` - prints the C command that is used to build the program. +- `-show-c-output` - prints the output, that your C compiler produced + while compiling your program. +- `-keepc` - do not delete the generated C source code file after a successful + compilation. Also keep using the same file path, so it is more stable, + and easier to keep opened in an editor/IDE. + +For best debugging experience if you are writing a low level wrapper for an existing +C library, you can pass several of these flags at the same time: +`v -keepc -cg -showcc yourprogram.v`, then just run your debugger (gdb/lldb) or IDE +on the produced executable `yourprogram`. + +If you just want to inspect the generated C code, +without further compilation, you can also use the `-o` flag (e.g. `-o file.c`). +This will make V produce the `file.c` then stop. + +If you want to see the generated C source code for *just* a single C function, +for example `main`, you can use: `-printfn main -o file.c`. + +To debug the V executable itself you need to compile from src with `./v -g -o v cmd/v`. + +You can debug tests with for example `v -g -keepc prog_test.v`. The `-keepc` flag is needed, +so that the executable is not deleted, after it was created and ran. + +To see a detailed list of all flags that V supports, +use `v help`, `v help build` and `v help build-c`. + +**Commandline Debugging** + +1. compile your binary with debugging info `v -g hello.v` +2. debug with [lldb](https://lldb.llvm.org) or [GDB](https://www.gnu.org/software/gdb/) e.g. `lldb hello` + +[Troubleshooting (debugging) executables created with V in GDB](https://github.com/vlang/v/wiki/Troubleshooting-(debugging)-executables-created-with-V-in-GDB) + +**Visual debugging Setup:** +* [Visual Studio Code](vscode.md) + +### Native Backend binaries + +Currently there is no debugging support for binaries, created by the +native backend (flag: `-b native`). + +### Javascript Backend + +To debug the generated Javascript output you can activate source maps: +`v -b js -sourcemap hello.v -o hello.js` + +For all supported options check the latest help: +`v help build-js` + +## V and C + +### Calling C from V + +**Example** +```v +#flag -lsqlite3 +#include "sqlite3.h" +// See also the example from https://www.sqlite.org/quickstart.html +struct C.sqlite3 { +} + +struct C.sqlite3_stmt { +} + +type FnSqlite3Callback = fn (voidptr, int, &&char, &&char) int + +fn C.sqlite3_open(&char, &&C.sqlite3) int + +fn C.sqlite3_close(&C.sqlite3) int + +fn C.sqlite3_column_int(stmt &C.sqlite3_stmt, n int) int + +// ... you can also just define the type of parameter and leave out the C. prefix +fn C.sqlite3_prepare_v2(&C.sqlite3, &char, int, &&C.sqlite3_stmt, &&char) int + +fn C.sqlite3_step(&C.sqlite3_stmt) + +fn C.sqlite3_finalize(&C.sqlite3_stmt) + +fn C.sqlite3_exec(db &C.sqlite3, sql &char, cb FnSqlite3Callback, cb_arg voidptr, emsg &&char) int + +fn C.sqlite3_free(voidptr) + +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) // this means `sqlite3* db = 0` + // passing a string literal to a C function call results in a C string, not a V string + C.sqlite3_open(c'users.db', &db) + // C.sqlite3_open(db_path.str, &db) + query := 'select count(*) from users' + stmt := &C.sqlite3_stmt(0) + // NB: you can also use the `.str` field of a V string, + // to get its C style zero terminated representation + 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('There are $nr_users users in the database.') + // + 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) +} +``` + +### Calling V from C + +Since V can compile to C, calling V code from C is very easy, once you know how. + +Use `v -o file.c your_file.v` to generate a C file, corresponding to the V code. + +More details in [call_v_from_c example](../examples/call_v_from_c). + +### Passing C compilation flags + +Add `#flag` directives to the top of your V files to provide C compilation flags like: + +- `-I` for adding C include files search paths +- `-l` for adding C library names that you want to get linked +- `-L` for adding C library files search paths +- `-D` for setting compile time variables + +You can (optionally) use different flags for different targets. +Currently the `linux`, `darwin` , `freebsd`, and `windows` flags are supported. + +NB: Each flag must go on its own line (for now) + +```v oksyntax +#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= +``` + +In the console build command, you can use: +* `-cflags` to pass custom flags to the backend C compiler. +* `-cc` to change the default C backend compiler. +* For example: `-cc gcc-9 -cflags -fsanitize=thread`. + +You can define a `VFLAGS` environment variable in your terminal to store your `-cc` +and `-cflags` settings, rather than including them in the build command each time. + +### #pkgconfig + +Add `#pkgconfig` directive is used to tell the compiler which modules should be used for compiling +and linking using the pkg-config files provided by the respective dependencies. + +As long as backticks can't be used in `#flag` and spawning processes is not desirable for security +and portability reasons, V uses its own pkgconfig library that is compatible with the standard +freedesktop one. + +If no flags are passed it will add `--cflags` and `--libs`, both lines below do the same: + +```v oksyntax +#pkgconfig r_core +#pkgconfig --cflags --libs r_core +``` + +The `.pc` files are looked up into a hardcoded list of default pkg-config paths, the user can add +extra paths by using the `PKG_CONFIG_PATH` environment variable. Multiple modules can be passed. + +To check the existence of a pkg-config use `$pkgconfig('pkg')` as a compile time "if" condition to +check if a pkg-config exists. If it exists the branch will be created. Use `$else` or `$else $if` +to handle other cases. + +```v ignore +$if $pkgconfig('mysqlclient') { + #pkgconfig mysqlclient +} $else $if $pkgconfig('mariadb') { + #pkgconfig mariadb +} +``` + +### Including C code + +You can also include C code directly in your V module. +For example, let's say that your C code is located in a folder named 'c' inside your module folder. +Then: + +* Put a v.mod file inside the toplevel folder of your module (if you +created your module with `v new` you already have v.mod file). For +example: +```v ignore +Module { + name: 'mymodule', + description: 'My nice module wraps a simple C library.', + version: '0.0.1' + dependencies: [] +} +``` + + +* Add these lines to the top of your module: +```v oksyntax +#flag -I @VMODROOT/c +#flag @VMODROOT/c/implementation.o +#include "header.h" +``` +NB: @VMODROOT will be replaced by V with the *nearest parent folder, where there is a v.mod file*. +Any .v file beside or below the folder where the v.mod file is, +can use `#flag @VMODROOT/abc` to refer to this folder. +The @VMODROOT folder is also *prepended* to the module lookup path, +so you can *import* other modules under your @VMODROOT, by just naming them. + +The instructions above will make V look for an compiled .o file in +your module `folder/c/implementation.o`. +If V finds it, the .o file will get linked to the main executable, that used the module. +If it does not find it, V assumes that there is a `@VMODROOT/c/implementation.c` file, +and tries to compile it to a .o file, then will use that. + +This allows you to have C code, that is contained in a V module, so that its distribution is easier. +You can see a complete minimal example for using C code in a V wrapper module here: +[project_with_c_code](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code). +Another example, demonstrating passing structs from C to V and back again: +[interoperate between C to V to C](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code_2). + +### C types + +Ordinary zero terminated C strings can be converted to V strings with +`unsafe { &char(cstring).vstring() }` or if you know their length already with +`unsafe { &char(cstring).vstring_with_len(len) }`. + +NB: The .vstring() and .vstring_with_len() methods do NOT create a copy of the `cstring`, +so you should NOT free it after calling the method `.vstring()`. +If you need to make a copy of the C string (some libc APIs like `getenv` pretty much require that, +since they return pointers to internal libc memory), you can use `cstring_to_vstring(cstring)`. + +On Windows, C APIs often return so called `wide` strings (utf16 encoding). +These can be converted to V strings with `string_from_wide(&u16(cwidestring))` . + +V has these types for easier interoperability with C: + +- `voidptr` for C's `void*`, +- `&byte` for C's `byte*` and +- `&char` for C's `char*`. +- `&&char` for C's `char**` + +To cast a `voidptr` to a V reference, use `user := &User(user_void_ptr)`. + +`voidptr` can also be dereferenced into a V struct through casting: `user := User(user_void_ptr)`. + +[an example of a module that calls C code from V](https://github.com/vlang/v/blob/master/vlib/v/tests/project_with_c_code/mod1/wrapper.v) + +### C Declarations + +C identifiers are accessed with the `C` prefix similarly to how module-specific +identifiers are accessed. Functions must be redeclared in V before they can be used. +Any C types may be used behind the `C` prefix, but types must be redeclared in V in +order to access type members. + +To redeclare complex types, such as in the following C code: + +```c +struct SomeCStruct { + uint8_t implTraits; + uint16_t memPoolData; + union { + struct { + void* data; + size_t size; + }; + + DataView view; + }; +}; +``` + +members of sub-data-structures may be directly declared in the containing struct as below: + +```v +struct C.SomeCStruct { + implTraits byte + memPoolData u16 + // These members are part of sub data structures that can't currently be represented in V. + // Declaring them directly like this is sufficient for access. + // union { + // struct { + data voidptr + size usize + // } + view C.DataView + // } +} +``` + +The existence of the data members is made known to V, and they may be used without +re-creating the original structure exactly. + +Alternatively, you may [embed](#embedded-structs) the sub-data-structures to maintain +a parallel code structure. + +### Export to shared library + +By default all V functions have the following naming scheme in C: `[module name]__[fn_name]`. + +For example, `fn foo() {}` in module `bar` will result in `bar__foo()`. + +To use a custom export name, use the `[export]` attribute: + +``` +[export: 'my_custom_c_name'] +fn foo() { +} +``` + +### Translating C to V V can translate your C code to human readable V code, and generating V wrappers on top of C libraries. @@ -6158,7 +6173,30 @@ Translating it to V gives you several advantages: - Cross-compilation becomes a lot easier. You don't have to worry about it at all. - No more build flags and include files either. -## Hot code reloading +## Other V Features + +### Inline assembly + +```v ignore +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 +``` + +For more examples, see [github.com/vlang/v/tree/master/vlib/v/tests/assembly/asm_test.amd64.v](https://github.com/vlang/v/tree/master/vlib/v/tests/assembly/asm_test.amd64.v) + +### Hot code reloading ```v live module main @@ -6194,34 +6232,7 @@ Right now it's not possible to modify types while the program is running. More examples, including a graphical application: [github.com/vlang/v/tree/master/examples/hot_reload](https://github.com/vlang/v/tree/master/examples/hot_reload). -## Cross compilation - -To cross compile your project simply run - -```shell -v -os windows . -``` - -or - -```shell -v -os linux . -``` -NB: Cross-compiling a windows binary on a linux machine requires the GNU C compiler for -MinGW-w64 (targeting Win64) to first be installed. - -```shell -sudo apt-get install gcc-mingw-w64-x86-64 -``` -(Cross compiling for macOS is temporarily not possible.) - -If you don't have any C dependencies, that's all you need to do. This works even -when compiling GUI apps using the `ui` module or graphical apps using `gg`. - -You will need to install Clang, LLD linker, and download a zip file with -libraries and include files for Windows and Linux. V will provide you with a link. - -## Cross-platform shell scripts in V +### Cross-platform shell scripts in V V can be used as an alternative to Bash to write deployment scripts, build scripts, etc. @@ -6295,7 +6306,7 @@ Or just run it more like a traditional Bash script: On Unix-like platforms, the file can be run directly after making it executable using `chmod +x`: `./deploy.vsh` -## Vsh scripts with no extension +### Vsh scripts with no extension Whilst V does normally not allow vsh scripts without the designated file extension, there is a way to circumvent this rule and have a file with a fully custom name and shebang. Whilst this feature