Delphi XE使用MCR创建结构化数组,以便在Matlab Compiled dll中使用

时间:2014-03-27 15:25:22

标签: delphi delphi-xe matlab-compiler

我在Windows 7上使用Delphi XE和Matlab 2012B编译器。

我正在尝试编写几个包装函数,因此可以更轻松地从Delphi XE调用使用Matlab 2012b编译器创建的DLL文件。我发现在使用MCR时我应该使用_proxy函数,这确实允许我成功调用几个函数。我也可以通过将它们作为PAnsiChar传递给Matlab而没有问题。

我目前正在尝试使用一些字段名创建StructArray。 由于我已经成功创建了数值数组和矩阵,我很确定前2个参数是正常的。我希望最后一个导致错误,但我不知道如何解决这个问题。查看Matlab帮助和示例文件,我正在做应该做的事情。显然我错了......

我知道使用Matlab r13我们必须将字段名称作为array[0..n] of pAnsiChar而不是array of pAnsiChar传递。我在这里试过这个也无济于事。

有人可以告诉我,我是否确实已将正确的函数映射到mxCreateStructArray(_730_proxy),并且我是否按预期传递参数?

type
  mxArray = pointer;

// mxArray *mxCreateStructArray(mwSize ndim, const mwSize *dims, int nfields, const char **fieldnames);
function MCRdll_CreateStructArray(aDimCount: integer; aDims: pointer; aFieldCount: integer; aFields: PPAnsiChar): mxArray; cdecl; external 'mclmcrrt8_0.dll' name 'mxCreateStructArray_730_proxy';

function MCR_CreateStructArray(aFieldNames: TArray<string>): mxArray;
var
  i: integer;
  lstDims: array of integer;
  lstNames: array of pAnsiChar;
begin
  SetLength(lstNames, Length(aFieldNames));
  for i := 0 to Length(aFieldNames) - 1 do
    lstNames[i] := ToPAnsiChar(aFieldNames[i]); //Creates a new PAnsiChar with the content of aFieldNames[i] 

  SetLength(lstDims, 2);
  lstDims[0] := 1;
  lstDims[1] := Length(aFieldNames);

  //This call raises an "External Exception" from Matlab.
  Result := MCRdll_CreateStructArray(Length(lstDims), @lstDims, Length(lstNames), @lstNames);
end;

1 个答案:

答案 0 :(得分:1)

MATLAB C API函数是:

mxArray *mxCreateStructArray(mwSize ndim, const mwSize *dims, 
    int nfields, const char **fieldnames);

据我了解,mwSize默认与int相同。这转化为Delphi中的Integerconst char**参数是const C字符串数组的地址。把它翻译成德尔福,你有:

function MCRdll_CreateStructArray(ndim: Integer; dims: PInteger; 
    nFields: Integer; fieldnames: PPAnsiChar): mxArray; cdecl; 
    external 'mclmcrrt8_0.dll' name 'mxCreateStructArray_730_proxy';

现在,如何获取参数。好吧,假设你想要一个向量,dims是一个长度为2的数组,ndim就是那个长度。我将其声明为静态数组:

var
  dims: array [0..1] of Integer;

对于字段名称,这些是可变长度。所以你需要一个PAnsiChar的动态数组。那就是:

var
  fieldnames: array of PAnsiChar;

您还需要将struct数组的向量长度传递给函数。这使你的功能是这样的:

function MCR_CreateStructArray(len: Integer; 
  const aFieldNames: array of AnsiString): mxArray;
var
  i: integer;
  dims: array [0..1] of Integer;
  fieldnames: array of PAnsiChar;
begin
  if Length(aFieldNames)=0 then
  begin
    Result := nil;
    exit;
  end;

  dims[0] := 1;
  dims[1] := len;

  SetLength(fieldnames, Length(aFieldNames));
  for i := 0 to high(fieldnames) do
    fieldnames[i] := PAnsiChar(aFieldNames[i]);

  Result := MCRdll_CreateStructArray(Length(dims), @lstDims[0], 
    Length(fieldnames), @fieldnames[0]);
end;

最终参数的替代方法是传递PPAnsiChar(fieldnames)。这是有效的,因为动态数组变量是第一个元素的地址。

那么,您的版本出了什么问题?您犯的最大错误是对传递给MCRdll_CreateStructArray的两个数组使用无类型指针。这意味着编译器无法检查您的间接是否正确。你没有。

首先,在代码中将@lstDims传递给第二个参数。现在lstDims是代码中的动态数组。其实现有lstDims是指向第一个元素的指针。因此,非正式地,lstDims具有类型^Integer。因此@lstDims的类型为^^Integer。这是间接的一个层次。你在最后一个参数中犯了同样的错误。

最后一点。我已经更改了函数的签名以接收AnsiString数组。这是我编写代码的简单方法,因为我不需要担心UTF-16到ANSI的转换,并且可以使用简单的PAnsiChar强制转换。你可能会受益于这个帮手:

function ToAnsiStringArray(const arr: array of string): TArray<AnsiString>;
var
  i: Integer;
begin
  SetLength(Result, Length(arr));
  for i := 0 to high(Result) do
    Result[i] := AnsiString(arr[i]);
end;

我没有编译任何这些,所以可能有一些不精确。我相信你不会因此而被推迟。