2012-05-14 10 views
5

Estoy desarrollando una biblioteca C++ donde el usuario proporcionará entradas complejas, como matrices y cuaterniones. No quiero tener que volver a implementar estos tipos, así que, internamente, usaré la biblioteca Eigen.En caso de que existan tipos de terceros expuestos en la API de mi biblioteca C++

Estoy tratando de decidir sobre la mejor manera de exponer estos tipos a los clientes de mis bibliotecas ' y he encontrado algunas opciones para mi API. Utilizo un tipo quaternion como ejemplo, pero esto podría aplicarse por igual a matrices y demás. Además, aunque estoy hablando específicamente de exponer los tipos de Eigen, supongo que esta pregunta puede aplicarse igualmente a otras bibliotecas externas en uso.

1) el uso de C++ sólo los tipos básicos

Esta opción requeriría clientes para pasar los datos a través de los tipos básicos. Para ejemplo, para pasar en un cuaternión (4 elementos), se podría hacer:

void my_func(double my_quat[4]) 

2) Exponer Tipos de Eigen

Eigen proporciona varios tipos de plantilla para matrices y cuaterniones. Para ejemplo, si una función requiere un cuaternión, que podría utilizar de Eigen Quaterniond tipo (que en realidad es un typedef para Quaternion<double>):

void my_func(const Eigen::Quaterniond& my_quat) 

3) Crear un contenedor simple para los diferentes tipos de clientes

que podría crear un tipo muy simple cuaternión (por ejemplo, algún tipo de estructura sencilla) que clientes tendrían que crear (tal vez a través de algún tipo de función de fábrica) para pase a mi API:

void my_func(const quaternion_t& my_quat) 

Mi biblioteca podría convertir el tipo quaternion_t en mi Eigen representación interna.

No me gusta la opción 1 demasiado porque quiero que haya un sentido más fuerte de escribiendo en mis API. La opción 2 requeriría que mis clientes también usaran Eigen, no para mencionar posibles problemas de compatibilidad si usaran una versión diferente de Eigen (dicho sea de paso, Eigen es una biblioteca de solo encabezado si importa eso ). Eso deja la opción 3.

¿Qué piensa la gente? ¿Básicamente respondí mi propia pregunta? ¿Hay algún ejemplo por ahí?

preguntas relacionadas

Una cuestión relacionada se le pidió here pero realmente no entró en detalles de si se debe exponer tipos externos.

+0

¿Qué pasa con la opción 3, con constructores que toman las opciones 1 y 2? La semántica de C++ le permite reenviar tipos de declaraciones lo suficientemente bien para que esto funcione (los clientes sin Eigen aún pueden incluir el encabezado y no fallar en el momento de la compilación). – Lalaland

+0

Estaba pensando en algo así, pero creo que estoy un poco confuso sobre cómo reenviar los tipos de plantilla typedef'd, aunque supongo que en mi caso probablemente limitaré a los clientes a pasar una instanciación específica de los tipos (como 'Quaternion ' en comparación con 'Quaternion '). – plasma

+0

También estoy un poco confundido acerca de qué pasaría si un cliente usa el constructor para hacer que el tipo de mi biblioteca sea de su tipo Eigen, pero están usando una versión diferente de Eigen que, digamos, puede tener un ligero cambio de implementación. – plasma

Respuesta

2

Envolver/encapsular. Supongamos que desea agregar alguna característica adicional, como el almacenamiento en caché del resultado de un cálculo, como la norma de un cuaternión, como un cambio de implementación.No puede hacer eso (tan fácilmente) si expone los tipos de terceros sin forzar a su código de cliente a cambiar su llamada.

+0

Estoy de acuerdo. Sin embargo, no necesariamente quiero exponer toda una matriz/cuaternión/etc. biblioteca propia, ya que la funcionalidad de mi biblioteca estará en un nivel superior. ¿Tendría sentido para mis clases contenedoras ofrecer una cantidad mínima de funcionalidad para comunicarse con mi API, y permitir que los clientes utilicen lo que quieran para realmente manipular los datos asociados con esos tipos? – plasma

+0

Las envolturas delgadas están bien. my_quaternion :: foo() {return their_quaternion.foo(); } está bien para las funciones que desea exponer. Como lo señaló @Attila anteriormente, el caso más dramático es uno en el que desea cambiar bibliotecas, una molestia con la que su cliente no debería tener que lidiar. Si cree que los clientes necesitarán un acceso reforzado a los objetos matemáticos completos de eigen: también ofrezca una función de conversión getEigen() y un ctor que tome como entrada eigen para ir y venir que garantizará que siempre funcionará incluso si vuelve a implementar en otra biblioteca. – djechlin

+0

¿Qué pasaría si el cliente que utiliza el ctor o la función getEigen() y mi biblioteca se compilan con diferentes versiones de Eigen? Supongo que si ambas versiones son compatibles con ABI, entonces estamos bien. Pero si algo cambiara, ¿esto no funcionaría más? En particular, estoy pensando en el caso en el que proporciono mi biblioteca como DSO, pero supongo que no sé qué sucedería si les doy a los clientes una biblioteca estática. – plasma

2

La exposición de las bibliotecas de terceros es la más fácil a corto plazo, pero es probable que te muerda por la espalda a largo plazo. Lo más fácil, porque los tipos están ahí por lo general, no es necesario que inventes el tuyo. Te morderá si quieres usar una biblioteca de implementación diferente en el futuro, o si deseas permitir la expansión de los datos que el cliente te transfiere.

Usar solo tipos básicos es casi como crear uno propio, pero es mucho más bajo, sin una buena razón. Sus usuarios tendrán dificultades para usar su biblioteca sin consultar constantemente la documentación sobre qué es qué.

Usar sus propios tipos es la mejor opción si desea flexibilidad en la línea. Puede parecer mucho trabajo por adelantado, ya que necesita volver a crear todos los tipos existentes, pero si le da algunos consejos, puede encontrar que si usa tipos ligeramente diferentes en la interfaz de su biblioteca, facilitará la implementación. cambia mejor después.

Así que la respuesta realmente depende de sus objetivos y planes/predicciones a largo plazo: si no se ve a sí mismo cambiando de su implementación actual, puede volver a usar los tipos existentes, pero si prevé/cambio de plan en el futuro, debe crear su propia interfaz independiente.

Cuestiones relacionadas