Reputation: 119
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
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.
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.
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:
The API exposed to C can be easily implemented with calls to the existing API.
I hope that helps.
Upvotes: 1
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
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