Overview
The focus of this document is on working with @cImport
and .h
files. Linking via the build system is not covered here.
While C
header files can be linked directly to Zig, C++
presents challenges. This is justifiably confusing as C/C++
header files can share the same header extension .h
.
Even though C++
has it’s own header extention (.hpp
), it is very common to find C++
projects that use a .h
extension instead.
The Problem
Using .h
files that actually link to .cpp
source files or are compiled with a C++
compiler present challenges for the Zig user due to several implementation details such as:
- Name Mangling
- Calling Convention
For more information on calling conventions, please checkout the following StackOverflow post: What are the different calling conventions in C/C++ and what do each mean? - Stack Overflow
Name Mangling according to IBM: Name mangling is commonly used to facilitate the overloading feature and visibility within different scopes.
IBM Documentation
These issues can cause Zig to fail at linking functions compiled by a C++
compiler via @cImport
at build time.
extern “C”
The C++
keyword extern "C"
tells the compiler to treat the function in a C
style. This works to our advantage in linking our functions to Zig.
The caveat is that C
does not recognize extern "C"
as a keyword. Thus anything marked with extern "C"
will cause @cImport
to complain.
A Solution
One way to solve this is to use a helper macro in your .h
files that you wish to link to .cpp
sources:
// head.h
#if defined(__cplusplus)
// if we are viewing it in C++ we see: extern "C"
#define EXTERN_C extern "C"
#else
// if we are viewing it in C we see: extern
#define EXTERN_C extern
#endif
// foo is interpreted based on who is viewing it
EXTERN_C void foo(int i, int j);
Then, in our .cpp
file, we only use the extern "C"
qualifier:
// body.cpp
#include "head.h"
extern "C" void foo(int i, int j) {
// insert mind-blowing function implementation here
}
Now, in main.zig
, we can import this file without trouble:
// main.zig
pub const C = @cImport({
@cInclude("/path/to/head.h");
});
// later...
C.foo(42, 42);