Pregunta

¿Qué son las & # 8220; normales & # 8221; ¿Cómo hacer complementos en lenguajes compilados (C # / C / C ++ / D)? Estoy especialmente interesado en los enfoques de lenguaje independiente, pero el lenguaje específico no es inaceptable.

Por el momento, & # 8220; tiempo de compilación & # 8221; Los enfoques complementarios (solo incluya el código o no y todo funciona) son válidos, pero se prefieren las cosas que pueden migrar a un enfoque más dinámico.

Con respecto al tipo de tiempo de ejecución, estoy más interesado en la mecánica de cargar el complemento y otras cosas que en diseñar la interfaz del complemento / aplicación

EDITAR: Por cierto, el complemento sería un esclavo, no un maestro. La acción básica de un complemento sería que, en una situación determinada, se le solicitaría que hiciera su cosa " y recibir un objeto de entorno que debería usar para obtener lo que necesita para funcionar.

¿Fue útil?

Solución

Para los lenguajes compilados (donde compilado significa que el programa se ejecuta como un ejecutable nativo, sin ningún tipo de máquina virtual), es casi necesario utilizar algún tipo de enfoque de biblioteca compartida específica de la plataforma. En Windows, esto significa usar DLL.

Usted define su interfaz de plugin en términos de un conjunto de funciones (con nombres específicos, argumentos y convenciones de llamada). Luego, carga las direcciones de las funciones dentro de la biblioteca compartida y va a la ciudad. En Windows, esto significa usar GetProcAddress () y, luego, emitir el valor de retorno a un puntero de función del tipo apropiado en C, o cualquiera que sea el equivalente en el idioma que estés usando.

Otra opción que puede o no ser más conveniente sería ejecutar una máquina virtual para otro idioma desde su aplicación nativa, y tener los complementos codificados para ese idioma. Por ejemplo, podría ejecutar una máquina virtual de Python utilizando CPython y cargar dinámicamente los módulos de Python.

Otros consejos

Mono.Addins parece ser una buena solución para .NET. Creo que incluye la API para permitirte descargar complementos (o complementos) desde un repositorio y cargarlos dinámicamente en un ensamblado en ejecución.

Descubrí que las partes difíciles de los complementos son: encontrarlos, resolver sus dependencias y manejar los problemas de versión. La forma en que maneje estos problemas debe ser clara para usted y para su enchufe en los autores. Si se equivoca en estos problemas, no causará dolor al final. Me gustaría ver los lenguajes de scripting y las aplicaciones que usan complementos para obtener ideas sobre lo que funciona bien.

Los constructores estáticos son más a menudo que no, " inteligente " en el mal sentido Ya que tendrá que cargar (C / C ++: dlopen y amigos en Linux) los complementos de una en una de todas maneras (en el caso dinámico), puede inicializarlos de manera tan manifiesta como lo hace. Entre otras cosas, eso puede darle la oportunidad de rechazar los complementos sin las API esperadas.

Tenga en cuenta que no es necesario utilizar bibliotecas de carga dinámica para los complementos. También se pueden utilizar otros mecanismos: memoria compartida, sockets, etc ...

Realmente depende de lo que quieras hacer. El patrón común de Unix como se ve en Emacs y Gimp es escribir un programa que consiste en un pequeño componente compilado que expone la funcionalidad esencial que un componente interpretado usa para hacer todo. Los complementos que proporcionan una nueva funcionalidad que puede construirse sobre la aplicación son fáciles, pero debe ser muy flexible en los principios que proporcione para que esto sea posible. En el extremo opuesto, imagine un editor de fotos que pueda guardar en múltiples formatos. Desea permitir que las personas escriban sus propios controladores de formato de archivo. Esto requiere hacer que su código use un conjunto simple de primitivos, y luego elegir una implementación en tiempo de ejecución. En directo (Unix) C use dlopen, en C ++ use extern C que limita lo que puede hacer y dlopen. En Objective-C tienes una clase para hacerlo por ti. En el primer caso, usted está haciendo o reutilizando un intérprete, por lo que tiene libertad para hacerlo como quiera.

Para un complemento de tipo esclavo donde cada complemento encapsula una implementación diferente de un conjunto común de funciones, simplemente depositaría las DLL en una carpeta de complemento conocida por la aplicación host (por ejemplo, " c: \ archivos de programa \ myapp \ plugins), y luego llame a los DLL desde el host a través de Reflection.

Usted podría realizar algún tipo de proceso de registro elaborado cuando se instala cada DLL, pero nunca he experimentado ningún problema con el enfoque simple de plugins en una carpeta.

Editar: para hacer esto en C #, solo agregas una clase pública a la DLL (llamada " Plugin " o lo que sea), e implementas las funciones necesarias allí. Desde tu host, creas un objeto de tipo Plugin y llamas a sus métodos (todos con Reflexión).

Las interfaces con un registro vacío (EventSource) parecen funcionar bien. Vea IHttpModule.Init (HttpApplication ) para un ejemplo.

Esto permite que el autor de la aplicación (que controla EventSource) agregue eventos según sea necesario, sin necesidad de extender la interfaz de IPlugin (lo que inevitablemente lleva a IPluginEx, IPlugin2, IPlugin2Ex, etc.)

Un enfoque que he usado (en .NET) es hacer que el host realice una llamada inicial al complemento (a través de Reflection), iniciando el complemento y pasando una referencia al host que guarda el complemento. El complemento luego llama a los métodos en el host (también a través de la reflexión) según sea necesario.

Creo que con la mayoría de los complementos, las llamadas normalmente se realizarían en la otra dirección (es decir, el host llamaría al complemento según sea necesario). En mi caso, los propios complementos tenían elementos de IU que necesitaban hacer uso de la funcionalidad del host.

Dejando de lado los problemas de implementación del módulo de bajo nivel (por ejemplo, los DLL de Windows y los problemas de implementación), un motor de juego que uso solo tiene una función de registro global en los DLL, e intenta encontrarlo y llamarlo a cada dll en el directorio de complementos. La función de registro hace cualquier contabilidad necesaria para exponer la funcionalidad.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top