I'm developing a C++ library where user's will provide complex inputs, such as matrices and quaternions. I don't want to have to reimplement these types so, internally, I'll be using the Eigen library.
I'm trying to decide on the best way to expose these types to my libraries' clients and have come up with a few options for my API. I use a quaternion type as an example, but this could apply equally to matrices and such. Also, although I'm specifically talking about exposing Eigen's types, I guess this question can apply equally well to other external libraries in use.
1) Use only basic C++ types
This option would require clients to pass data in via basic types. For example, for passing in a quaternion (4 elements), one could do:
void my_func(double my_quat[4])
2) Expose Eigen's Types
Eigen provides several templated types for arrays and quaternions. For
example, if a function requires a quaternion, I could use Eigen's Quaterniond
type (which is really a typedef for Quaternion<double>
):
void my_func(const Eigen::Quaterniond& my_quat)
3) Create a simple wrapper for the various types for clients
I could create a very simple quaternion type (say, some sort of simple struct) that clients would have to create (perhaps via some sort of factory function) to pass to my API:
void my_func(const quaternion_t& my_quat)
My library would convert the quaternion_t
type to my internal Eigen
representation.
I don't like option 1 too much since I want there to be a stronger sense of typing in my APIs. Option 2 would require my clients to use Eigen as well, not to mention potential problems with compatibility should they use a different version of Eigen (incidentally, Eigen is a header-only library if that matters). That leaves option 3.
What do folks think? Have I basically answered my own question? Any examples out there?
Related Questions
A related question was asked here but didn't really go into details of whether one should expose external types.
Exposing the 3rd party libraries is the easiest in the short term, but will most likely to bite you in the back in the long term. Easiest, because the types are alrady there, you do not need to come up with your own. Will bite you if you would want to use a different implementation library in the future, or would want to allow expansion of the data the client passes to you.
Using only basic types is is almost like coming up with your own, but it's much lower level, for no good reason. Your users will have a hard time using your library without constantly refering to the documentation on what's what.
Using your own types is the best option if you want flexibility down the line. It might seem like a lot of work up front as you need to re-create all the already existing types, but if you give it some tought, you might find that if you use slightly different types in your library's interface, it will facilitate implementation change better later on.
So the answer really depends on your goals and long-term plans/predictions: if you don't see yourself ever changing from your current implementation, you can go with re-using the existing types, but if you foresee/plan change in the future, you should create your own independent interface.
Wrap / encapsulate. Say you want to add some additional feature, such as caching the result of a computation, like the norm of a quaternion, as an implementation change. You can't do that (as easily) if you expose the 3rd party types without forcing your client code to change its call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With