es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo construir una biblioteca para plugins de Rust para una aplicación existente?

Me gustaría escribir una biblioteca para facilitar la escritura de complementos DLL para un programa no Rust, con una API/ABI en C, pero no sé cómo podría lograrlo.

En mi POC tengo funciones como:

#[no_mangle]
extern "C" fn init(mut *mut ctx) -> i32 { ... }

#[no_mangle]
extern "C" fn do_something(mut ctx) -> i32 { ... }

#[no_mangle]
extern "C" fn cleanup(*mut ctx) -> i32 { ... }

He utilizado una estructura Box para mi variable ctx.

Tengo mucho código de plantilla en mi POC para esto y me gustaría ponerlo todo en una biblioteca para evitar repetirlo en todas partes.

Así que básicamente mi objetivo es tener una librería que me permita simplemente definir el contexto personalizado y las funciones para cada complemento, algo así:

use plugin_lib;

struct MyCtx {
...
}

fn my_init(pp_ctx: *mut *mut MyCtx) -> i32 { ... }
fn my_do_something(p_ctx: *mut MyCtx) -> i32 { ... }
fn my_cleanup(p_ctx: *mut MyCtx) -> i32 { ... }

Pero si intento crear una biblioteca con funciones my_ faltantes, obviamente no se enlazará, ¿verdad? Y al mismo tiempo, si simplemente defino objetos en una librería e intento usarlos en la parte del complemento, no puedo hacer que exporte funciones a menos que las vuelva a escribir todas (o al menos envolver las llamadas a las de la librería, lo cual suena mal).

¿Cuál sería la forma correcta de construir esto? O si hay varias posibilidades, ¿cómo lo harías tú?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Un enfoque para lograr esto es utilizando la interfaz de funciones externas (FFI) de Rust y creando una biblioteca envolvente para la API en C del programa no Rust. La biblioteca envolvente puede proporcionar un conjunto de funciones y estructuras predefinidas, lo que facilita la escritura de complementos DLL para el programa no Rust.

    Para crear esta biblioteca envolvente, puedes definir un conjunto de estructuras y funciones comunes requeridas por los complementos. Estas estructuras y funciones se pueden declarar como extern "C" y marcarlas como #[no_mangle] para hacerlas accesibles desde otros lenguajes. Por ejemplo, puedes definir una estructura para almacenar el contexto de los complementos:

    #[repr(C)]
    pub struct PluginCtx {
        // Campos para el contexto del complemento
    }
    

    Luego, puedes definir un conjunto de funciones que los complementos deben implementar, como plugin_init, plugin_do_something y plugin_cleanup. Estas funciones pueden tomar un puntero a la estructura de contexto como argumento y devolver un entero para indicar éxito o fracaso:

    #[no_mangle]
    pub extern "C" fn plugin_init(ctx: *mut *mut PluginCtx) -> i32 {
        // Implementación para la inicialización del complemento
    }
    
    #[no_mangle]
    pub extern "C" fn plugin_do_something(ctx: *mut PluginCtx) -> i32 {
        // Implementación para la funcionalidad del complemento
    }
    
    #[no_mangle]
    pub extern "C" fn plugin_cleanup(ctx: *mut PluginCtx) -> i32 {
        // Implementación para la limpieza del complemento
    }
    

    Para facilitar la definición del contexto y las funciones específicas del complemento, puedes crear un trait en Rust que defina los métodos requeridos. Luego, cada complemento puede implementar este trait para su propia estructura de contexto:

    pub trait Plugin {
        fn init(&mut self) -> i32;
        fn do_something(&mut self) -> i32;
        fn cleanup(&mut self) -> i32;
    }
    

    Las funciones específicas del complemento se pueden implementar llamando a las funciones correspondientes en la biblioteca envolvente, pasando la estructura de contexto del complemento como argumento. Por ejemplo:

    impl Plugin for MyPluginCtx {
        fn init(&mut self) -> i32 {
            unsafe {
                plugin_lib::plugin_init(&mut self.ctx_ptr as *mut *mut PluginCtx)
            }
        }
    
        fn do_something(&mut self) -> i32 {
            unsafe {
                plugin_lib::plugin_do_something(self.ctx_ptr as *mut PluginCtx)
            }
        }
    
        fn cleanup(&mut self) -> i32 {
            unsafe {
                plugin_lib::plugin_cleanup(self.ctx_ptr as *mut PluginCtx)
            }
        }
    }
    

    Con este enfoque, los desarrolladores de complementos solo necesitan definir su propia estructura de contexto e implementar los métodos de complemento requeridos. Luego pueden vincular su complemento a la biblioteca envolvente, que proporciona las estructuras y funciones comunes requeridas por el programa no Rust.

Comments are closed.