質問

Consider I'm writting a static library. Let it has a class Foo

// mylib.h
#include <dependency_header_from_other_static_library.h>

class Foo {
    // ...
private:
    type_from_dependent_library x;
}

As you can see this library (let call it mylib) depends on another library. It compiles well. But when user compile it's code (that uses Foo and includes mylib.h) and linking with my lib the compilation fails, because user need to have dependency_header_from_other_static_library.h header file to compile code as well.

I want to hide this dependency from the user. How this can be done? The one thing that comes to mind is a PIMPL idiom. Like:

// mylib.h
#include <dependency_header_from_other_static_library.h>

class Foo {
    // ...
private:
    class FooImpl;
    boost::shared_ptr<FooImpl> impl_;
}

// mylib_priv.h
class FooImpl {
    // ...
private:
    type_from_dependent_library x;
}

But it requires me to duplicate the interface of the class Foo in FooImpl. And, is it an overkill to use PIMPL in my case?

Thanks.

役に立ちましたか?

解決

When decoupling a header from other headers, there are a few approaches you might be able to use:

  1. If the used library makes a promise about how it declares its types, you may be able to forward declare the needed types in your header. Of course, this still means you can only refer to these types as pointers or in function signatures in the header but this may be good enough. For example, if the used library promises to have a class LibraryType that you need to use, you can do something like this:

    // Foo.h
    class LibraryType;
    class Foo {
        // ...
        LibraryType* data;
    };
    

    This may cut you the necessary slack to use the type without including its header and without jumping through a PImpl approach.

  2. If the library doesn't make a promise about how it declares it types you may use void* to refer to the corresponding types. Of course, this means that whenever you access the data in your implementation, you'll need to cast the void* to the appropriate type. Since the type is statically known, using static_cast<LibraryType*> is perfectly fine, i.e., there isn't any overhead due to the cast, but it is still relatively painful to do.

  3. The other alternative is, of course, to use the PImpl idiom. If you type provides any reasonably service, it will probably change the interface quite a bit and it shouldn't amount much to replicating the interface between the class itself and the privately declared type. Also, note that the private type is just a data container, i.e., it is reasonably to just make it a struct and have no protection to its accesses. The only real issue is that you need to make sure that the type's definition is visible at the point where the destructor is called. Using std::shared_ptr<T>(new T(/*...*)) arranges for this.

Effectively, all three approaches do the same thing although with slightly different techniques: they provide you an opaque handle to be used in the header file whose definition is only known to the implementation. This way, the client of the library doesn't need to include the corresponding header files. However, unless the symbols are resolved when building the library, it would still be necessary for the client to have access to the used library.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top