Search code examples
c++dependenciesshared-librariesmultiple-definition-error

How to add compatibility with library structure without adding dependency on library?


I am currently writing a library that will sometimes be used alongside OpenCV. Because OpenCV defines a Point_ class that is commonly used in some of the contexts my library will be used in, I'd like to add the option to pass in Point_s as arguments to some of my functions. That said, OpenCV is a pretty heavy library and I'd very much prefer not to depend on it just to get access to it's Point_ class.

Defining my own Point_ identical Point_ class causes the expected multiple definition error.

I considered using a pre-processor macro to check if the OpenCV header containing Point_ has already been included and only define it if it has not been, but I'm concerned that if my libraries header is included first, the multiple definition error will return and this will make my library difficult to use for anyone other than myself.

Is there a way to provide a definition that will only be used if there is not definition anywhere else, and/or will be overridden if one does appear elsewhere?


Solution

  • What you could do is define your library in terms of your point class and optionally generate conversions for the OpenCVlibrary types if present. Something like this:

    #ifdef HAVE_OPENCV
    #include <opencv2/opencv.hpp>
    #endif
    
    struct my_point
    {
        double x;
        double y;
    
    #ifdef HAVE_OPENCV
        my_point(cv::Point2d p): x(p.x), y(p.y) {}
    
        operator cv::Point2d() const { return {x, y}; }
    #endif
    };
    
    my_point my_function(my_point p)
    {
        return p;
    }
    
    int main()
    {
        cv::Point2d p;
    
        // automatic conversions happen between OpenCV version
        // and your library's version
        cv::Point2d q = my_function(p);
    }
    

    Because the conversion operators are trivial, inline functions, the compiler will optimize them away completely leaving the code as if no conversion were happening at all.

    Optionally (and preferably imo) you could make the conversions explicit which might make for safer code:

    struct my_point
    {
        double x;
        double y;
    
    #ifdef HAVE_OPENCV
        // make these explicit
        explicit my_point(cv::Point2d p): x(p.x), y(p.y) {}
    
        explicit operator cv::Point2d() const { return {x, y}; }
    #endif
    };
    
    my_point my_function(my_point p)
    {
        return p;
    }
    
    int main()
    {
        cv::Point2d p;
    
        // Now the library user needs to explicitly ask for
        // the conversions to take place
        cv::Point2d q = cv::Point2d(my_function(my_point(p)));
    }