¿Cómo traducir "matriz de" parámetros a C ++?

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

Obligatory reminder: C and C++ are totally different things. -

@Charles Salvia, I mean to say c++. I updated my post. Thanks. -

3 Respuestas

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

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

  TIntegerArray: array of Integer;

  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 OPENARRAY() y 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 )) );

Respondido el 09 de enero de 11 a las 12:01

Lebeau, thank you. I understand now. Hmmm yea I forgot about ARRAYOFCONST. I think I had to use it once. Let me ask is this also in other C++ compilers such as VC too? Or would I have to declare it in the header of my own binding? I'm trying to make the bindings work with the most commonly used C++ compilers. - PyroBASIC

Then your engine should not be using Pascal-specific features in its public interfaces, like open arrays. Use plain vanilla array pointers instead. If you need to support multiple data types in your array, then use the standard Win32 VARIANT data type, not OleVariant or TVarRec (which is used by the compiler internally for array of const values), as they are specific to Delphi and C++Builder and will not work in other C++ compilers. - Remy Lebeau

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)
    arr := VarArrayCreate(...);
      // ... init arr from array of OLEVariant parameter

      // call actual API fn:

      // Dispose of variant array

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.

Respondido el 08 de enero de 11 a las 23:01

wonderful, thanks. Our engine is coded in Delphi so the C side will be calling into a Delphi routine which is expected an OLEVariant param. Hmm.. I didn't consider encoding the types in a string, nice. - PyroBASIC

The engine allows compiling script to machine code and you can then call routines/methods on the script side. I can specify the calling convention on the script side so I was thinking of just calling the routine directly. It works in Delphi but I've not tested in C. Wanted to get an idea if it will work before installing VC. I can call DLM_GetAddress to return a pointer to the routine. This is assigned to a procedural variable with the same signature as the script routine. I can then directly make the call. Off hand I don't see any problems doing the same in C?. Any thoughts about this? - PyroBASIC

Like Deltics said, do not use Delphi-specific data types, like OleVariant. They will not work in C, even though you can call the routines. OleVariant is a compiler-managed data type, so you would have to replicate all of Delphi's compiler+RTL backend logic in your C code manually in order to use it. It is not worth it. When dealing with cross-language code, just use plain vanilla POD data types only. - Remy Lebeau

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] y 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.

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 y SysUtils.pas. What follows is from memory (sorry in advance for being of such little help).

A Delphi dynarray is a record:

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

@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 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.

Respondido el 20 de junio de 20 a las 12:06

ahh.. yea numParams ok. Would I need to manually inc/dec the ref count on the c++ side? - PyroBASIC

You need to make sure that the refcount doesn't go to zero on the Delphi side while the C side still holds a reference. As @Deltics suggests, if you're using OLE, then you'll probably be better off resourcing to OLE arrays. If you're just using Variants for their convenience, and you don't need OLE, then stay away from OLE. - Apalala

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.