C++ cyclic dependency using class templates - how to refactor?

407 Views Asked by At

I am dealing with a cyclic dependency problem in C++.

The situation looks like the following:

libA.so:
    - Body.cpp
    - Header.cpp
    - DataObject.cpp
    - DataObject::read(boost::asio::streambuf* data)
      {
          boost::asio::streambuf data;

          ....

          body = (new DataConverter<Body>)->convert(&data);
          header = (new DataConverter<Header>)->convert(&data);
      }

  libB.so:
      - DataConverter.cpp
          -> DataConverter<T>
          -> T* DataConverter<T>::convert(boost::asio::streambuf* data)

  libA.so <-> libB.so

There is a cyclic dependency because libA uses the Converter-Class from libB and libB needs to now about the Object-Types of libA that need to be converted - since DataConverter::convert returns a Body or Header object.

I thought about solving this issue with forward declarations - but that does not seem to be the cleanest solution to me. All in all my plan was to provide an extensible DataConverter solution.

What would you guys suggest as best practice? A complete different design is also welcome :)

Best, Sebastian

4

There are 4 best solutions below

2
On

You might want to create abstract base classes defining the interface and hide the implementations (derived classes) from each other.

0
On

If you need a class template DataConverter, then this cannot be part of any compiled library. It has to be available via include files. Once you put the DataConverter code in a header, used by both libA and libB your cyclic dependency disappears.

0
On

Your design seems to be flawed. If two libraries, named A and B, are dependent on each other, it means that they must always be shipped together. If they must always be shipped together, it means that they are logically part of the same interface. It means that in fact, you have only one library.

There is not enough information to tell what would be the best solution, but here are some tips:

  1. Merge these libraries.
  2. Make one library dependent on the other one, for example, by moving DataConverter to libA.
  3. Create a utility library, dependent on these two.
  4. Create an appropriate interface in libB, using templates or virtual classes, and make libA dependent on libB. The latter (virtual classes) is very likely to be a better choice in dynamically-linked library.
0
On

Some alternatives:

  1. DataConverter as a fully generic implementation which will be instantianted in libA.so with proper types at compile time. This were a typical c++-ish solution. Your "convertable" types from libA (or others) would have to satisfy some Convertable concept which the fully templatized implementation of DataConverter would be using

  2. Dependency inversion, as proposed by JohnB. You'd basically accomplish the same goal, but with interfaces, implementations and registration/resolving at runtime. A lot more of work to do, but scalable, ABI achievable, deployable as library etc...

  3. Some kind of clever combination of both, something like Boost.Serialization. This is however difficult to achieve and easy to break...