I'm working on c++ language bindings for our game engine (made with Delphi XE). How would I translate the array of OleVariant y matriz de 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;
preguntado el 08 de enero de 11 a las 17:01
Delphi has two completely separate array semantics that both use the same
array of code syntax for different purposes.
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;
Por otro lado, cuando
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
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 )) );
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.
Así que ....
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;
Matriz de 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.
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,
Length(A) en Delphi.
Ten cuidado: 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
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 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:
- A pointer to an array of structures.
- A number-of-records parameter.
- 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 Posee what it passes in parameters.
The API exposed to C can be easily implemented with calls to the existing API.
Espero que eso ayude.