Calling C functions from V

  1. #flag -lsqlite3
  2. #include "sqlite3.h"
  3. // See also the example from https://www.sqlite.org/quickstart.html
  4. struct C.sqlite3 {
  5. }
  6. struct C.sqlite3_stmt {
  7. }
  8. type FnSqlite3Callback = fn (voidptr, int, &charptr, &charptr) int
  9. fn C.sqlite3_open(charptr, &&C.sqlite3) int
  10. fn C.sqlite3_close(&C.sqlite3) int
  11. fn C.sqlite3_column_int(stmt &C.sqlite3_stmt, n int) int
  12. // ... you can also just define the type of parameter and leave out the C. prefix
  13. fn C.sqlite3_prepare_v2(&sqlite3, charptr, int, &&sqlite3_stmt, &charptr) int
  14. fn C.sqlite3_step(&sqlite3_stmt)
  15. fn C.sqlite3_finalize(&sqlite3_stmt)
  16. fn C.sqlite3_exec(db &sqlite3, sql charptr, FnSqlite3Callback voidptr, cb_arg voidptr, emsg &charptr) int
  17. fn C.sqlite3_free(voidptr)
  18. fn my_callback(arg voidptr, howmany int, cvalues &charptr, cnames &charptr) int {
  19. unsafe {
  20. for i in 0 .. howmany {
  21. print('| ${cstring_to_vstring(cnames[i])}: ${cstring_to_vstring(cvalues[i]):20} ')
  22. }
  23. }
  24. println('|')
  25. return 0
  26. }
  27. fn main() {
  28. db := &C.sqlite3(0) // this means `sqlite3* db = 0`
  29. // passing a string literal to a C function call results in a C string, not a V string
  30. C.sqlite3_open('users.db', &db)
  31. // C.sqlite3_open(db_path.str, &db)
  32. query := 'select count(*) from users'
  33. stmt := &C.sqlite3_stmt(0)
  34. // NB: you can also use the `.str` field of a V string,
  35. // to get its C style zero terminated representation
  36. C.sqlite3_prepare_v2(db, query.str, -1, &stmt, 0)
  37. C.sqlite3_step(stmt)
  38. nr_users := C.sqlite3_column_int(stmt, 0)
  39. C.sqlite3_finalize(stmt)
  40. println('There are $nr_users users in the database.')
  41. //
  42. error_msg := charptr(0)
  43. query_all_users := 'select * from users'
  44. rc := C.sqlite3_exec(db, query_all_users.str, my_callback, 7, &error_msg)
  45. if rc != C.SQLITE_OK {
  46. eprintln(cstring_to_vstring(error_msg))
  47. C.sqlite3_free(error_msg)
  48. }
  49. C.sqlite3_close(db)
  50. }

#flag

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 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)

  1. #flag linux -lsdl2
  2. #flag linux -Ivig
  3. #flag linux -DCIMGUI_DEFINE_ENUMS_AND_STRUCTS=1
  4. #flag linux -DIMGUI_DISABLE_OBSOLETE_FUNCTIONS=1
  5. #flag linux -DIMGUI_IMPL_API=

#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:

  1. #pkgconfig r_core
  2. #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.

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:
  1. Module {
  2. name: 'mymodule',
  3. description: 'My nice module wraps a simple C library.',
  4. version: '0.0.1'
  5. dependencies: []
  6. }
  • Add these lines to the top of your module:
  1. #flag -I @VROOT/c
  2. #flag @VROOT/c/implementation.o
  3. #include "header.h"

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. The @VROOT folder is also prepended to the module lookup path, so you can import other 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. 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, 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. Another example, demonstrating passing structs from C to V and back again: interoperate between C to V to C.

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.

C types

Ordinary zero terminated C strings can be converted to V strings with unsafe { charptr(cstring).vstring() } or if you know their length already with unsafe { charptr(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*,
  • byteptr for C’s byte* and
  • charptr for C’s char*.
  • &charptr 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).

socket.v has an example which calls C code from V .