C/C++;API设计困境 我一直在分析C++中API设计的问题,以及如何在实现接口与语言分离时围绕语言中的一个大洞进行工作。

C/C++;API设计困境 我一直在分析C++中API设计的问题,以及如何在实现接口与语言分离时围绕语言中的一个大洞进行工作。,c++,interface,pimpl-idiom,C++,Interface,Pimpl Idiom,我是一个纯粹主义者,并且坚信将系统的公共界面与其实现相关的任何信息巧妙地分离开来。我每天都在一个巨大的代码库上工作,这个代码库不仅构建速度非常慢,主要是因为头文件拉了大量的其他头文件,而且作为客户机也非常难以挖掘某些东西的功能,因为该接口包含各种供公共、内部和私人使用的功能 我的库被分成几个层,每个层使用一些其他层。这是一种设计选择,向客户机公开每个级别,这样他们就可以通过使用较低级别的实体来扩展高级实体的功能,而无需使用我的存储库 现在问题来了。经过长时间的思考,我得出这样的结论:C++中没有

我是一个纯粹主义者,并且坚信将系统的公共界面与其实现相关的任何信息巧妙地分离开来。我每天都在一个巨大的代码库上工作,这个代码库不仅构建速度非常慢,主要是因为头文件拉了大量的其他头文件,而且作为客户机也非常难以挖掘某些东西的功能,因为该接口包含各种供公共、内部和私人使用的功能

我的库被分成几个层,每个层使用一些其他层。这是一种设计选择,向客户机公开每个级别,这样他们就可以通过使用较低级别的实体来扩展高级实体的功能,而无需使用我的存储库

现在问题来了。经过长时间的思考,我得出这样的结论:C++中没有任何方法可以将一个类的公共接口与细节分开,满足以下所有要求:

  • 它不需要任何代码复制/冗余。原因:它是不可伸缩的,虽然对于一些类型来说它是可以的,但是对于现实的代码库来说它很快就会变成更多的代码。代码库中的每一行都有维护成本,我更愿意花在有意义的代码行上

  • 它没有开销。原因:我不想为编译时众所周知(或者至少应该如此!)的东西支付任何性能

  • 这不是一个黑客。原因:可读性、可维护性,以及它的丑陋性

  • 据我所知,这是我的问题所在,在C++中有三种方法完全隐藏类从公共接口实现。

  • 虚拟接口:违反要求1(代码重复)和2(开销)
  • Pimpl:违反要求1和2
  • 重新解释将this指针强制转换为.cpp中的实际类。零开销,但会引入一些代码重复和违反(3)
  • C在这里获胜。定义一个不透明的句柄到你的实体和一组函数,把这个句柄作为第一个参数,很好地满足了所有的要求,但它不是惯用的C++。我知道有人会说“在写C++时只使用C风格”,但它并没有回答这个问题,因为我们在这里谈论一个习题C++解决方案。

    定义一个不透明的句柄到你的实体,一组函数,把这个句柄作为第一个参数,很好地满足了所有的要求,但它不是惯用的C++。 您仍然可以将其封装在类中。不透明句柄将是类的唯一私有数据成员,其实现不会以任何方式公开。在实现方面,它只是一个指向私有数据结构的指针,由类的成员函数取消引用。与C解决方案相比,这仍然是一个小小的改进,因为所有相关的数据和函数都将封装在一个类中,这使得客户端不必跟踪句柄并将其传递给每个函数

    是的,我想取消对指针的引用会带来一些微不足道的开销,但是C解决方案也会有同样的问题

    不需要代码复制,尽管它可以被认为是黑客(或者至少是不优雅的C++设计),但它肯定不是C语言中实现的相同方法。唯一的区别是C程序员对于“黑客”的门槛较低,因为他们的语言表达方式少。 我正在考虑的设计的大致草图(基本上与PIMPL相同,但仅将数据成员设置为不透明):

    //在实现文件中:
    名称空间{
    结构DrawingPenData
    {
    int厚度;
    红色;
    绿色;
    蓝色;
    //…描述对象或跟踪其状态所需的任何其他信息
    };
    }
    //ctor、dtor、成员函数等的定义。
    //例如:
    空心绘图笔::设置厚度(内部厚度)
    {
    //通过句柄获取对象数据。
    DrawingPenData*pData=重新解释(本->pPen);
    //更新厚度。
    pData->厚度=厚度;
    }
    

    如果您需要在
    DrawingPen
    上工作的私有函数,但不想在
    DrawingPen
    头中公开这些函数,则只需将它们放在实现文件中相同的匿名命名空间中,接受对类对象的引用。

    我尽可能理解这个问题,“我是一个纯粹主义者,坚信将系统的公共界面与其实现相关的任何信息巧妙地分离开来。”而且“这是一个向客户公开每一层的设计选择”不能(对我而言)以任何形式组合在一起有意义的方式。啊,你说你想要一个<代码> C++ +>代码>解决方案,只要它不使用你在代码> C++ +>代码>中不习惯的技巧。虽然我没有得到推理。为什么要这样的限制?或者,澄清,没有理由更喜欢<代码> C <代码>超过<代码> C++ >代码>这里因为没有任何阻止。你在
    C++
    中做了什么,你仍然可以在实际的实现代码中使用
    C++
    C
    有很多优势。@ChristianHackl很抱歉,这还不清楚。我会尝试在这方面做更多的扩展,因为这并不矛盾。假设你正在编写一个渲染引擎。一个yer可能是图形API上的一个精简抽象。您肯定希望将实现隐藏在那里,因为它依赖于API。但该库还提供了更高级别的实体,使用此抽象来渲染内容,如网格、灯光等。它不只是将库锁定在高级别实体上,而是公开较低级别的实体
    // In a header file:
    
    class DrawingPen
    {
      public:
    
         DrawingPen(...);   // ctor
         ~DrawingPen();     // dtor
    
         void SetThickness(int thickness);
         // ...and other member functions
    
      private:
         void *pPen;   // opaque handle to private data
    };
    
    // In an implementation file:
    
    namespace {
    struct DrawingPenData
    {
        int thickness;
        int red;
        int green;
        int blue;
        // ... whatever else you need to describe the object or track its state
    };
    }
    
    
    // Definitions of the ctor, dtor, member functions, etc.
    // For instance:
    
    void DrawingPen::SetThickness(int thickness)
    {
        // Get the object data through the handle.
        DrawingPenData *pData = reinterpret_cast<DrawingPenData*>(this->pPen);
    
        // Update the thickness.
        pData->thickness = thickness;
    }