Hello, can someone provide a minimal example of how to export a function in zig and link it statically to the main c-program where it then gets called from? I couldnt find any info about how to do this anywhere.
I also wanna export a struct too btw
Hello, can someone provide a minimal example of how to export a function in zig and link it statically to the main c-program where it then gets called from? I couldnt find any info about how to do this anywhere.
I also wanna export a struct too btw
Somehow I canāt -femit-h
to get to work to output a C header, but basically:
bla.zig:
const Bla = extern struct {
a: i32,
b: i32,
c: i32,
};
export fn get_bla(a: i32, b: i32, c: i32) Bla {
return Bla{ .a = a, .b = b, .c = c };
}
ā¦compile into a library via zig build-lib bla.zig
.
Result shoul be a libbla.a
.
main.c:
#include <stdio.h>
typedef struct {
int a, b, c;
} Bla;
extern Bla get_bla(int a, int b, int c);
int main() {
const Bla bla = get_bla(23, 45, 67);
__builtin_dump_struct(&bla, printf);
}
ā¦compile the C source and link with the libbla.a:
zig cc zig cc main.c libbla.a -o main
ā¦you should see an exe main
, run that and:
> ./main
Bla {
int a = 23
int b = 45
int c = 67
}
ā¦the missing piece is generating a C header with public declarations instead of declaring the C struct and extern function manually in the C code, normally thereās an -femit-h
cmdline arg for that, but either Iām not using it right, or it currently doesnāt work.
Thank you for your help!
could you provide a minimal build script that compiles this? thats the main part i cant figure out
Btw, -femit-h
currently is expected to not work apparently:
A concrete build.zig is left as an excercise to the reader
Basically:
use addLibrary
to compile the zig file into a library as described here: Zig Build System ā” Zig Programming Language
use addExecutable
to compile the C file into an executable, but donāt add a root module, basically just
const exe = b.addExecutable(.{
.name = "bla",
.target = target,
.optimize = optimize,
link_libc = true,
});
ā¦then add the main.c
and the library:
exe.addCSourceFile(.{ b.path("main.c") });
exe.linkLibrary(lib);
Will try this later, thank you!
As a second semester college student the first sentence feels oddly familiar haha
I still does not work and I get the same error as before:
error: ld.lld: undefined symbol: hello_test()
note: referenced by PriorityQueue_zig_performance.cpp:4 (extra/PriorityQueue_zig_performance.cpp:4)
my build file section responsible for this is
const pq_mod = b.addModule("PriorityQueue", .{
.root_source_file = b.path("extra/PriorityQueue_zig_export.zig"),
.target = target,
.optimize = optimize,
});
const pq_lib = b.addLibrary(.{
.name = "PriorityQueue",
.root_module = pq_mod,
});
{
const exe = b.addExecutable(.{
.name = "zig_perf",
.target = target,
.optimize = optimize,
});
exe.addCSourceFile(.{
.file = b.path("extra/PriorityQueue_zig_performance.cpp"),
});
exe.linkLibCpp();
exe.linkLibrary(pq_lib);
b.installArtifact(exe);
b.installArtifact(pq_lib);
}
my zig file is
const std = @import("std");
export fn hello_test() callconv(.C) void {
std.io.getStdOut().writeAll("hello, world\n") catch unreachable;
}
and my cpp file is
extern void hello_test();
int main() {
hello_test();
return 0;
}
dont get confused by all the priorityqueue names, i wanna try to use the zig priority queue from c++ but first get a little demo working
This problem has to do with C++'s name mangling.
Since extra/PriorityQueue_zig_performance.cpp
has the file ending cpp
, the buildsystem assumes that this is a C++ file (and I guess you want it to be a C++ file too).
But this means that your
extern void hello_test();
will be assumed to be a C++ too.
So you need to tell your compiler to treat it like a C function, so that the compiler doesnāt apply name mangling to the declaration and make the linker capable of finding it.
You can do that like this:
extern "C" {
extern void hello_test();
}
If you look up the object file, the mangled name would be (on my Linux x86_64 system) _Z10hello_testv
instead of the hello_test
that you want.
OMG it works!!!
theres no way i couldve figured that out by myself, thank you so much
we have to use cpp for university and nothing made me appreciate zig more than using cpp
Tbf, you asked about C, not C++
In my example code, if you rename main.c
to main.cc
(e.g. so that it is compiled as C++), you also need to change:
extern Bla get_bla(int a, int b, int c);
to:
extern "C" Bla get_bla(int a, int b, int c);
ā¦that way the C++ compiler knows that this is an external C symbol which doesnāt use name mangling.
Yea I admit that I should have expressed myself more clearly, although I find it strange that this happened because with the exception of a single keyword, people always claim that all c is valid cpp
ā¦only people who didnāt follow the development of C and C++ since around the mid-1990s
C and C++ are different languages and have diverged significantly since C99 to a point where C99 code simply doesnāt compile anymore in C++ mode.
There are also plenty of differences even when trying to build C89 code. For instance union type punning is valid in C, but UB in C++.
It is possible to write a dialect that compiles both in C and C++ (e.g. the ācommon C/C++ subsetā), but this ābastard dialectā is not standard C.
The more you know, Iāll definitely tell some of my c friends about this!
āC++ friendsā most likely Iāve seen this mindset typically with C++ peeps who havenāt kept up with C development, never with actual C programmers.
No actually, the person in mind is really not a fan of cpp and I myself didnt know about this too, which is why I find it so surprising. The idea of cpp being a superset has to be one of the biggest commonly believed myths ive fallen a victim to until today
Let me tell you, C++ programmers arenāt the only people working with C who donāt keep up with Cās development. Thereās a ton of people working with C89/99 because of platform compatibility.
C89 vs C99 is exactly where I draw the line between āoldā and ānewā C, since C99 was a significant language update via designated init and compound literals. Everything after C99 was more or less just minor updates.
Especially the designated init feature in C99 still stands out as one of the most well-designed āafterthoughtā language features in any language. Every little detail just clicks, and most more recent languages (including Zig unfortunately, and not to mention the totally botched designated-init thing in C++20) miss one or another minor feature which might look unimportant on its own but is missing dearly when coming from C99.
I agree with you that C89 to C99 is the biggest language schism, but Iād also balk at calling a language standard older than some of the developers working with it ānewā.
I draw the line right after C99. C99 is usually supported sans a few major features we all know are poorly supported. Anything after is the ācheck your compiler documentation for supported featuresā part of C.
(Either way I donāt want to turn this thread into a religious war about C standards, lord knows thereās been enough of those.)