tcc 0.1.0

Rust wrapper around the Tiny C Compiler
Documentation
use tcc;

// A Rust function which says hello, to be passed to our C code.
extern "C" fn say_hello_rust(who: *const ::std::os::raw::c_char) {
    let who = unsafe { ::std::ffi::CStr::from_ptr(who) };
    let who = who.to_str().unwrap();
    println!("Hello {} from Rust!", who);
}

fn main() -> Result<(), ()> {
    let say_hello_c = {
        // Let’s create our main Tcc struct!
        let mut tcc = tcc::Tcc::new();

        // Then set the output type, it is possible to create executable files or libraries, but here
        // we want to demonstrate a JIT instead, so let’s build in memory.
        tcc.set_output_type(tcc::OutputType::Memory)?;

        // Now we define a function, it takes one string argument and prints hello on stdout.
        tcc.compile_string(r#"
            #include <tcclib.h>

            void say_hello(const char *who) {
                printf("Hello %s from C!\n", who);
            }
        "#)?;

        // Relocating is required since we want to obtain this say_hello() function afterwards.
        match tcc.relocate() {
            // And here we retrieve it by its symbol name.
            Ok(mut tcc) => tcc.get_symbol("say_hello")?,

            // This failed.
            Err(tcc) => panic!("Relocation failed for {:?}", tcc)
        }
    };

    // We have to create a new Tcc struct to compile a second program.
    let mut tcc = tcc::Tcc::new();

    // We can set a custom error handling, if it isn’t set it will print errors and warnings to
    // stdout.
    tcc.set_error_func(Some(Box::new(|string| eprintln!("tcc: {}", string))));

    // We still want to JIT, so use a memory output type again.
    tcc.set_output_type(tcc::OutputType::Memory)?;

    // Our main C code.
    tcc.compile_string(r#"
        #include <tcclib.h>

        void hello1(const char *who);
        void hello2(const char *who);

        int main(int argc, char *argv[]) {
            const char *arg = (argc > 1) ? argv[1] : "world";
            hello1(arg);
            hello2(arg);
        }
    "#)?;

    // Now we pass both say_hello() functions as symbols to our C code.
    tcc.add_symbol("hello1", &say_hello_c)?;
    tcc.add_symbol("hello2", &(say_hello_rust as *const ::std::os::raw::c_void))?;

    // And now we can call our JIT’d program, the arguments are passed to main() as usual.
    tcc.run(&["argv[0]", "you"])?;

    // That’s it!
    Ok(())
}