Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/atilaneves/dpp
Directly include C headers in D source code
https://github.com/atilaneves/dpp
clang cpp cpp17 d dlang dlanguage libclang translation
Last synced: 25 days ago
JSON representation
Directly include C headers in D source code
- Host: GitHub
- URL: https://github.com/atilaneves/dpp
- Owner: atilaneves
- License: bsl-1.0
- Created: 2017-08-01T19:43:29.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2024-06-11T20:04:21.000Z (5 months ago)
- Last Synced: 2024-09-26T01:31:50.738Z (about 1 month ago)
- Topics: clang, cpp, cpp17, d, dlang, dlanguage, libclang, translation
- Language: D
- Size: 1.48 MB
- Stars: 231
- Watchers: 18
- Forks: 31
- Open Issues: 86
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
d++ - #include C and C++ headers in D files
====================================================[![CI](https://github.com/atilaneves/dpp/actions/workflows/d.yml/badge.svg)](https://github.com/atilaneves/dpp/actions/workflows/d.yml)
[![Coverage](https://codecov.io/gh/atilaneves/dpp/branch/master/graph/badge.svg)](https://codecov.io/gh/atilaneves/dpp)
[![Open on run.dlang.io](https://img.shields.io/badge/run.dlang.io-open-blue.svg)](https://run.dlang.io/is/JK0CAf)Goal
----To directly `#include` C and C++ headers in [D](https://dlang.org) files and have the same semantics and ease-of-use
as if the file had been `#included` from C or C++ themselves. Warts and all, meaning that C `enum` declarations
will pollute the global namespace, just as it does "back home".This work was supported by [Symmetry Investments](http://symmetryinvestments.com/).
Example
-------```c
// c.h
#ifndef C_H
#define C_H#define FOO_ID(x) (x*3)
int twice(int i);
#endif
``````c
// c.c
int twice(int i) { return i * 2; }
``````d
// foo.dpp
#include "c.h"
void main() {
import std.stdio;
writeln(twice(FOO_ID(5))); // yes, it's using a C macro here!
}
```At the shell:
```
$ gcc -c c.c
$ d++ foo.dpp c.o
$ ./foo
$ 30
```[![Open on run.dlang.io](https://img.shields.io/badge/run.dlang.io-open-blue.svg)](https://run.dlang.io/is/WwpvhT)
C++ support
-----------C++ support is currently limited. Including any header from the C++
standard library is unlikely to work. Simpler headers might, the
probability rising with how similar the C++ dialect used is to
C. Despite that, dpp currently does try to translate classes,
templates and operator overloading. It's unlikely to work on
production headers without judicious use of the `--ignore-cursor` and
`--ignore-namespace` command-line options. When using these, the user
can then define their own versions of problematic declarations such as
`std::vector`.Limitations
-----------* Only known to work on Linux with libclang versions 6 and up. It might work in different conditions.
* When used on multiple files, there might be problems with duplicate definitions depending on imports.
It is recommended to put all `#include`s in one `.dpp` file and import the resulting D module.
* Not currently able to translate Linux kernel headers.Success stories
--------------Known project headers whose translations produce D code that compiles:
* nanomsg/nn.h, nanomsg/pubsub.h
* curl/curl.h
* stdio.h, stdlib.h
* pthread.h
* julia.h
* xlsxwriter.h
* libvirt/libvirt.h, libvirt/virterror.h
* libzfs
* openssl/ssl.h
* imapfilter.h
* libetpan/libetpan.h
* Python.hCompilation however doesn't guarantee they work as expected and YMMV. Please consult the examples.
Command-line arguments
----------------------It is likely that the header or headers need `-I` flags to indicate paths to be searched,
both by this executable and by libclang itself. The `--include-path` option can be
used for that, once for each such path.Use `-h` or `--help` to learn more.
Details
-------`d++` is an executable that wraps a D compiler such as dmd (the default) so that D files with `#include`
directives can be compiled.It takes a `.dpp` file and outputs a valid D file that can be compiled. The original can't since D
has no preprocessor, so the `.dpp` file is "quasi-D", or "D with #include directives".
The only supported C preprocessor directive is `#include`.The input `.dpp` file may also use C preprocessor macros defined in the file(s) it `#include`s, just as a C/C++
program would (see the example above). It may not, however, define macros of its own.`d++` goes through the input file line-by-line, and upon encountering an `#include` directive, parses
the file to be included with libclang, loops over the definitions of data structures and functions
therein and expands in-place the relevant D translations. e.g. if a header contains:```c
uint16_t foo(uint32_t a);
```The output file will contain:
```d
ushort foo(uint a);
```d++ will also enclose each one of these original `#include` directives with either
`extern(C) {}` or `extern(C++) {}` depending on the header file name and/or command-line options.As part of expanding the `#include`, and as well as translating declarations, d++ will also
insert text to define macros originally defined in the `#include`d translation unit so that these
macros can be used by the D program. The reason for this is that nearly every non-trivial
C API requires the preprocessor to use properly. It is possible to mimic this usage in D
with enums and CTFE, but the result is not guaranteed to be the same. The only way to use a
C or C++ API as it was intended is by leveraging the preprocessor.Trivial literal macros however(e.g. `#define THE_ANSWER 42`) are translated as
D enums.As a final pass before writing the output D file, d++ will run the C
preprocessor (currently the cpp binary installed on the system) on the
intermediary result of expanding all the `#include` directives so that
any used macros are expanded, and the result is a D file that can be compiled.In this fashion a user can write code that's not-quite-D-but-nearly that can "natively"
call into a C/C++ API by `#include`ing the appropriate header(s).Translation notes
----------------### enum
For convenience, this declaration:
```c
enum Enum { foo, bar, baz }
```Will generate this translation:
```d
enum Enum { foo, bar, baz }
enum foo = Enum.foo;
enum bar = Enum.bar;
enum baz = Enum.baz;
```This is to mimic C semantics with regards to the global namespace whilst also allowing
one to, say, reflect on the enum type.### Renaming enums
There is the ability to rename C enums. With the following C definition:
```c
enum FancyWidget { Widget_foo, Widget_bar }
```Then adding this to your .dpp file after the `#include` directive:
```d
mixin dpp.EnumD!("Widget", // the name of the new D enum
FancyWidget, // the name of the original C enum
"Widget_"); // the prefix to cut out
```will yield this translation:
```d
enum Widget { foo, bar }
```### Names of structs, enums and unions
C has a different namespace for the aforementioned user-defined types. As such, this is legal C:
```c
struct foo { int i; };
extern int foo;
```The D translations just use the short name for these aggregates, and if there is a name collision
with a variable or function, the latter two get renamed and have a `pragma(mangle)` added to
avoid linker failures:```d
struct foo { int i; }
pragma(mangle, "foo") extern export __gshared int foo_;
```### Functions or variables with a name that is a D keyword
Similary to name collisions with aggregates, they get an underscore
appended and a `pragma(mangle)` added so they link:```c
void debug(const char* msg);
```Becomes:
```d
pragma(mangle, "debug")
void debug_(const(char)*);
```Build Instructions
------------------
```
dub install dpp
```After the instructions for your OS (see below), you can use this commands to run dpp:
```
dub run dpp -- yoursourcefilenamehere.dpp
```Note: for a reproducible and cross-platform build environment, you can run [setup-cpp](https://github.com/aminya/setup-cpp) with `--llvm=11.0.0`. This will set up LLVM 11.0.0 and the proper environment variables.
### Windows
Install [LLVM](https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/LLVM-12.0.0-win64.exe) into `C:\Program Files\LLVM\`, making sure to tick the "Add LLVM to the system PATH for all users" option.
If `libclang.lib` was not found, put the `lib` folder of the llvm directory on the PATH.
### Linux
If `libclang` is not installed, install `libclang-10-dev` with apt: `sudo apt-get install -y -qq libclang-10-dev`
If `libclang.so` was not found, link it using the following command (adjust the installation path and the llvm version):
```
sudo ln -s path_to_llvm/lib/libclang-12.so.1 /lib/x86_64-linux-gnu/libclang.so
```### MacOS
If using an external LLVM installation, add these to your `~/.bash_profile`
```bash
LLVM_PATH="/usr/local/opt/llvm/" # or any other path
LLVM_VERSION="11.0.0"
export PATH="$LLVM_PATH:$PATH"
export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)
export LD_LIBRARY_PATH="$LLVM_PATH/lib/:$LD_LIBRARY_PATH"
export DYLD_LIBRARY_PATH="$LLVM_PATH/lib/:$DYLD_LIBRARY_PATH"
export CPATH="$LLVM_PATH/lib/clang/$LLVM_VERSION/include/"
export LDFLAGS="-L$LLVM_PATH/lib"
export CPPFLAGS="-I$LLVM_PATH/include"
export CC="$LLVM_PATH/bin/clang"
export CXX="$LLVM_PATH/bin/clang++"
```(adjust the clang version and the external llvm installation path.)
Then run `source ~/.bash_profile`
If `libclang.dylib` was not found, link it using the following command (adjust the installation path):
```
ln -s path_to_llvm/lib/libclang.dylib /usr/local/opt/llvm/lib/libclang.dylib
```