如何将“数组”参数转换为C ++?

时间:2011-01-08 17:16:23

标签: delphi

我正在为我们的游戏引擎(用Delphi XE制作)开发c ++语言绑定。如何将 OleVariant 数组和数组const 参数转换为在C ++端正常工作?

function  DLM_CallRoutine(const aFullname: PWideChar;
const aParamList: array of OleVariant): OleVariant; stdcall;

function  DLM_CreateObject(const aClassName: PWideChar;
const aParamList: array of const): Integer; stdcall;

感谢。

3 个答案:

答案 0 :(得分:3)

Delphi有两个完全独立的数组语义,它们使用相同的array of代码语法用于不同的目的。

array of用于声明数据类型或变量时,正在使用Dynamic Array,例如:

type
  TIntegerArray: array of Integer;

var
  Array1: TIntegerArray;
  Array2: array of Integer;

在C ++中,这些对应于DynamicArray<T>模板类,例如:

typedef DynamicArray<int> TIntegerArray;

TIntegerArray Array1;
DynamicArray<int> Array2;

另一方面,当直接在函数参数中使用array of时(即,不使用typedef),则使用Open Array代替,即:

procedure DoSomething(Arr: array of Integer);
procedure DoSomethingElse(Arr: array of const);

传递给Open Array参数的值由编译器使用两个单独的参数传递 - 指向实际数组的指针,以及数组中最后一个元素的索引。 Delphi隐藏了这个事实,因此编码器只看到一个参数,并提供了一个简单的语法来指定参数值:

DoSomething([12345, 67890]);
DoSomethingElse(['Hello', 12345, True]);

但是,在C ++中,用于数组的两个参数是明确声明的,并且通常使用OPENARRAY()ARRAYOFCONST()宏指定值,例如:

// despite their names, the Size parameters are actually indexes.
// This misnaming has already been slated to be fixed in a future
// C++Builder release...
void __fastcall DoSomething(int const *Arr, const int Arr_Size);
void __fastcall DoSomethingElse(TVarRec const *Arr, const int Arr_Size);

DoSomething(OPENARRAY(int, (( 12345, 67890 )) );
DoSomethingElse(ARRAYOFCONST(( "Hello", 12345, true )) );

答案 1 :(得分:2)

在创建具有其他语言接口的代码时,最好避免使用特定于Delphi的类型和约定。如果您知道您的代码将与Delphi代码连接,您可以选择提供Delphi友好的接口,并使用“包装器”将Delphi友好的类型和机制映射到其他语言的更便携的类型和机制。

...所以

OLEVariant数组

由于您传递的是变量数组,显然您的C ++代码是变体感知/能力的,在这种情况下,我会将这些值本身传递给变量数组(作为Variant本身传递),保留数组的动态特性,但消除了对Delphi RTL特定“魔术”类型(动态数组)的担忧。

因此,您的Delphi代码可能有一个 Fn(OLEVariant数组),在将调用传递给实际的API代码之前,它会在内部将数组重新打包为Variant的Variant数组(伪代码):

  Fn(array of OLEVariant)
  begin
    arr := VarArrayCreate(...);
    try
      // ... init arr from array of OLEVariant parameter

      // call actual API fn:
      APIFn(arr);

    finally
      // Dispose of variant array
    end;
  end;

Const数组

这些值最终会在TVarRec数组中传递(不直接连接到变体,具有相似的意图)。再次,这是一个“神奇的”Delphi类型 - 你需要研究TVarRec类型并将其映射到一些等效的C ++类型。

在这种情况下,我将确定您需要在params列表中传递什么,并采用一种在两种语言之间更便携的机制。也许一个包含名称/值对的简单字符串分隔了参数名称和值的字符串?

在这种情况下,提供一个围绕API调用的Delphi友好包装将涉及与您当前使用的(const数组)略有不同的方法,因为const数组不提供数组中的命名条目。或者您可以选择简单地提供以字符串形式编码的分隔的类型/值集合,在这种情况下,您可以继续使用const的数组用于Delphi端,使用包装函数处理TVarRec数组以适当地格式化字符串用于API函数。

答案 2 :(得分:1)

如果我没记错的话,Delphi中的动态数组会在前几个字节中存储数组的大小。你必须声明你的接口函数是一个指针,加上一个大小:

function  DLM_CallRoutine(const aFullname: PWideChar; 
          aParamList: POleVariant; numParams :integer): OleVariant; stdcall;

调用者必须在Delphi中传递第一个实际元素的地址和元素的数量@A[0]Length(A)

注意:在涉及OleVariant和其他OLE类型的地方存在memmory管理/垃圾回收问题。您必须确保引用计数适当增加和减少。

Delphi Dynarrays

这在Delphi帮助的某处正确记录,可以通过查看System.pasSysUtils.pas的源代码来学习。以下内容来自记忆(对于这样的小帮助提前抱歉)。

德尔福dynarray是一个记录:

type
  TDynArray = record
    refcount :integer;
    size:    :integer;
    content  :array[size*elementSize] of byte;
  end;

@A[0]@content相同。要获取包含引用计数的地址,您必须进行一些转换。

C调用Delphi

你可以用C编写的客户端操作Delphi dynarrays的结构,但是为什么要对它们强加Delphi实现语义?如果C正在调用您的代码,那么无论如何都要使用C方式来执行它,即:

  1. 指向结构数组的指针。
  2. 记录数量参数。
  3. 您的API有义务复制作为参数传递的内容,以便调用者可以在调用后立即释放它所需的内容。 C代码拥有它在参数中传递的内容。
  4. 通过调用现有API,可以轻松实现向C公开的API。

    我希望有所帮助。