2

我需要将我的部分 Win32 Delphi 应用程序提供给另一家公司的 Linux gcc 程序。

吞吐量和部署要求使任何类型的远程服务都不合适,因此我正在考虑使用 FreePascal 构建 gcc 应用程序可以调用的 .SO(Linux 等效的 DLL)。

自从我使用 C/C++ 并且从未在 Linux 上使用过很长时间了,所以我有点不确定如何最好地构建 DLL/SO 接口以与 gcc 调用者兼容。

这是我的数据结构的表示

TFoo = record
  x, y : double;
  a : smallint;
  b : string;
end;

TBar = record
  a : double;
  b : longint;
  c : string;
end;

TFooBar = record
  foo : array of TFoo;
  bar : array of TBar;
end;

procedure Process(const inFooBar : TFooBar);

要通过 FreePascal 使此 Process 方法在外部可用。所以我需要如何修改这些声明?我在想一些事情

TFoo = record
  x, y : double;
  a : smallint;
  b : PChar;
end;

TBar = record
  a : double;
  b : longint;
  c : PChar;
end;

TFooBar = record
  foo : ^TFoo;
  foo_count : longint;
  bar : ^TBar;
  bar_count : longint;
end;

procedure Process(const inFooBar : TFooBar);

我在正确的轨道上吗?我不必完全正确,其他公司的程序员很可能会纠正我的错误。我只是不想让他们看到我发给他们的东西时笑得太厉害。

4

3 回答 3

1

To ensure that the record packing/alignment/padding is as GCC expects, add {$packrecords c} to your Pascal source. Note that this directive is specific to the Free Pascal Compiler, Delphi does not support it.

于 2011-05-25T08:31:45.993 回答
1

使用PAnsiChar而不是PChar,而integer不是longint/smallint(因为您使用对齐的记录,所以使用 smallint 字段没有任何好处)。

定义一些指针类型,例如PFooand PBar,这将比^TFooeg写得更好

如果您需要访问一些数组,您可以定义TFooArrayand TBarArray,如下面的代码所示。

不要忘记任何函数/过程的cdeclorstdcall调用约定。

type
TFoo = record
  x, y : double;
  a : integer;
  b : PAnsiChar;
end;

TBar = record
  a : double;
  b : integer;
  c : PAnsiChar;
end;

TFooArray = array[0..maxInt div sizeof(TFoo)-1] of TFoo;
TBarArray = array[0..maxInt div sizeof(TBar)-1] of TBar;

PBar = ^TBar;
PFoo = ^TFoo;
PFooArray = ^TFooArray;
PBarArray = ^TBarArray;

TFooBar = record
  foo : PFooArray;
  foo_count : integer;
  bar : PBarArray;
  bar_count : integer;
end;

procedure Process(const inFooBar : TFooBar); cdecl; external 'ProcessGCCLibrary.so';

您的进程将是只读的吗?

如果 C 部分需要添加一些项目,则必须为外部库提供一些内存重新分配方法,至少映射reallocmem().

请注意,Delphi 动态数组可以很容易地映射到 C 兼容结构中,如下所示:

type
  TFooDynArray: array of TFoo;
  TBarDynArray: array of TBar;

procedure CallProcess(const aFoo: TFooDynArray; const aBar: TBarDynArray);
var tmp: TFooBar;
begin
  tmp.foo := pointer(aFoo);
  tmp.foo_count := length(aFoo);
  tmp.bar := pointer(aBar);
  tmp.bar_count := length(aBar);
  Process(tmp);
end;

这可以使您的 Delphi 代码更具可读性。如果您想使用类似方法的方法对这样的动态记录数组进行高级访问,请查看我们的TDynArray包装器TList

由于AnsiString类型映射 a PAnsiChar,从二进制的角度来看,您甚至可以这样定义您的记录:

type
TFoo = record
  x, y : double;
  a : integer;
  b : AnsiString;
end;

TBar = record
  a : double;
  b : integer;
  c : AnsiString;
end;

当映射到 gcc 应用程序时,它将被读取为常规*char.

使用AnsiString这里你不需要处理你的 Delphi 代码的内存分配。使用关联的动态数组,它可以使您的 Delphi 代码更易于维护。请注意,我们的TDynArray包装器将按预期处理嵌套AnsiString在记录中,即使是最高级别的方法(例如二进制序列化或散列)。

于 2011-05-24T06:43:56.110 回答
0

看起来还不错。想法:

  • 您的数组(声明为指针和计数)将完全手动管理 - 这对您来说很好吗?

  • 你应该改变:

    procedure Process(const inFooBar : TFooBar);
    

    包括与 C 兼容的调用约定。我不确定 FreePascal 和 GCC 都支持什么,但类似的东西cdecl应该可以正常工作。

  • 您还应该(为了安全起见)指定结构对齐/打包。

  • PChar应该明确地窄或宽(现在宽是正常的。)

于 2011-05-24T04:54:49.147 回答