2010-02-21 14 views
8

कृपया मदद करें! मुझे डेल्फी के लिए कुछ सी शीर्षकों के लिए रैपर लिखने के लिए इस रूपांतरण की आवश्यकता है।डेल्फी "वर्ंजर की सरणी" को "varargs"

एक उदाहरण के रूप:

function pushfstring(fmt: PAnsiChar): PAnsiChar; cdecl; varargs; external; 

... 

function PushString(fmt: AnsiString; const args: array of const): AnsiString; 
begin 
    Result := AnsiString(pushfstring(PAnsiString(fmt), args)); // it's incorrect :/ 
end; 

मैं कैसे "स्थिरांक की सरणी" से "varargs" में बदल सकते हैं?

संपादित करें: फ़ंक्शन पुशस्ट्रिंग वास्तव में रिकॉर्ड के अंदर है (मैंने एक सरलीकृत उदाहरण दिया है), और मेरे पास pushfstring तक सीधे पहुंच नहीं है। डायरेक्ट कॉल को बाहर रखा गया है।

संपादित 2: मैं डेल्फी के लिए LUA लाइब्रेरी के लिए इकाइयां लिखता हूं और मामला मेरे लिए काफी महत्वपूर्ण है।

निर्दिष्ट करना और इस मामले के सभी विवरण उपलब्ध कराने के

- मैं सी में इस समारोह है:

LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);

डेल्फी में मैं कुछ इस तरह है:

LuaLibrary.pas

{...} 
interface 
{...} 
function lua_pushfstring(L: lua_State; fmt: PAnsiChar): PAnsiChar; cdecl; varargs; 
implementation 
{...} 
function lua_pushfstring; external 'lua.dll'; // or from OMF *.obj file by $L

dtxLua.pas

uses LuaLibrary; 
{...} 
type 
    TLuaState = packed record 
    private 
    FLuaState: lua_State; 
    public 
    class operator Implicit(A: TLuaState): lua_State; inline; 
    class operator Implicit(A: lua_State): TLuaState; inline; 
    {...} 
    // btw. PushFString can't be inline function 
    function PushFString(fmt: PAnsiChar; const args: array of const): PAnsiChar; 
    //... and a lot of 'wrapper functions' for functions like a lua_pushfstring, 
    // where L: lua_State; is the first parameter 
    end; 
implementation 
{...} 
function TLuaState.PushFString(fmt: PAnsiChar; const args: array of const) 
    : PAnsiChar; 
begin 
    Result := lua_pushfstring(FLuaState, fmt, args); // it's incorrect :/ 
end;

और Lua.pas जैसे अन्य इकाइयों मैं dtxLua.pas से केवल TLuaState का उपयोग में, कई उपयोगी और शांत बातों के लिए ...

की
+0

'pushfstring' फ़ंक्शन जिसे आप कॉल करने का प्रयास कर रहे हैं वह बाहरी कार्य है। इसके लिए "प्रत्यक्ष पहुंच नहीं है" असंभव है क्योंकि आप कहीं भी इसके लिए घोषणा कर सकते हैं। हालांकि मैं अज्ञात संख्याओं के पैरामीटर के साथ एक varargs फ़ंक्शन को कॉल करने की आपकी इच्छा की सराहना करता हूं, लेकिन आपको वास्तव में अपने मामले में इसकी आवश्यकता नहीं है क्योंकि आप जहां भी 'पुशस्ट्रिंग' कहलाते हैं, वहां से सीधे 'pushfstring' को कॉल कर सकते हैं। –

+0

@Rob - मुझे संदेह है कि उसके पास एक फ़ंक्शन पॉइंटर है। –

+0

'pushfstring' के लिए सी प्रोटोटाइप क्या है? –

उत्तर

12

मेरा अनुमान है कि कि pushfstring के लिए प्रोटोटाइप इस तरह कुछ हद तक है:

void pushfstring(const char *fmt, va_list args); 

यह नहीं हैं ' टी, और इसके बजाय है:

void pushfstring(const char *fmt, ...); 

... तो मुझे आपको भी कवर करना चाहिए था।

सी में, यदि आप एक variadic समारोह से दूसरे में एक फोन पर पारित करने के लिए है, तो आप va_list, va_start और va_end, का उपयोग करें और समारोह के v संस्करण फोन करना चाहिए। इसलिए, यदि आप printf स्वयं को कार्यान्वित कर रहे थे, तो आप स्ट्रिंग को प्रारूपित करने के लिए vsprintf का उपयोग कर सकते हैं - आप सीधे sprintf पर कॉल नहीं कर सकते हैं और विविध तर्क सूची के साथ पास कर सकते हैं। आपको va_list और दोस्तों का उपयोग करने की आवश्यकता है।

डेल्फी से सी के va_list को संभालने के लिए यह बहुत अजीब है, और तकनीकी रूप से यह नहीं किया जाना चाहिए - va_list का कार्यान्वयन सी कंपाइलर विक्रेता के रनटाइम के लिए विशिष्ट है।

हालांकि, हम कोशिश कर सकते हैं। मान लीजिए कि हमें एक छोटे से वर्ग है - हालांकि मैं यह उपयोग में आसानी के लिए एक रिकॉर्ड बनाया:

type 
    TVarArgCaller = record 
    private 
    FStack: array of Byte; 
    FTop: PByte; 
    procedure LazyInit; 
    procedure PushData(Loc: Pointer; Size: Integer); 
    public 
    procedure PushArg(Value: Pointer); overload; 
    procedure PushArg(Value: Integer); overload; 
    procedure PushArg(Value: Double); overload; 
    procedure PushArgList; 
    function Invoke(CodeAddress: Pointer): Pointer; 
    end; 

procedure TVarArgCaller.LazyInit; 
begin 
    if FStack = nil then 
    begin 
    // Warning: assuming that the target of our call doesn't 
    // use more than 8K stack 
    SetLength(FStack, 8192); 
    FTop := @FStack[Length(FStack)]; 
    end; 
end; 

procedure TVarArgCaller.PushData(Loc: Pointer; Size: Integer); 
    function AlignUp(Value: Integer): Integer; 
    begin 
    Result := (Value + 3) and not 3; 
    end; 
begin 
    LazyInit; 
    // actually you want more headroom than this 
    Assert(FTop - Size >= PByte(@FStack[0])); 
    Dec(FTop, AlignUp(Size)); 
    FillChar(FTop^, AlignUp(Size), 0); 
    Move(Loc^, FTop^, Size); 
end; 

procedure TVarArgCaller.PushArg(Value: Pointer); 
begin 
    PushData(@Value, SizeOf(Value)); 
end; 

procedure TVarArgCaller.PushArg(Value: Integer); 
begin 
    PushData(@Value, SizeOf(Value)); 
end; 

procedure TVarArgCaller.PushArg(Value: Double); 
begin 
    PushData(@Value, SizeOf(Value)); 
end; 

procedure TVarArgCaller.PushArgList; 
var 
    currTop: PByte; 
begin 
    currTop := FTop; 
    PushArg(currTop); 
end; 

function TVarArgCaller.Invoke(CodeAddress: Pointer): Pointer; 
asm 
    PUSH EBP 
    MOV EBP,ESP 

    // Going to do something unpleasant now - swap stack out 
    MOV ESP, EAX.TVarArgCaller.FTop 
    CALL CodeAddress 
    // return value is in EAX 
    MOV ESP,EBP 

    POP EBP 
end; 

इस रिकॉर्ड का उपयोग करना, हम स्वयं कॉल फ्रेम विभिन्न सी कॉल के लिए उम्मीद का निर्माण कर सकते हैं। एक्स 86 पर सी का कॉलिंग कन्वेंशन कॉलर की सफाई के साथ, स्टैक पर दाईं ओर बाएं से दाएं पास करना है। है थोड़ा और अधिक शामिल,

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

const 
    // necessary as 4.123 is Extended, and %g expects Double 
    C: Double = 4.123; 
begin 
    // the old-fashioned way 
    printf('test of printf %s %d %.4g'#10, PAnsiChar('hello'), 42, C); 
    // the hard way 
    CallManually(@printf, [AnsiString('test of printf %s %d %.4g'#10), 
         PAnsiChar('hello'), 42, C]); 
end. 

va_list संस्करण कॉलिंग va_list तर्क के स्थान के रूप में ध्यान से जहां रखा जाना चाहिए:

function CallManually(Code: Pointer; 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)); 
     // fill as needed 
    else 
     raise Exception.Create('Unknown type'); 
    end; 
    end; 
    Result := caller.Invoke(Code); 
end; 

एक उदाहरण के रूप printf ले रहा है: यहाँ बुला एक सामान्य सी के कंकाल दिनचर्या है यह उम्मीद है:

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. 

नोट्स:

  • उपरोक्त विंडोज़ पर x86 की अपेक्षा करता है। माइक्रोसॉफ्ट सी, बीसीसी 32 (एम्बरकाडेरो सी ++) और जीसीसी सभी प्रयोगों के अनुसार, उसी तरह से va_list पास (स्टैक पर पहले चरम तर्क के लिए सूचक) पास करते हैं, इसलिए यह आपके लिए काम करना चाहिए; लेकिन जैसे ही विंडोज धारणा पर x86 टूटा हुआ है, उम्मीद है कि यह संभवतया भी तोड़ने की उम्मीद है।

  • ढेर को इसके निर्माण के साथ आसानी से बदलने के लिए बदल दिया गया है। इसे अधिक काम से बचा जा सकता है, लेकिन va_list गुजरना भी मुश्किल हो जाता है, क्योंकि इसे तर्कों को इंगित करने की आवश्यकता होती है जैसे कि उन्हें ढेर पर पारित किया गया था। नतीजतन, कोड को इस बारे में एक धारणा बनाने की आवश्यकता है कि कितनी बार नियमित रूप से उपयोग किया जाता है; यह उदाहरण 8K मानता है, लेकिन यह बहुत छोटा हो सकता है। यदि आवश्यक हो तो बढ़ाएं।

+0

कॉल निर्देश से पहले "सरणी स्टैक" को असली स्टैक पर दबाकर कोड को बेहतर बनाना संभव है? – arthurprs

+0

बैरी - सम्मान। मुझे यही चाहिए। – HNB

+0

@arthurprs - जैसा कि मैंने उल्लेख किया है, मैं सरणी में चीजों का निर्माण करता हूं और फिर चीजों को आसान, समझने योग्य और लचीला बनाने के लिए इसे ढेर के रूप में रखता हूं। जब आप असली स्टैक का उपयोग कर रहे हों तो स्टैक प्रबंधन के विवरण को दूर करना बहुत मुश्किल है। ढेर में कॉपी करना भी किया जा सकता है। मैं इसे पाठक के लिए एक अभ्यास के रूप में छोड़ देता हूं ... :) –

2

एक "सरणी (क्योंकि LuaLibrary भारी है, dtxLua मेरी आवरण है) कॉन्स "वास्तव में टीवीर्रैक की एक सरणी है, जो एक विशेष प्रकार का प्रकार है। यह varargs के साथ संगत नहीं है, और आप वास्तव में इसके चारों ओर एक रैपर के बिना varargs समारोह को कॉल करने में सक्षम होना चाहिए।

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

तो बजाय

function pushfstring(fmt: PAnsiChar): PAnsiChar; cdecl; varargs; external; 

आपको चाहिए की:

+0

फ़ंक्शन रीसेट करें PushString वास्तव में रिकॉर्ड के अंदर है (मैंने दिया एक सरलीकृत उदाहरण), और मेरे पास pushfstring तक सीधे पहुंच नहीं है। डायरेक्ट कॉल को बाहर रखा गया है। – HNB

4

आवरण आप लिखने की कोशिश कर रहे हैं, में मुफ्त पास्कल संभव है के बाद से मुफ्त पास्कल के लिए 2 के समान घोषणाओं का समर्थन करता है बाहरी कार्यों varargs

function pushfstring(fmt: PAnsiChar; Args: Array of const): PAnsiChar; cdecl; external; 

अद्यतन: मैं डेल्फी में एक ही चाल की कोशिश की है, लेकिन यह काम नहीं करता है:

//function sprintf(S, fmt: PAnsiChar; const args: array of const): Integer; 
//   cdecl; external 'MSVCRT.DLL'; 

function sprintf(S, fmt: PAnsiChar): Integer; 
      cdecl; varargs; external 'MSVCRT.DLL'; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    S, fmt: Ansistring; 

begin 
    SetLength(S, 99); 
    fmt:= '%d - %d'; 
// sprintf(PAnsiChar(S), PAnsiChar(fmt), [1, 2]); 
    sprintf(PAnsiChar(S), PAnsiChar(fmt), 1, 2); 
    ShowMessage(S); 
end; 
+0

इस जानकारी के लिए धन्यवाद, जानना अच्छा है। – HNB

1

बैरी केली मुझे ढेर बदले बिना एक समाधान की मांग के लिए प्रेरित किया है ... यहाँ समाधान (शायद भी RealCall_CDecl, RTTI इकाई से आह्वान इस्तेमाल कर सकते हैं बजाय) है।

// 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.
संबंधित मुद्दे