PyroBASIC
PyroBASIC

Reputation: 119

How to translate "array of" param to C++?

I'm working on c++ language bindings for our game engine (made with Delphi XE). How would I translate the array of OleVariant and array of const params to work properly in C++ side?

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;

Thanks.

Upvotes: 3

Views: 2318

Answers (3)

Apalala
Apalala

Reputation: 9244

If I remember correctly, dynamic arrays in Delphi store the size of the array in the first few bytes. You would have to declare your interface function as taking a pointer, plus a size:

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

Callers would have to pass the address of the first actual element and the number of elements, @A[0] and Length(A) in Delphi.

Beware: There are memmory management/garbage collection issues wherever OleVariant and other OLE types are involved. You have to make sure that reference counts are incremented and decremented appropiately.

Delphi Dynarrays

This is correctly documented somewhere in the Delphi help, and can probably be learned from looking at the source code of System.pas and SysUtils.pas. What follows is from memory (sorry in advance for being of such little help).

A Delphi dynarray is a record:

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

@A[0] is equivalent is the same as @content. To get to the address that includes the refcount you'd have to do some casting.

Having C call Delphi

You could have the clients written in C manipulate the structures for Delphi dynarrays, But why impose Delphi implementation semantics on them? If C is calling into your code, then by all means use the C way of doing it, which is:

  1. A pointer to an array of structures.
  2. A number-of-records parameter.
  3. The obligation of your API to copy what was passed as parameters so the caller can free what it likes right after the call. The C code owns what it passes in parameters.

The API exposed to C can be easily implemented with calls to the existing API.

I hope that helps.

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 598011

Delphi has two completely separate array semantics that both use the same array of code syntax for different purposes.

When array of is used to declare a data type or variable, a Dynamic Array is being used, eg:

type
  TIntegerArray: array of Integer;

var
  Array1: TIntegerArray;
  Array2: array of Integer;

In C++, these correspond to the DynamicArray<T> template class, eg:

typedef DynamicArray<int> TIntegerArray;

TIntegerArray Array1;
DynamicArray<int> Array2;

On the other hand, when array of is used in a function parameter directly (ie, without using a typedef), then an Open Array is being used instead, ie:

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

Values passed to an Open Array parameter are passed by the compiler using two separate parameters - a pointer to the actual array, and the index of the last element in the array. Delphi hides this fact so the coder only sees one parameter, and provides a simple syntax for specifying the parameter values:

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

In C++, however, the two parameters used for the array are explicitally declared, and values are typically specified using the OPENARRAY() and ARRAYOFCONST() macros, eg:

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

Upvotes: 3

Deltics
Deltics

Reputation: 23056

When creating code with interfaces to other languages it is wise to avoid Delphi specific types and conventions. Where you know your code will be interfacing with Delphi code, you may choose to provide Delphi-friendly interfaces, and "wrappers" to map Delphi-friendly types and mechanisms onto more portable ones for those other languages.

So....

Array of OLEVariant

Since you are passing an array of variants, clearly your C++ code is variant aware/capable, in which case I would pass these values in a variant array themselves (passed as a Variant itself), preserving the dynamic nature of the array, but eliminating any concerns over Delphi RTL specific "magic" types (dynamic arrays).

So you may have a Fn(array of OLEVariant) for your Delphi code, which internally re-packages the array into a Variant Array of Variant before passing the call on to the actual API code (psuedo-code):

  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;

Array of Const

The values end up being passed in an array of TVarRec (not directly connected to variants, tho with a similar intention). Again this is a "magic" Delphi type - you will need to research the TVarRec type and map it to some equivalent C++ type.

In this case I would determine exactly what you need to pass in the params list and adopt a mechanism that is more portable between the two languages. Perhaps a simple string containing a name/value pair delimited string of parameter names and values?

In this case, providing a Delphi friendly wrapper around the API call would involve a slightly different approach from that which you are currently using (array of const), given that array of const does not provide for named entries in the array. Or you might choose simply to provide a delimited set of type/values, encoded in a string, in which case you could continue to use array of const for the Delphi side, with a wrapper function that processes the TVarRec array to format a string approriately for the API function.

Upvotes: 2

Related Questions