Skip to content

Parametrize dlopen feature names #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 24, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,5 @@ authors = ["Victor Berger <victor.berger@m4x.org>"]
license = "MIT"
description = "Helper macros for handling manually loading optional system libraries."


[dependencies]
libloading = "0.6"

[features]
dlopen = []
70 changes: 45 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
[![](http://meritbadge.herokuapp.com/dlib)](https://crates.io/crates/dlib)
[![Docs.rs](https://docs.rs/dlib/badge.svg)](https://docs.rs/dlib)

dlib is a small crate providing macros to make easy the use of external system libraries
that can or cannot be optionally loaded at runtime, depending on whether the `dlopen` cargo
feature is enabled.
dlib is a small crate providing macros to make easy the use of external system libraries that
can or cannot be optionally loaded at runtime, depending on whether a certain feature is enabled.

## Usage

dlib defines the `external_library!` macro, which can be invoked with way:
dlib defines the `external_library!` macro, which can be invoked in this way:

```rust
external_library!(Foo, "foo",
external_library!("dlopen-foo", Foo, "foo",
statics:
me: c_int,
you: c_float,
Expand All @@ -30,8 +29,9 @@ As you can see, it is required to separate static values from functions and from
having variadic arguments. Each of these 3 categories is optional, but the ones used must appear
in this order. Return types of the functions must all be explicit (hence `-> ()` for void functions).

If the feature `dlopen` is absent, this macro will expand to an extern block defining each of the
items, using the second argument of the macro as a link name:
If the feature named by the first argument (in this example, `dlopen-foo`) is absent on your crate,
this macro will expand to an extern block defining each of the items, using the third argument
of the macro as a link name:

```rust
#[link(name = "foo")]
Expand All @@ -47,9 +47,9 @@ extern "C" {

```

If the feature `dlopen` is present, it will expand to a `struct` named by the first argument of the macro,
with one field for each of the symbols defined, and a method `open`, which tries to load the library
from the name or path given as argument
If the feature named by the first argument is present on your crate, it will expand to a `struct`
named by the second argument of the macro, with one field for each of the symbols defined;
and a method `open`, which tries to load the library from the name or path given as an argument.

```rust
pub struct Foo {
Expand All @@ -73,37 +73,57 @@ with all of its fields pointing to the appropriate symbol.

If the library specified by `name` could not be found, it returns `Err(DlError::NotFount)`.

It will also fail on the first missing symbol, with `Err(DlError::MissingSymbol(symb))` where `symb` is a `&str`
containing the missing symbol name.
It will also fail on the first missing symbol, with `Err(DlError::MissingSymbol(symb))` where `symb`
is a `&str` containing the missing symbol name.

## Remaining generic in your crate

If you want your crate to remain generic over the `dlopen` cargo feature, simply add this to your `Cargo.toml`
If you want your crate to remain generic over dlopen vs. linking, simply add a feature to your `Cargo.toml`:

```
```toml
[dependencies]
dlib = "0.2"
dlib = "0.5"

[features]
dlopen = ["dlib/dlopen"]
dlopen-foo = []
```

And the library also provides helper macros to dispatch the access to foreign symbols:
Then give the name of that feature as the first argument to dlib's macros:

```rust
ffi_dispatch!(Foo, function, arg1, arg2);
ffi_dispatch_static!(Foo, static);
external_library!("dlopen-foo", Foo, "foo",
functions:
fn foo() -> c_int,
);
```

These will expand to the appropriate value or function call depending on the presence of the `dlopen` feature.
`dlib` provides helper macros to dispatch the access to foreign symbols:

```rust
ffi_dispatch!("dlopen-foo", Foo, function, arg1, arg2);
ffi_dispatch_static!("dlopen-foo", Foo, my_static_var);
```

These will expand to the appropriate value or function call depending on the presence or absence of the
`dlopen-foo` feature on your crate.

You must still ensure that the functions/statics or the wrapper struct `Foo` are in scope. For example,
you could use the [`lazy_static`](https://crates.io/crates/lazy_static) crate to do the initialization,
and store the wrapper struct in a static variable that you import wherever needed:

```rust
#[cfg(feature = "dlopen-foo")]
lazy_static::lazy_static! {
pub static ref FOO_STATIC: Foo =
Foo::open("libfoo.so").ok().expect("could not find libfoo");
}
```

You must still ensure that the functions/statics or the wrapper struct `Foo` are in scope. A simple pattern would be
for example to use the `lazy_static!` crate to do the initialization and store the wrapper struct in a static, that you then
just need to import everywhere needed. Then, it can become as simple as putting this on top of all modules using the FFI:
Then, it can become as simple as putting this on top of all modules using the FFI:

```rust
#[cfg(features = "dlopen")]
#[cfg(feature = "dlopen-foo")]
use ffi::FOO_STATIC;
#[cfg(not(features = "dlopen"))]
#[cfg(not(feature = "dlopen-foo"))]
use ffi::*;
```
67 changes: 27 additions & 40 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,32 @@ extern crate libloading;

pub use libloading::{Library, Symbol};

#[cfg(feature = "dlopen")]
#[macro_export]
macro_rules! ffi_dispatch(
($handle: ident, $func: ident, $($arg: expr),*) => (
($handle.$func)($($arg),*)
)
);
($feature: expr, $handle: ident, $func: ident, $($arg: expr),*) => (
{
#[cfg(feature = $feature)]
let ret = ($handle.$func)($($arg),*);
#[cfg(not(feature = $feature))]
let ret = $func($($arg),*);

#[cfg(not(feature = "dlopen"))]
#[macro_export]
macro_rules! ffi_dispatch(
($handle: ident, $func: ident, $($arg: expr),*) => (
$func($($arg),*)
)
ret
}
);
);

#[cfg(feature = "dlopen")]
#[macro_export]
macro_rules! ffi_dispatch_static(
($handle: ident, $name: ident) => (
$handle.$name
)
);
($feature: expr, $handle: ident, $name: ident) => (
{
#[cfg(feature = $feature)]
let ret = $handle.$name;
#[cfg(not(feature = $feature))]
let ret = &$name;

#[cfg(not(feature = "dlopen"))]
#[macro_export]
macro_rules! ffi_dispatch_static(
($handle: ident, $name: ident) => (
&$name
)
ret
}
);
);

#[macro_export]
Expand Down Expand Up @@ -132,12 +128,12 @@ macro_rules! dlopen_external_library(
$(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+,)|*
$(varargs: $(fn $vname: ident($($vargs: ty),+) -> $vret: ty),+,)|*
) => (
dlopen_external_library!(__struct,
$crate::dlopen_external_library!(__struct,
$structname, $(statics: $($sname: $stype),+,)|*
$(functions: $(fn $fname($($farg),*) -> $fret),+,)|*
$(varargs: $(fn $vname($($vargs),+) -> $vret),+,)|*
);
dlopen_external_library!(__impl,
$crate::dlopen_external_library!(__impl,
$structname, $(statics: $($sname: $stype),+,)|*
$(functions: $(fn $fname($($farg),*) -> $fret),+,)|*
$(varargs: $(fn $vname($($vargs),+) -> $vret),+,)|*
Expand All @@ -146,32 +142,23 @@ macro_rules! dlopen_external_library(
);
);

#[cfg(not(feature = "dlopen"))]
#[macro_export]
macro_rules! external_library(
($structname: ident, $link: expr,
($feature: expr, $structname: ident, $link: expr,
$(statics: $($sname: ident: $stype: ty),+,)|*
$(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+,)|*
$(varargs: $(fn $vname: ident($($vargs: ty),+) -> $vret: ty),+,)|*
) => (
link_external_library!(
$link, $(statics: $($sname: $stype),+,)|*
#[cfg(feature = $feature)]
$crate::dlopen_external_library!(
$structname, $(statics: $($sname: $stype),+,)|*
$(functions: $(fn $fname($($farg),*) -> $fret),+,)|*
$(varargs: $(fn $vname($($vargs),+) -> $vret),+,)|*
);
);
);

#[cfg(feature = "dlopen")]
#[macro_export]
macro_rules! external_library(
($structname: ident, $link: expr,
$(statics: $($sname: ident: $stype: ty),+,)|*
$(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+,)|*
$(varargs: $(fn $vname: ident($($vargs: ty),+) -> $vret: ty),+,)|*
) => (
dlopen_external_library!(
$structname, $(statics: $($sname: $stype),+,)|*
#[cfg(not(feature = $feature))]
$crate::link_external_library!(
$link, $(statics: $($sname: $stype),+,)|*
$(functions: $(fn $fname($($farg),*) -> $fret),+,)|*
$(varargs: $(fn $vname($($vargs),+) -> $vret),+,)|*
);
Expand Down