From 9d8116f8958972f29037780e39ad917b5f3ab994 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 8 Feb 2020 18:00:35 +0200 Subject: [PATCH] docs: tips for C/V interoperability --- doc/docs.md | 98 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 71 insertions(+), 27 deletions(-) diff --git a/doc/docs.md b/doc/docs.md index 6e1ab3a025..7bf77372ae 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -828,8 +828,6 @@ pub fn say_hi() { You can have as many .v files in `mymodule/` as you want. -Build it with `v build module ~/code/modules/mymodule`. - That's it, you can now use it in your code: ```v @@ -1067,12 +1065,17 @@ fn test_hello() { } ``` +`assert` keyword can be used outside of tests as well. + All test functions have to be placed in `*_test.v` files and begin with `test_`. -To run the tests do `v hello_test.v`. To test an entire module, do -`v test mymodule`. +To run the tests do `v hello_test.v`. -`assert` keyword can be used outside of tests as well. +To test an entire module, do `v test mymodule`. + +You can also do `v test .` to test everything inside your curent folder (and underneath it). + +You can pass `-stats` to v test, to see more details about the individual tests in each _test.v file. ## Memory management @@ -1223,14 +1226,19 @@ temporarily disabled). struct C.sqlite3 struct C.sqlite3_stmt +fn C.sqlite3_open(charptr, C.sqlite3) fn C.sqlite3_column_int(stmt C.sqlite3_stmt, n int) int +// Or just define the type of parameter & leave C. prefix +fn C.sqlite3_prepare_v2(sqlite3, charptr, int, sqlite3_stmt, charptr) int +fn C.sqlite3_step(sqlite3) +fn C.sqlite3_finalize(sqlite3_stmt) fn main() { path := 'users.db' - db := &C.sqlite3{!} // a temporary hack meaning `sqlite3* db = 0` + db := &C.sqlite3(0) // a temporary hack meaning `sqlite3* db = 0` C.sqlite3_open(path.str, &db) query := 'select count(*) from users' - stmt := &C.sqlite3_stmt{!} + stmt := &C.sqlite3_stmt(0) C.sqlite3_prepare_v2(db, query.str, - 1, &stmt, 0) C.sqlite3_step(stmt) nr_users := C.sqlite3_column_int(stmt, 0) @@ -1239,12 +1247,16 @@ fn main() { } ``` -Add `#flag` directives to the top of your V files to provide C compilation flags like `-l` for -linking, `-I` for adding include files locations, `-D` for setting compile time variables, etc. +Add `#flag` directives to the top of your V files to provide C compilation flags like: -You can use different flags for different targets. Right now, `linux`, `darwin` , and `windows` are supported. +- `-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 -For now you have to use one flag per line: +You can use different flags for different targets. Right now, `linux`, `darwin` , `freebsd`, and `windows` are supported. + +NB: For now you have to use one flag per line: ```v #flag linux -lsdl2 @@ -1254,19 +1266,56 @@ For now you have to use one flag per line: #flag linux -DIMGUI_IMPL_API= ``` -C strings can be converted to V strings with `string(cstring)` or `string(cstring, len)`. +You can also add C code, in your V module. For example, lets say that your C code is located in a folder named 'c' inside your module folder. Then: -V uses `voidptr` for C's `void*` and `byteptr` for C's `byte*` or `char*`. +```v +#flag -I @VMODULE/c +#flag @VMODULE/c/implementation.o +#include "header.h" +``` -To cast `voidptr` to V references use `user := &User(user_void_ptr)`. +... 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 `@VMODULE/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 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/compiler/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. +For example: `-cc gcc-9 -cflags -fsanitize=thread`. + +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 +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*`, +- `byteptr` for C's `byte*` and +- `charptr` for C's `char*`. +- `&charptr` for C's `char**` + +To cast `voidptr` to V references, use `user := &User(user_void_ptr)`. `voidptr` can also be dereferenced to V structs by casting: `user := User(user_void_ptr)`. -Check out socket.v for an example of calling C code from V: -[https://github.com/vlang/v/blob/master/vlib/net/socket.v](https://github.com/vlang/v/blob/master/vlib/net/socket.v) +Check out [socket.v for an example of calling C code from V](https://github.com/vlang/v/blob/master/vlib/net/socket.v) . -To debug issues with the C code, `v -show_c_cmd .` is useful. It prints the -C command that is used to build the program. +To debug issues with the generated C code, you can pass these flags: + +- `-cg` - produces a less optimized executable with more debug information in it. +- `-keep_c` - keep the generated C file, so your debugger can also use it. +- `-pretty_c` - run clang-format over the generated C file, so it looks nicer and is easier to read. +- `-show_c_cmd` - prints the C command that is used to build the program. + +For best debugging experience, you can pass all of them at the same time: `v -cg -keep_c -pretty_c -show_c_cmd yourprogram.v` , then just run your debugger (gdb/lldb) or IDE with the produced executable `yourprogram`. + +If you just want to inspect the generated C code, without compiling it further, you can also use: `-o file.c`. This will make V produce the `file.c` then stop. ## Compile time if @@ -1359,13 +1408,10 @@ in order to improve readability: To improve safety and maintainability, operator overloading has several limitations: --It's only possible to overload `+, -, *, /` operators. - -- Calling other functions inside operator functions is not allowed. - -- Operator functions can't modify their arguments. - -- Both arguments must have the same type (just like with all operators in V). +- It's only possible to overload `+, -, *, /` operators. +- Calling other functions inside operator functions is not allowed. +- Operator functions can't modify their arguments. +- Both arguments must have the same type (just like with all operators in V). ## Inline assembly @@ -1423,9 +1469,7 @@ If you have well-written, well-tested C code, then of course you can always simp Translating it to V gives you several advantages: - If you plan to develop that code base, you now have everything in one language, which is much safer and easier to develop in than C. - - 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