8
votes

Delphi "Array of Cons" à "Varargs"

aide s'il vous plaît! J'ai besoin de cette conversion pour écrire un wrapper pour certains en-têtes C pour Delphi.

Par exemple: xxx

Comment puis-je convertir "gamme de const" à "varargs"?

EDIT : Fonction Pushstring est réellement à l'intérieur de l'enregistrement (j'ai donné un exemple simplifié), et je n'ai pas d'accès direct à la pousseuse. L'appel direct est exclu.

EDIT 2 : J'écris les unités de Lua Bibliothèque pour Delphi et le cas est assez important pour moi.

Spécifier et fournir Tous les détails de la question - J'ai cette fonction en C: xxx

dans Delphi, j'ai quelque chose comme ceci:

lualibrary.pas xxx

dtxlua.pas xxx

et dans d'autres unités comme Lua .PAS J'utilise uniquement le tluastate de dtxlua.pas (car Lualibrary est volumineux, DTXLUA est mon wrapper), pour de nombreuses choses utiles et cool ...


4 commentaires

La fonction pushfstring que vous essayez d'appeler est une fonction externe. Il est impossible de "ne pas avoir d'accès direct", car vous pouvez faire une déclaration pour le savoir où vous le souhaitez. Bien que j'apprécie votre désir d'appeler une fonction Varargs avec un nombre inconnu de paramètres, vous n'avez pas réellement besoin de cela dans votre cas car vous peut appelez directement pushfstring de Où que vous aurait appelé pousststring .


@Rob - Je soupçonne qu'il a un pointeur de la fonction.


Quel est le prototype C pour pushfstring ?


Vous devrez coder certains assemblage pour appuyer chaque Arg de la matrice de Constray dans la pile (du dernier à d'abord), puis appuyez sur FMT, appelle Poussefstring et réinitialisez la pile


4 Réponses :


2
votes

Un "tableau de const" est en fait un tableau de TVARRRC, qui est un type de variante spécial. Il n'est pas compatible avec Varargs, et vous devriez vraiment pouvoir appeler la fonction Varargs directement sans enveloppe autour de celle-ci.


1 commentaires

Fonction Pushstring est en fait à l'intérieur de l'enregistrement (j'ai donné un exemple simplifié) et je n'ai pas d'accès direct à la pousseuse. L'appel direct est exclu.



4
votes

L'emballage que vous essayez d'écrire est possible dans Free Pascal, car Pascal gratuit prend en charge 2 déclarations équivalentes pour les fonctions externes de Varargs:

http://www.freecuce.org/docs-html/ref/refsu68.html

donc au lieu de xxx

Vous devez écrire xxx

mise à jour: j'ai essayé le même tour à Delphi, mais cela ne fonctionne pas: xxx


0 commentaires

14
votes

Je suppose que le prototype de pushfstring code> est quelque peu comme suit: xxx pré>

si ce n'est pas le cas, et est à la place: P> xxx pré>

... Puis je devrais vous couvrir également. p>

in c, si vous devez transmettre un appel d'une fonction variable à une autre, vous devez utiliser VA_LIST code>, VA_Start code> et VA_END code> et appelez la version V code> de la fonction. Donc, si vous impliez la mise en œuvre printf code> vous-même, vous pouvez utiliser vsprintf code> pour formater la chaîne - vous ne pouvez pas appeler sprintf code> directement et transmettre le long de la Liste d'arguments variadiques. Vous devez utiliser va_list code> et amis. P>

C'est assez gênant de gérer VA_LIST code> de Delphi, et techniquement, cela ne devrait pas être fait - la mise en œuvre de va_list code> est spécifique au temps d'exécution du fournisseur C compilateur C. p>

Cependant, nous pouvons essayer. Supposons que nous ayons une petite classe - bien que je l'ai fait un record pour la facilité d'utilisation: p> xxx pré>

Utilisation de cet enregistrement, nous pouvons construire manuellement le cadre d'appel attendu pour divers appels C. La convention d'appel de C sur X86 est de passer des arguments de droite à gauche sur la pile, avec le nettoyage de l'appelant. Voici le squelette d'une routine d'appel générique C: p> xxx pré>

prise printf code> comme exemple: p>

function CallManually2(Code: Pointer; Fmt: AnsiString;
    const Args: array of const): Pointer;
var
  i: Integer;
  caller: TVarArgCaller;
begin
  for i := High(Args) downto Low(Args) do
  begin
    case Args[i].VType of
      vtInteger: caller.PushArg(Args[i].VInteger);
      vtPChar: caller.PushArg(Args[i].VPChar);
      vtExtended: caller.PushArg(Args[i].VExtended^);
      vtAnsiString: caller.PushArg(PAnsiChar(Args[i].VAnsiString));
      vtWideString: caller.PushArg(PWideChar(Args[i].VWideString));
      vtUnicodeString: caller.PushArg(PWideChar(Args[i].VUnicodeString));
    else
      raise Exception.Create('Unknown type'); // etc.
    end;
  end;
  caller.PushArgList;
  caller.PushArg(PAnsiChar(Fmt));
  Result := caller.Invoke(Code);
end;

function vprintf(fmt: PAnsiChar; va_list: Pointer): Integer; cdecl;
    external 'msvcrt.dll' name 'vprintf';

begin
  // the hard way, va_list
  CallManually2(@vprintf, 'test of printf %s %d %.4g'#10, 
      [PAnsiChar('hello'), 42, C]);
end.


3 commentaires

Il est possible de améliorer le code en appuyant sur la "pile de réseau" à la réelle pile avant l'instruction d'appel?


Barry - respect. C'est ce dont j'avais besoin.


@ArthURPRS - Comme je le mentionne, je construisais des choses dans le tableau, puis placez-la en tant que pile pour rendre les choses faciles, compréhensibles et flexibles. Il est beaucoup plus difficile de résumer les détails de la gestion de la pile lorsque vous utilisez la vraie pile. La copie dans la pile pourrait également être effectuée. Je laisse ça comme un exercice pour le lecteur ... :)



1
votes

Barry Kelly m'a inspiré à la recherche d'une solution sans remplacer la pile ... Voici la solution (peut également utiliser l'invoke à partir de l'unité RTTI, à la place RealCall_CDecl).

// This function is copied from PascalScript
function RealCall_CDecl(p: Pointer;
  StackData: Pointer;
  StackDataLen: Longint; // stack length are in 4 bytes. (so 1 = 4 bytes)
  ResultLength: Longint; ResEDX: Pointer): Longint; Stdcall; 
  // make sure all things are on stack
var
  r: Longint;
begin
  asm
    mov ecx, stackdatalen
    jecxz @@2
    mov eax, stackdata
    @@1:
    mov edx, [eax]
    push edx
    sub eax, 4
    dec ecx
    or ecx, ecx
    jnz @@1
    @@2:
    call p
    mov ecx, resultlength
    cmp ecx, 0
    je @@5
    cmp ecx, 1
    je @@3
    cmp ecx, 2
    je @@4
    mov r, eax
    jmp @@5
    @@3:
    xor ecx, ecx
    mov cl, al
    mov r, ecx
    jmp @@5
    @@4:
    xor ecx, ecx
    mov cx, ax
    mov r, ecx
    @@5:
    mov ecx, stackdatalen
    jecxz @@7
    @@6:
    pop eax
    dec ecx
    or ecx, ecx
    jnz @@6
    mov ecx, resedx
    jecxz @@7
    mov [ecx], edx
    @@7:
  end;
  Result := r;
end;

// personally created function :)
function CallManually3(Code: Pointer; const Args: array of const): Pointer;
var
  i: Integer;
  tmp: AnsiString;
  data: AnsiString;
begin
  for i := Low(Args) to High(Args) do
  begin
    case Args[i].VType of
      vtInteger, vtPChar, vtAnsiString, vtWideString, vtUnicodeString: begin
          tmp := #0#0#0#0;
          Pointer((@tmp[1])^) := TVarRec(Args[i]).VPointer;
      end;
      vtExtended: begin
          tmp := #0#0#0#0#0#0#0#0;
          Double((@tmp[1])^) := TVarRec(Args[i]).VExtended^;
      end;
      // fill as needed
    else
      raise Exception.Create('Unknown type');
    end;

    data := data + tmp;
  end;

  Result := pointer(RealCall_CDecl(Code, @data[Length(data) - 3], 
    Length(data) div 4, 4, nil));
end;

function printf(fmt: PAnsiChar): Integer; cdecl; varargs;
    external 'msvcrt.dll' name 'printf';

begin
  CallManually3(@printf, 
    [AnsiString('test of printf %s %d %.4g'#10), 
      PAnsiChar('hello'), 42, 4.123]);
end.


0 commentaires