Написание Delphi / FreePascal DLL для вызова из gcc-приложения - PullRequest
2 голосов
/ 24 мая 2011

Мне нужно сделать части моего приложения Win32 Delphi доступными для программы Linux gcc другой компании.

Требования к пропускной способности и развертыванию делают любой вид удаленного сервиса непригодным, поэтому я собираюсь использовать FreePascal для сборки.SO (Linux-эквивалент DLL), который может вызывать приложение gcc.

Прошло много времени с тех пор, как я использовал 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);

Чтобы сделать этот метод Process внешне доступным через FreePascal. SO Как мне нужно изменитьэти декларации?Я думаю что-то вроде

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);

Я на правильном пути?Я не должен понимать это правильно, программисты другой компании, скорее всего, исправят мои ошибки.Я просто не хочу, чтобы они смеялись слишком сильно, когда увидят, что я им послал.

Ответы [ 3 ]

1 голос
/ 24 мая 2011

Используйте PAnsiChar вместо PChar и integer вместо longint/smallint (поскольку вы используете выровненные записи, использование поля smallint не имеет смысла).

Определите некоторые типы указателей, такие как PFoo и PBar, которые будут написаны лучше, чем ^TFoo, например,

Если вам нужен доступ к некоторым массивам, вы можете определить TFooArray и TBarArray, как показано в коде ниже.

Не забывайте cdecl или stdcall соглашение о вызовах для любой функции / процедуры.

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 отображает 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 в записях, как и ожидалось, даже для методов самого высокого уровня (например, двоичная сериализация или хеширование).

1 голос
/ 25 мая 2011

Чтобы убедиться, что упаковка / выравнивание / заполнение записей соответствует ожидаемым GCC, добавьте {$ packrecords c} к своему источнику Pascal.Обратите внимание, что эта директива относится только к компилятору Free Pascal, Delphi ее не поддерживает.

0 голосов
/ 24 мая 2011

выглядит довольно хорошо. Мысли:

  • Ваши массивы (объявленные как указатель и счетчик) будут полностью управляться вручную - это нормально для вас?

  • Вы должны изменить:

    procedure Process(const inFooBar : TFooBar);
    

    для включения C-совместимого соглашения о вызовах. Я не уверен, что FreePascal и GCC оба поддерживают, но что-то вроде cdecl должно работать нормально.

  • Вы также должны (в целях безопасности) указать выравнивание конструкции / уплотнение.

  • PChar должен быть явно узким или широким (в наши дни это нормально).

...