A tool that does the boring parts of translating C to D.
YouTube: DConf Online '22 - Translating C to D
From the root of this repository:
dub run --arch=x86_64 -- ../test/main.cArguments are .c or .h files, for which it outputs a translated .d file next to it.
This .d file may not compile yet, but it's supposed to get you 90% there.
The remaining errors are usually because of non-trivial macros, initializers, or because of D's stricter type system.
You can copy the built binary to a folder in your PATH, for example on linux:
dub build
sudo cp build/ctod /usr/local/bin/This allows you to simply invoke ctod from anywhere.
This was written before ImportC. Statically linking C libraries with dub is clumsy: while there's usually C bindings in the package repository, you still need to grab release binaries for each build target / configration, include them in the repo, and fiddle with dub flags until there are no linker errors anymore.
That's why I like to translate C code to D. While this requires some intelligence, a lot of the work is simple syntactic find-and-replace actions, and converting declaration syntax. Even with VIM macros it was getting tedious, so I wrote this tool. I used it for translating glfw and libsoundio, as well as some single-file C libs.
The tree-sitter parser for C is used to parse C code, including macros. tree-sitter is a parser generator with a focus on incremental parsing and error recovery, useful for IDE tools. Error recovery is particularly useful for this tool: it always gives a best-effort translated output, even when it encounters what it thinks is a parse error. If you want, you can convert broken C code to similarly broken D code.
This tool makes use of the fact that in general, if D code looks like C, it either behaves the same as C or doesn't compile. Also, D has almost all features that C has. The tool doesn't need a deep understanding of C code, and a lot of things are passed through without any change:
// Identical in both C and D
float fabs(float x) {
if (x < 0.0)
return -x;
return x;
}However, there's also a lot of things that are different between C and D. This ranges from simple syntax changes:
x->y=>x.y(float) 1lu=>cast(float) 1Lu"con" "cat"=>"con" ~ "cat"
To more difficult translations, such as declarations with complex types:
char *(*(*fun)[5])(int);D:
char* function(int)[5]* fun;It's being developed like this:
- Run
ctodon a C file - Look at the output, notice something that needs to be adjusted to be valid D
- Enter the C code in the tree-sitter playground to see what it looks like in the AST
- Add code to recognize and translate the pattern
- Repeat for as many patterns as possible
So this is really just a big find-and-replace tool that operates on an AST provided by tree-sitter.
You can take a look at testcases.md to see the capabilities of ctod.
If you only need to translate C headers and not actual code, there are also these tools available:
Those tools use libclang (the LLVM C parser api) to parse C instead of tree-sitter.