Show how to call a function in a shared library (without dynamically linking to it at compile-time). In particular, show how to call the shared library function if the library is available, otherwise use an internal equivalent function.

This is a special case of calling a foreign language function where the focus is close to the ABI level and not at the normal API level.



Related task

 OpenGL -- OpenGL is usually maintained as a shared library.


#![allow(unused_unsafe)]
extern crate libc;
 
use std::io::{self,Write};
use std::{mem,ffi,process};
 
use libc::{c_double, RTLD_NOW};
 
// Small macro which wraps turning a string-literal into a c-string.
// This is always safe to call, and the resulting pointer has 'static lifetime
macro_rules! to_cstr {
    ($s:expr) => {unsafe {ffi::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()).as_ptr()}}
}
 
macro_rules! from_cstr {
    ($p:expr) => {ffi::CStr::from_ptr($p).to_string_lossy().as_ref() }
}
 
fn main() {
    unsafe {
        let handle = libc::dlopen(to_cstr!("libm.so.6"), RTLD_NOW);
 
        if handle.is_null() {
            writeln!(&mut io::stderr(), "{}", from_cstr!(libc::dlerror())).unwrap();
            process::exit(1);
        }
 
        let extern_cos = libc::dlsym(handle, to_cstr!("cos"))
                .as_ref()
                .map(mem::transmute::<_,fn (c_double) -> c_double)
                .unwrap_or(builtin_cos);
        println!("{}", extern_cos(4.0));
    }
}
 
fn builtin_cos(x: c_double) -> c_double {
    x.cos()
}