Since it is not possible (yet) to produce binding from Rust to C++ library, I have take another approach.
- Create a C++ to C library:
quickfix-ffi/quickfix-bind
. - Create a C to Rust unsafe library:
quickfix-ffi
. - Create a Rust unsafe to safe library:
quickfix
.
Actually there is 3 components in the project
quickfix-ffi/libquickfix (lang: C++)
^
|
v
quickfix-ffi/quickfix-bind (lang: C)
^
| ABI=C
v
quickfix-ffi (lang: rust)
^
| Wrapper to add safety to `unsafe` bloc.
v
quickfix (lang: rust)
About C++ to C binding library:
.cpp
file is made of multiple macros to try making it as short as possible..h
contains less possible macro to make it easier to compare with rust code.
About compilation process:
-
Everything is statically linked in the final binary:
libquickfix
is rebuild from this repository using git sub repo.libquickfix_bind
is also build from here.
-
I have not implement search of existing library installation using
pkg-config
, but PR are welcomed.
There are few sub-components to make it works:
📄 FIX XML file (ex: FIX43.xml)
|
v
📦 quickfix-spec-parser: parse XML file to a rust struct / enum model.
|
v
📦 quickfix-msg-gen: generate code from the XML model.
|
v
📦 quickfix-msg43: contains a `build.rs` file to include generated code into a real crate.
Few more words on quickfix-spec-parser
: this crate is agnostic from any programming language.
It is a pure representation of the XML spec file as rust struct and enums.
It can be used to generate code, doc, whatever you want to.
Rust std::error::Error
and C++ exceptions cannot be match together.
Moreover:
- Rust
panic
should not leak into C++ code. - C++
throw
should not leak into Rust code.
So we have to:
-
Catch EVERY exceptions that can occurs in C++ code and convert them to error code.
- That is the purpose of the C macro:
CATCH_OR_RETURN
. - Error code can be converted back to text / code using
Fix_getLastErrorCode
andFix_getLastErrorMessage
.
- That is the purpose of the C macro:
-
Catch EVERY
panic
that can occurs in Rust code.- For this we are using
std::panic::catch_unwind
to wrap every user callbacks. - Sadly we do not cancel the control flow if this occurs.
- panic message will be displayed on screen but that's all... User can still register a new panic hook if needed.
- For this we are using
Wait, what about intentional control flow change (like DoNotSend
) ?
Here is how it works:
- User return
MsgToAppError::DoNotSend
from its callback. - enum is converted to an integer that will be passed to C code.
- depending on the integer code, exception will be throw from C++ in
ApplicationBind
.
Pros:
- No C binding library are required.
- All functions of the original library "should" be available.
Cons:
- Does not work for now since many C++ features are not available "correctly".
- inline function.
- vtable correct usage.
- operator overloading.
- stdlib is not correctly handle.
- Still require an unsafe to safe rust wrapper.
- Output code is just awful and terribly slow to compile.
I have found this project after starting the project. However to me it still have some issue.
Pro:
- Better support of C++ STL than bindgen.
- Looks more popular and supported than bindgen.
Cons:
- Still require manual writing of a bridge between Rust / C++ libraries. So why adding a library, I could make the bridge from standard C11 and use standard Rust. See more details on how it works here.
- rust-rdkafka: for how to build C library using cmake and link to rust project.