मेरा अनुमान है कि कि 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 मानता है, लेकिन यह बहुत छोटा हो सकता है। यदि आवश्यक हो तो बढ़ाएं।
'pushfstring' फ़ंक्शन जिसे आप कॉल करने का प्रयास कर रहे हैं वह बाहरी कार्य है। इसके लिए "प्रत्यक्ष पहुंच नहीं है" असंभव है क्योंकि आप कहीं भी इसके लिए घोषणा कर सकते हैं। हालांकि मैं अज्ञात संख्याओं के पैरामीटर के साथ एक varargs फ़ंक्शन को कॉल करने की आपकी इच्छा की सराहना करता हूं, लेकिन आपको वास्तव में अपने मामले में इसकी आवश्यकता नहीं है क्योंकि आप जहां भी 'पुशस्ट्रिंग' कहलाते हैं, वहां से सीधे 'pushfstring' को कॉल कर सकते हैं। –
@Rob - मुझे संदेह है कि उसके पास एक फ़ंक्शन पॉइंटर है। –
'pushfstring' के लिए सी प्रोटोटाइप क्या है? –