首页 > 解决方案 > 有什么方法可以在 DelphiInterface 上实现 dynamic_cast 以获得底层对象类?

问题描述

我正在编写代码来实现跨不同后端系统的一组通用功能。(例如在不同的订单处理系统中提出销售订单)。我已经抽象了这些功能并为它们定义了 Delphi 风格的接口。为了允许在不同的后端系统中分阶段实施,每个系统都可以支持不同的功能块。(例如“提高销售订单”功能)。所以有一个核心的API接口,它可以让我们找出安装了哪些能力(有一个能力接口),并获得一个特定的命名能力(每个命名能力都有自己的接口)。

因此,我正在实现一系列对象类,这些对象类同时实现了 IAPICapability 接口和特定的函数接口(例如 IAPIRaiseSalesOrder 接口)。IAPICapability 接口有一个 CanInstallTo 方法,该方法确定是否可以将特定功能加载到核心 API 接口中。

我想要做的是确定我尝试安装的 API 是否是此模块的正确 API 类型。

我最初指定将 IInterface 传递给 CanInstallTo,但我找不到任何方法将其转换为对象,以便我可以使用 dynamic_cast 来确定它是否是该类期望插入的内容。

查看文档 DelphiInterface 包括运算符重载以获取基础指针。我希望这在 C++ 术语中实际上是一个带有 vtable 的对象指针 - 因为在 C++ 术语中,接口是通过多重继承实现的。

但是,我还没有找到可以将其转换为对象指针的代码。

我可以在 IAPIInterface 中包含一个“签名”并检查它,但这不允许我使用相同的对象类来实现多种类型的后端(使用接口应该允许我这样做)。我可以允许“SupportsSignature”方法实现多种类型的后端......但如果我有一个具有类继承的对象,我应该能够使用dynamic_cast。

现在我已经更改了将 TComponent * 传递给 CanInstallTo 的规范,但这要求使用 TComponent 作为基类,这不是一个探针,但不符合使用抽象接口的一般概念。

此代码不起作用,但可以让我了解我正在尝试做的事情:

bool TAPICapabilityModule::CanInstallTo(_di_IInterface Target)
{
bool              blRc=false;
TWantedAPIType  * pAPI;

  if((pAPI=dynamic_cast<TWantedAPIType *>(Target))!=NULL)
  { // continue with other checks if needed
    if(pAPI->Capability[MY_CAPABILTY_ID]==NULL)
    {
      blRc=true;
    }
  }
  return(blRc);
}

我现在使用 TComponent * 作为核心 API 实现,但我想知道是否有人知道如何完全使用接口来完成它?

标签: c++delphic++builder

解决方案


我发现了一个模板辅助函数,它可以完全满足我的要求。我正在运行 Rio (10.3.1),它在系统文件 systobj.h 中实现 - 这应该包含在 <System.hpp> 中(这反过来可能包含在几乎所有使用 RTL 的东西中)。

此文件中的相关代码(它完全符合我的要求,包装对象指针上的 dynamic_cast)

  // --------------------------------------------------------------------------
  // Object <-> Interface cast helpers
  // --------------------------------------------------------------------------
  struct __declspec(uuid("{CEDF24DE-80A4-447D-8C75-EB871DC121FD}")) __IObjCastGUIDHolder;

  template <typename DESTOBJ>
  DESTOBJ* _interfaceToObjectCast(IInterface* src)
  {
    if (src)
    {
      TObject *obj = 0;
      src->QueryInterface(__uuidof(__IObjCastGUIDHolder), reinterpret_cast<void**>(&obj));
      return dynamic_cast<DESTOBJ*>(obj);
    }
    return 0;
  }

它可以这样使用:

bool TAPICapabilityClass::CanInstallTo(_di_IInterface Target)
{
bool              blRc=false;
TWantedAPIType  * pAPI;

  if((pAPI=dynamic_cast<TWantedAPIType>(Target))!=NULL)
  { // continue with other checks if needed
    if(pAPI->Capability[MY_CAPABILTY_ID]==NULL)
    {
      blRc=true;
    }
  }
  return(blRc);
}

从消费应用程序的角度来看,它仍然是所有抽象接口,如下所示:

void TApplicationUnit::InstallCapability(TComponent *pAPI, TObject *pCapability)
{
_di_IAPICore        diAPI;
_di_IAPICapability  diCapability

  if( (System::Sysutils::Supports(pAPI, _uuidof(IAPICore), (void **)(&diAPI))) &&
      (System::Sysutils::Supports(pCapability, _uuidof(IAPICapability), (void **)(&diCapability))) &&
      (diCapability->CanInstallTo(diAPI)) )
  {
    diAPI->InstallCapability(diCapability);
  }
} 

我还没有达到可以正确测试代码的地步(但它可以编译)。

关于将抽象接口转换为对象是否是好的设计,评论中有一些有效的观点。正如我在 C++ 多重继承方面看到的那样,我希望能够在抽象类型之间进行动态转换。用例的设计目标要求各种后台系统的实现对消费应用程序隐藏,但是在多个类之间拆分功能需要实现类知道同一实现中的其他类(但不一定是它们的内部工作)。

从我对 Delphi 的了解来看,有一个松散的并行,即同一单元中的 Delphi 类能够以其他类不能仅仅因为它们在同一个单元中的方式相互访问。在许多情况下,您希望将抽象接口呈现给消费类,但在实现类的内部,您需要对其他类有更详细的了解。


推荐阅读