V and C
Including C code
You can 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 top-level folder of your module (if you
created your module with
v new
you already have v.mod file). For example:
- Add these lines to the top of your module:
@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 a 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. Another example, demonstrating passing structs from C to V and back again: interoperate between C to V to 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 file 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.
Each flag must go on its own line (for now)
In the console build command, you can use:
-cc
to change the default C backend compiler.-cflags
to pass custom flags to the backend C compiler (passed before other C options).-ldflags
to pass custom flags to the backend C linker (passed after every other C option).
For example:
v -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
to pkgconfig (not to V).
In other words, both lines below do the same:
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.
C types
Ordinary zero terminated C strings can be converted to V strings with
or if you know their length already with:
The
.vstring()
and.vstring_with_len()
methods do NOT create a copy of thecstring
, 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 likegetenv
pretty much require that, since they return pointers to internal libc memory), you can usecstring_to_vstring(cstring)
.
On Windows, C APIs often return so called wide
strings (utf16 encoding).
These can be converted to V strings with
V has these types for easier interoperability with C:
voidptr
for C'svoid*
&byte
for C'sbyte*
&char
for C'schar*
&&char
for C'schar**
To cast a voidptr
to a V reference, use:
voidptr
can also be dereferences into a V struct through casting:
See Example of a module that calls C code from V for a complete example.
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:
Members of sub-data-structures may be directly declared in the containing struct as below:
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 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:
Translating C to V
V can translate your C code to human-readable V code, and generate V wrappers on top of C libraries.
C2V currently uses Clang's AST to generate V, so to translate a C file to V, you need to have Clang installed on your machine.
Let's create a simple program test.c first:
Run v translate test.c
, and V will generate test.v:
To generate a wrapper on top of a C library use translate wrapper
command:
v translate wrapper c_code/libsodium/src/libsodium
This will generate a directory libsodium
with a V module.
See libsodium wrapper generated with C2V.
When should you translate C code and when should you simply call C code from V?
If you have well-written, well-tested C code, then of course you can always simply call this C code from V.
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.
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.
See Call V from C Example for a complete example.