doc: enhance the Option example, tweak the Calling C section (#5830)
parent
cf7d03bda6
commit
f5579525c4
85
doc/docs.md
85
doc/docs.md
|
@ -70,6 +70,7 @@ you can do in V.
|
||||||
|
|
||||||
* [Advanced](#advanced)
|
* [Advanced](#advanced)
|
||||||
* [Calling C functions from V](#calling-c-functions-from-v)
|
* [Calling C functions from V](#calling-c-functions-from-v)
|
||||||
|
* [Debugging generated C code](#debugging-generated-c-code)
|
||||||
* [Conditional compilation](#conditional-compilation)
|
* [Conditional compilation](#conditional-compilation)
|
||||||
* [Compile time pseudo variables](#compile-time-pseudo-variables)
|
* [Compile time pseudo variables](#compile-time-pseudo-variables)
|
||||||
* [Reflection via codegen](#reflection-via-codegen)
|
* [Reflection via codegen](#reflection-via-codegen)
|
||||||
|
@ -174,6 +175,7 @@ fn foo() (int, int) {
|
||||||
a, b := foo()
|
a, b := foo()
|
||||||
println(a) // 2
|
println(a) // 2
|
||||||
println(b) // 3
|
println(b) // 3
|
||||||
|
c, _ := foo() // ignore values using `_`
|
||||||
```
|
```
|
||||||
|
|
||||||
Functions can return multiple values.
|
Functions can return multiple values.
|
||||||
|
@ -1329,7 +1331,7 @@ sum := World(Moon{})
|
||||||
```
|
```
|
||||||
|
|
||||||
To check whether a sum type instance holds a certain type, use `sum is Type`.
|
To check whether a sum type instance holds a certain type, use `sum is Type`.
|
||||||
To cast a sum type to one of its variants you use `sum as Type`:
|
To cast a sum type to one of its variants you can use `sum as Type`:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
fn (m Mars) dust_storm() bool
|
fn (m Mars) dust_storm() bool
|
||||||
|
@ -1347,6 +1349,8 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Matching sum types
|
||||||
|
|
||||||
You can also use `match` to determine the variant:
|
You can also use `match` to determine the variant:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
@ -1401,6 +1405,7 @@ Note: shadowing only works when the match expression is a variable. It will not
|
||||||
|
|
||||||
### Option/Result types and error handling
|
### Option/Result types and error handling
|
||||||
|
|
||||||
|
Option types are declared with `?Type`:
|
||||||
```v
|
```v
|
||||||
struct User {
|
struct User {
|
||||||
id int
|
id int
|
||||||
|
@ -1411,12 +1416,6 @@ struct Repo {
|
||||||
users []User
|
users []User
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_repo() Repo {
|
|
||||||
return Repo {
|
|
||||||
users: [User{1, 'Andrew'}, User {2, 'Bob'}, User {10, 'Charles'}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (r Repo) find_user_by_id(id int) ?User {
|
fn (r Repo) find_user_by_id(id int) ?User {
|
||||||
for user in r.users {
|
for user in r.users {
|
||||||
if user.id == id {
|
if user.id == id {
|
||||||
|
@ -1428,7 +1427,9 @@ fn (r Repo) find_user_by_id(id int) ?User {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
repo := new_repo()
|
repo := Repo {
|
||||||
|
users: [User{1, 'Andrew'}, User {2, 'Bob'}, User {10, 'Charles'}]
|
||||||
|
}
|
||||||
user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks
|
user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1458,29 +1459,38 @@ user := repo.find_user_by_id(7) or {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
There are three ways of handling an optional.
|
### Handling optionals
|
||||||
|
|
||||||
The first method is to propagate the error:
|
There are three ways of handling an optional. The first method is to
|
||||||
|
propagate the error:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
resp := http.get(url)?
|
import net.http
|
||||||
println(resp.text)
|
|
||||||
```
|
|
||||||
|
|
||||||
`http.get` returns `?http.Response`. Because it was called with `?`, the error will be propagated to the calling function
|
fn f(url string) ?string {
|
||||||
(which must return an optional as well). If it is used in the `main()` function it will `panic` instead, since the error
|
resp := http.get(url)?
|
||||||
cannot be propagated any further.
|
return resp.text
|
||||||
|
|
||||||
The code above is essentially a condensed version of
|
|
||||||
|
|
||||||
```v
|
|
||||||
resp := http.get(url) or {
|
|
||||||
return error(err)
|
|
||||||
}
|
}
|
||||||
println(resp.text)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The second is to break from execution early:
|
`http.get` returns `?http.Response`. Because `?` follows the call, the
|
||||||
|
error will be propagated to the caller of `f`. When using `?` after a
|
||||||
|
function call producing an optional, the enclosing function must return
|
||||||
|
an optional as well. If error propagation is used in the `main()`
|
||||||
|
function it will `panic` instead, since the error cannot be propagated
|
||||||
|
any further.
|
||||||
|
|
||||||
|
The body of `f` is essentially a condensed version of:
|
||||||
|
|
||||||
|
```v
|
||||||
|
resp := http.get(url) or {
|
||||||
|
return error(err)
|
||||||
|
}
|
||||||
|
return resp.text
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
The second method is to break from execution early:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
user := repo.find_user_by_id(7) or {
|
user := repo.find_user_by_id(7) or {
|
||||||
|
@ -1492,8 +1502,9 @@ Here, you can either call `panic()` or `exit()`, which will stop the execution o
|
||||||
or use a control flow statement (`return`, `break`, `continue`, etc) to break from the current block.
|
or use a control flow statement (`return`, `break`, `continue`, etc) to break from the current block.
|
||||||
Note that `break` and `continue` can only be used inside a `for` loop.
|
Note that `break` and `continue` can only be used inside a `for` loop.
|
||||||
|
|
||||||
|
---
|
||||||
The third method is to provide a default value at the end of the `or` block. In case of an error,
|
The third method is to provide a default value at the end of the `or` block. In case of an error,
|
||||||
that value would be assigned instead, so it must have the same type as the `Option` being handled.
|
that value would be assigned instead, so it must have the same type as the content of the `Option` being handled.
|
||||||
|
|
||||||
```v
|
```v
|
||||||
fn do_something(s string) ?string {
|
fn do_something(s string) ?string {
|
||||||
|
@ -1506,7 +1517,7 @@ b := do_something('bar') or { 'default' } // b will be 'default'
|
||||||
```
|
```
|
||||||
|
|
||||||
V does not have a way to forcibly "unwrap" an optional (as other languages do, for instance Rust's `unwrap()`
|
V does not have a way to forcibly "unwrap" an optional (as other languages do, for instance Rust's `unwrap()`
|
||||||
or Swift's `!`). To do this use `or { panic(err) }` instead.
|
or Swift's `!`). To do this, use `or { panic(err) }` instead.
|
||||||
|
|
||||||
## Generics
|
## Generics
|
||||||
|
|
||||||
|
@ -1844,6 +1855,8 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### #flag
|
||||||
|
|
||||||
Add `#flag` directives to the top of your V files to provide C compilation flags like:
|
Add `#flag` directives to the top of your V files to provide C compilation flags like:
|
||||||
|
|
||||||
- `-I` for adding C include files search paths
|
- `-I` for adding C include files search paths
|
||||||
|
@ -1863,6 +1876,8 @@ NB: Each flag must go on its own line (for now)
|
||||||
#flag linux -DIMGUI_IMPL_API=
|
#flag linux -DIMGUI_IMPL_API=
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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:
|
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
|
* Put a v.mod file inside the toplevel folder of your module (if you
|
||||||
|
@ -1885,26 +1900,28 @@ Module {
|
||||||
#include "header.h"
|
#include "header.h"
|
||||||
```
|
```
|
||||||
NB: @VROOT will be replaced by V with the *nearest parent folder, where there is a v.mod file*.
|
NB: @VROOT 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 @VROOT/abc to refer to this folder.
|
Any .v file beside or below the folder where the v.mod file is, can use `#flag @VROOT/abc` to refer to this folder.
|
||||||
The @VROOT folder is also *prepended* to the module lookup path, so you can *import* other
|
The @VROOT folder is also *prepended* to the module lookup path, so you can *import* other
|
||||||
modules under your @VROOT, by just naming them.
|
modules under your @VROOT, by just naming them.
|
||||||
|
|
||||||
The instructions above will make V look for an compiled .o file in your module folder/c/implementation.o .
|
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 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 `@VROOT/c/implementation.c` file,
|
If it does not find it, V assumes that there is a `@VROOT/c/implementation.c` file,
|
||||||
and tries to compile it to a .o file, then will use that.
|
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.
|
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 example for using C code in a V wrapper module here:
|
You can see a complete minimal example for using C code in a V wrapper module here:
|
||||||
[minimal V project, that has a module, which contains C code](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code)
|
[project_with_c_code](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code).
|
||||||
|
|
||||||
You can use `-cflags` to pass custom flags to the backend C compiler. You can also use `-cc` to change the default C backend compiler.
|
You can use `-cflags` to pass custom flags to the backend C compiler. You can also use `-cc` to change the default C backend compiler.
|
||||||
For example: `-cc gcc-9 -cflags -fsanitize=thread`.
|
For example: `-cc gcc-9 -cflags -fsanitize=thread`.
|
||||||
|
|
||||||
|
### C types
|
||||||
|
|
||||||
Ordinary zero terminated C strings can be converted to V strings with `string(cstring)` or `string(cstring, len)`.
|
Ordinary zero terminated C strings can be converted to V strings with `string(cstring)` or `string(cstring, len)`.
|
||||||
|
|
||||||
NB: `string/1` and `string/2` do NOT create a copy of the `cstring`, so you should NOT free it after calling `string()`. If you need to make a copy of the C string (some libc APIs like `getenv/1` pretty much require that, since they
|
NB: Each `string(...)` function does NOT create a copy of the `cstring`, so you should NOT free it after calling `string()`. 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)`
|
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).
|
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))` .
|
These can be converted to V strings with `string_from_wide(&u16(cwidestring))` .
|
||||||
|
@ -1920,7 +1937,9 @@ 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)`.
|
`voidptr` can also be dereferenced into a V struct through casting: `user := User(user_void_ptr)`.
|
||||||
|
|
||||||
[Socket.v has an example which calls C code from V](https://github.com/vlang/v/blob/master/vlib/net/socket.v) .
|
[socket.v has an example which calls C code from V](https://github.com/vlang/v/blob/master/vlib/net/socket.v) .
|
||||||
|
|
||||||
|
## Debugging generated C code
|
||||||
|
|
||||||
To debug issues in the generated C code, you can pass these flags:
|
To debug issues in the generated C code, you can pass these flags:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue