代码之家  ›  专栏  ›  技术社区  ›  Mark Pim

将delphi集从c传递给外部delphi函数#

  •  3
  • Mark Pim  · 技术社区  · 15 年前

    我试图从C调用一个外部Delphi函数,它将Delphi集作为参数:

    德尔福代码

    type
      tStatus = (sIn, sOut, sAbsent, sSick);
      tStatusSet = set of tStatus;
    
    function LoadEmployees(tStatusSet aStatusSet): tEmpList;
    

    我需要整理一个枚举值的c数组(即 tStatus )以Delphi将作为 tStatusSet 类型:

    C码

    tStatusSet lStatusSet = ConvertToDelphiSet(sIn, sOut);
    
    tEmpList lEmpList = LoadEmployees(aStatusSet);
    

    ConvertToDelphiSet 理想情况下应该是一个通用的解决方案,能够处理任何枚举。我们的定义是:

    int ConvertToDelphiSet<T>(params T[] aArgs) {
      int lResult = 0;
    
      foreach (T lItem in aArgs)
      {
        int lValue = lItem.ToInt32();
        lValue = (int)Math.Pow(2, lValue);
    
        lResult |= lValue;
      }
    

    但这并没有返回正确的值(例如,将四个值都传递给 tStand delphi中的值只能看到集合中的第三个值)。

    有关于Delphi如何在内部表示一个集合的文档吗?它是所有值的简单位字段吗?有没有更有力的方法来实现这一点?这是未来的证据,还是我依赖的无证内部功能可能会改变?

    3 回复  |  直到 15 年前
        1
  •  10
  •   Deltics    15 年前

    我相信这是一个小领域,但可能不是你所说的“简单”。

    Delphi集可以有多达256个元素,或者具有高达255的元素,这可能不一定是相同的事情,因为EnUM成员可以被分配特定的值并且不一定是连续的,例如:

      TEnum = (a, b, c=255);
      TSet  = set of TEnum;
    

    结果一个tset的最大可能大小为256位,即使它只有3个可能的成员。(注意,成员“a”的值是0,而不是1!)

    如果对集合类型或该类型的变量使用sizeof()函数,则可以看到这一点,该函数将指示该类型或变量占用的存储字节数。

      TEnum = (a, b, c=255);
      TSet  = set of TEnum;
    
      >>> sizeof(TSet) = 32
    
    
      TEnum = (a, b, c);
      TSet  = set of TEnum;
    
      >>> sizeof(TSet) = 1
    

    您设计的任何依赖于delphi集合类型的内部存储的机制都将是脆弱的,并且将要求您在c和delphi中定义的枚举类型都是匹配的,但不能轻松/可靠地标识为不同步。

    如果这是一个实际问题,那么我建议您将值作为枚举成员数组传递 姓名 并在delphi中使用rtti在delphi端重建集合,方法是将枚举名转换为相应的枚举值,并根据需要将它们添加到集合中(如果指定了无效的枚举成员名 - 1 作为枚举值返回 GET枚举值 ):

      enumValue := TEnum( GetEnumValue(TypeInfo(TEnum), sEnumMemberName) );
      if Ord(enumValue) = -1 then
         raise Exception.Create('Invalid enum value' + sEnumMemberName);
    
      Include(setVar, enumValue);
    

    GetEnumValue() 以及相应的 getEnumName() 功能是 打字信息 单位。

    通过这种方式,您将能够检测并处理这样一种情况:C代码指定了一个枚举值(按名称),而Delphi代码无法识别该值(当然,反之亦然),这种情况在假设任意N字节块中的匿名位时可能不太可靠存储的。

    当然,您仍然必须确保您的c和delphi代码都使用相同的或容易映射的枚举成员名称。

    一个折衷方案可能是将值作为简单的字节数组传递=枚举的每个成员都有一个基本字节大小的序数值,所以只要C语言中的枚举成员与DELPHI中的枚举成员具有相同的底层序数值,就可以根据需要简单地按顺序排列它们。在非常粗糙的伪代码中,您应该能够很容易地适应:

    鉴于:

         enumValues: array of Byte;
         setVar: TSet;  // set of TEnum
    
      for i := 0 to Length(enumValues) do
        Include(setVar, TEnum(enumValues[i]));
    
        2
  •  4
  •   mghie    15 年前

    Delphi集的格式当然有文档记录,但是当您使用不同编程语言和开发环境中不常见的dll函数参数和函数结果的数据类型时,您会陷入困境。坚持使用windows api中使用的类型,就不会有这样的问题。

    对于不同的delphi版本,这也是正确的。当使用字符串或对象时,编译器将以相同的方式实现它们,并在内部使用相同的内存管理器。

    例如,使用 DWORD 并将集合中的每个元素编码为具有2的不同幂的常数。

        3
  •  2
  •   utku_karatas    15 年前

    理想情况下,您不应该将涉及接口的集合公开给其他语言,因为大多数语言没有这个概念,但从技术上讲,您想要的是可能的。

    正如您所说的,集合是Delphi自动为您管理的简单位字段,但它比这更棘手,因为集合变量的大小取决于集合元素的数量。集合可以有多达256个元素,这使得集合变量可以在内存中保存1到32字节。

    在这一点上,您可以在字节值上进行一些拨号,并将其传递给Delphi调用,因为您的集合只能包含4个元素。