3

I am trying to hide the inclusion of a third party file in a main class header in a library I wrote, from the executables that link it. What I mean is:

I have a library that I wrote that defines class A. Class A inherits from class B (which is defined in a third party library). Example:

// In A.h
#include “B.h”
class A : public B
{
    A* something(A* val);
}

// In A.cpp
A* A::something(A*val)
{
    // Do something
    return val;
}

The header file for class B makes some changes to the environment that are ideal for my library, but are harmful to any executable that links my library. Someone pointed me to Opaque pointers as a potential solution, though I cannot see how I could use them to hide “B”.

Does anyone know a way to hide the inclusion of B.h? For solutions, C++11 is OK, but linking to additional dependancies (like boost) is not an option.

latreides
  • 379
  • 1
  • 4
  • 14
  • Do you really need A to inherit B? – drescherjm Dec 16 '13 at 03:06
  • @drescherjm Unfortunately yes. – latreides Dec 16 '13 at 07:33
  • For inheritance you *must* see the definition of `B`. You can avoid this by insteadd providing cast operators to `B&` and `const B&` in your class `A` and using the Pimpl idiom in your implementation. – Ralph Tandetzky Dec 16 '13 at 10:01
  • 1
    @RalphTandetzky, that won't help - any code that uses `B&` must include `B.h` which causes the problem again. – Mark Ransom Dec 16 '13 at 13:22
  • @MarkRansom I know, but the inclusion is hidden anyways and the client code does not have to include `B.h`, even though it can. In this manner at least compile-time dependencies are avoided. What would be the point of public inheritance anyways, if the `B` interface was not usable at all? – Ralph Tandetzky Dec 16 '13 at 17:18
  • @RalphTandetzky, perhaps I misunderstood you. When you suggested cast operators I assumed the returned reference would be used by the client code, which it couldn't do if `B.h` wasn't included. – Mark Ransom Dec 16 '13 at 17:26
  • @MarkRansom I only deal in pointers when it comes to A (there is never an instance of A that is not a pointer), but there are times (in templates that need to be accessable by the executable) when I access a member of a pointer of A, which seems to be a problem at this point. – latreides Dec 16 '13 at 20:51

2 Answers2

3

The way to use opaque pointers is to forward declare the classes you need to use so you don't need to include their definitions. Since you're not including B.h the clients of your library won't be polluted by their definitions.

// In A.h
class B;

class A
{
private:
    B* opaque;
};
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • But I am not declaring B as a member of A. A must inherit from B. Since B is not a pointer in this instance, how can I hide it with opaque pointers? It does not seem like a viable solution. I mentioned it only because it was suggested to me, and I could not see how it would solve my issue. – latreides Dec 16 '13 at 07:35
  • @latreides, if you really *must* derive from `B` then you're out of options, you're forced to include `B.h` for its definition. But if you can get away with simply having the same methods, just duplicate each method of `B` in `A` and have it call the opaque pointer for implementation. – Mark Ransom Dec 16 '13 at 13:35
  • Its a complicated inheritance that is much more than just some members and methods. To be specific, its what the Boehm Garbage Collector requires you to do so that instances of class A will be garbage collected correctly. – latreides Dec 16 '13 at 20:54
3

One normal way to hide the "implementation" in C++ world is by Pimpl/Handle-body/bridge idiom.

Instead of exposing your class A to user of your API, have a handle class that expose only what you want:

In A.h

class AImpl;  // forward declaration

class A {
private:
  AImpl* impl;

public:
  foo();
  bar();
}

Then have your actual implementation in another file:

AImpl.h

#include <B.h>

class AImpl: public B {
private:
public:
  foo();
  bar();
  somethingThatYouDontWantToExpose();
}
Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
  • Ok, this can definitely work, and answers the original question, but looking at my code, after implementing the above suggestion I would have an orphaned typedef that uses something from "B.h" and just declaring the typedef like I would the class AImpl above, without any information, does not seem to work. – latreides Dec 16 '13 at 08:08