0

我正在处理 C++ 中的循环依赖问题。

情况如下所示:

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

存在循环依赖关系,因为 libA 使用来自 libB 的 Converter-Class,而 libB 现在需要了解需要转换的 libA 的对象类型 - 因为 DataConverter::convert 返回一个 Body 或 Header 对象。

我想过用前向声明来解决这个问题——但这对我来说似乎不是最干净的解决方案。总而言之,我的计划是提供一个可扩展的 DataConverter 解决方案。

你们会建议什么作为最佳实践?也欢迎完全不同的设计:)

最好的,塞巴斯蒂安

4

4 回答 4

8

如果您需要一个类模板DataConverter,那么它不能是任何已编译库的一部分。它必须通过包含文件可用。一旦你把DataConverter代码放在一个标题中,两者都使用libAlibB你的循环依赖就会消失。

于 2012-09-01T10:29:48.610 回答
3

你的设计似乎有缺陷。如果名为 A 和 B 的两个库相互依赖,则意味着它们必须始终一起交付。如果它们必须始终一起交付,则意味着它们在逻辑上是同一接口的一部分。这意味着实际上,您只有一个库。

没有足够的信息来说明最佳解决方案,但这里有一些提示:

  1. 合并这些库。
  2. 使一个库依赖于另一个库,例如,通过将 DataConverter 移动到 libA。
  3. 创建一个实用程序库,依赖于这两个。
  4. 在 libB 中创建适当的接口,使用模板或虚拟类,并使 libA 依赖于 libB。后者(虚拟类)很可能是动态链接库中更好的选择。
于 2012-09-01T11:58:34.427 回答
1

您可能希望创建定义接口的抽象基类并相互隐藏实现(派生类)。

于 2012-09-01T10:26:14.147 回答
1

一些替代方案:

  1. DataConverter作为一个完全通用的实现,它将在编译时以适当的类型在 libA.so 中实例化。这是一个典型的 c++-ish 解决方案。您来自 libA(或其他)的“可转换”类型必须满足Convertable完全模板化实现DataConverter将使用的某些概念

  2. 依赖倒置,由 JohnB 提出。您基本上可以实现相同的目标,但在运行时使用接口、实现和注册/解析。还有很多工作要做,但可扩展、可实现 ABI、可部署为库等……

  3. 两者的某种巧妙组合,例如Boost.Serialization. 然而,这很难实现,也很容易打破......

于 2012-09-01T12:20:28.250 回答