How to use a dynamic library generated by Zig-lang in C++ codebase

Cengizhan Pasaoglu
CodeX
Published in
5 min readJan 22, 2022

In the past, I wrote a similar story about using a dynamic library with C++ and Python but it was in Turkish. Please write a comment or do not hesitate to contact me on any social media account if you want to make me translate it. Today, we will try to produce a dynamic library by using Zig programming language afterwards use it in C++ code. Let’s begin!

Before explaining an implementation detail of this codebase, maybe I should introduce my new favorite programming language Zig. Zig is a freshly-started programming language that encourages you to write metaprogramming style based on compile-time code. Its syntax is similar to Go language at least for me but it has many many unique and significant feature sets that I can explain in another story such as comptime, independent from Glibc, zero dependency, drop-in C/C++ compiler support and so on. Currently, it supports cross-platform compilation and could be installed from many different package managers that you can find them here.

In Zig, there are many different options and you can list them simply by writing “Zig” in the terminal. If you want to create an executable project from scratch you may want to use “zig init-exe”. On the other hand, if you want to create a library project, you can directly use “zig init-lib” command as you can see screenshot below.

Zig list all options/commands on the terminal

If you choose & run “zig init-lib” command above, Zig will generate build.zig file in the current directory and newly created “src” folder inside with main.zig file. In this simple example, we do not have any interaction with build.zig file which is mostly related to compiling, linking and testing configurations. We should go inside main.zig file and try to understand what is written by default.

Default main.zig file created by Zig itself

By default, you will see this content in the main.zig file and I will try to explain line by line. The first line, reminds you what? Oh, same for me. It is like using directive of C# or a combination of using directive and include preprocessor of C++. The second line is a great example of alias usage. It simplifies the usage of testing functionality which is supported by language standards.

Afterwards, you can guess that it is a function call that takes two i32 type parameters and return the sum of them in the same type. “fn” is a keyword for representing a function (or could be an abbreviation), and afterwards “add” is its name. Then parameter syntax is given like “parameter name-colon-type” and i32 equivalent of int32_t C type. The return type is located after these parameters as Go language does. If you are familiar with Golang, you might say that they are almost similar except for colons of parameters and keyword name choice for functions.

Test is another key feature of Zig language, you can directly use it in the source code. “test” keyword is standing for test case and afterwards you can give a name for the case. If you are familiar with unit test frameworks like GTest or Catch2 from C++, the next line is simply understandable, right?

Expectation vs Reality on testing

In-depth explanation, you might realize that one thing I did not mention yet intentionally. Oh right, “export” keyword. With the new example below, I will explain it in more detail.

Export keyword usage in Zig

Let’s assume that there are almost similar two function calls named “add_internal” and “add_external”. The second function has another keyword “export” which makes it publicly available. Let’s compile this code block and generate a dynamic library.

To generate a dynamic library from a file, you have to give “-dynamic” option explicitly because Zig generates a static library by default. So, after running the “zig build-lib -dynamic {filename}” command, we will have a dynamic library. Let’s check that our function call is located in the symbol table of the dynamic library. As seen in the screenshot below, “add_external” is located in the symbol table but “add_internal” is not, as we expected.

“add_external” in the symbol table

Now, we are ready to use it in C++ code. Let’s write a simple C++ code to use function immediately which was written in Zig. Here is the simple a few line C++ code to compile & run.

Simple C++ code to use Zig-based function

Now, everything is OK, right? Oops, compile stage seems OK but linkage we have a problem here. Why?

Linkage error for add_internal function.

A few minutes later… Oh, right mangling! Compile stage was OK because there is no conflict at all for function names. However, “ld” could not find the implementation details of this function because the function name was mangled. Then, the next question is how we can disable mangling for this function? Hello, “extern Cmy old friend. Let’s modify our function signature by adding this keyword in front of it like below.

After adding extern keyword to function signature

Now, we know that our function signature will not be mangled anymore and we can use it as it is. Let’s compile & run it again without any error.

Printed output after successful compilation

Perfect! Now, we are ready to use our implementation in C++ code base. I hope that you also enjoy from this journey. In next story, I will try to explain more attractive functionality from Zig language. Please share your thoughts about this story here or any place where you can find me!

--

--