Wie umschließt man eine Funktion mit Argumenten variabler Länge?
-
09-06-2019 - |
Frage
Ich möchte dies in C/C++ tun.
Ich bin rübergekommen Argumente mit variabler Länge Dies legt jedoch eine Lösung mit Python und C nahe libffi.
Nun, wenn ich einpacken möchte printf
Funktion mit myprintf
Was ich mache, ist wie folgt:
void myprintf(char* fmt, ...)
{
va_list args;
va_start(args,fmt);
printf(fmt,args);
va_end(args);
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 9;
int b = 10;
char v = 'C';
myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b);
return 0;
}
Aber die Ergebnisse sind nicht wie erwartet!
This is a number: 1244780 and
this is a character: h and
another number: 29953463
Irgendein Punkt, wo habe ich übersehen??
Lösung
Das Problem ist, dass Sie „printf“ nicht mit va_args verwenden können.Du musst benutzen vprintf wenn Sie variable Argumentlisten verwenden.vprint, vsprintf, vfprintf usw.(Es gibt auch „sichere“ Versionen in der C-Laufzeitumgebung von Microsoft, die Pufferüberläufe usw. verhindern.)
Ihre Beispielarbeiten lauten wie folgt:
void myprintf(char* fmt, ...)
{
va_list args;
va_start(args,fmt);
vprintf(fmt,args);
va_end(args);
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 9;
int b = 10;
char v = 'C';
myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b);
return 0;
}
Andere Tipps
In C++11 ist dies eine mögliche Lösung mit Variadic templates
:
template<typename... Args>
void myprintf(const char* fmt, Args... args )
{
std::printf( fmt, args... ) ;
}
BEARBEITEN
Wie @rubenvb hervorhebt, sind Kompromisse zu berücksichtigen. Beispielsweise generieren Sie Code für jede Instanz, was zu einer Aufblähung des Codes führt.
Ich bin mir auch nicht sicher, was du mit rein meinst
In C++ verwenden wir
#include <cstdarg>
#include <cstdio>
class Foo
{ void Write(const char* pMsg, ...);
};
void Foo::Write( const char* pMsg, ...)
{
char buffer[4096];
std::va_list arg;
va_start(arg, pMsg);
std::vsnprintf(buffer, 4096, pMsg, arg);
va_end(arg);
...
}
Tatsächlich gibt es eine Möglichkeit, eine Funktion aufzurufen, die nein hat va_list
Version aus einem Wrapper.Die Idee besteht darin, Assembler zu verwenden, Argumente im Stapel nicht zu berühren und die Rückgabeadresse der Funktion vorübergehend zu ersetzen.
Beispiel für Visual C x86. call addr_printf
Anrufe printf()
:
__declspec( thread ) static void* _tls_ret;
static void __stdcall saveret(void *retaddr) {
_tls_ret = retaddr;
}
static void* __stdcall _getret() {
return _tls_ret;
}
__declspec(naked)
static void __stdcall restret_and_return_int(int retval) {
__asm {
call _getret
mov [esp], eax ; /* replace current retaddr with saved */
mov eax, [esp+4] ; /* retval */
ret 4
}
}
static void __stdcall _dbg_printf_beg(const char *fmt, va_list args) {
printf("calling printf(\"%s\")\n", fmt);
}
static void __stdcall _dbg_printf_end(int ret) {
printf("printf() returned %d\n", ret);
}
__declspec(naked)
int dbg_printf(const char *fmt, ...)
{
static const void *addr_printf = printf;
/* prolog */
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
nop
}
{
va_list args;
va_start(args, fmt);
_dbg_printf_beg(fmt, args);
va_end(args);
}
/* epilog */
__asm {
mov esp, ebp
pop ebp
}
__asm {
call saveret
call addr_printf
push eax
push eax
call _dbg_printf_end
call restret_and_return_int
}
}
Verwenden Sie C oder C++?Die nächste C++-Version, C++0x, wird unterstützen Verschiedene Vorlagen die eine Lösung für dieses Problem bieten.
Eine weitere Problemumgehung kann durch geschicktes Überladen von Operatoren erreicht werden, um eine Syntax wie diese zu erreichen:
void f(varargs va) {
BOOST_FOREACH(varargs::iterator i, va)
cout << *i << " ";
}
f(args = 1, 2, 3, "Hello");
Damit dies funktioniert, muss die Klasse varargs
muss zum Überschreiben implementiert werden operator =
das gibt ein Proxy-Objekt zurück, das wiederum überschreibt operator ,
.Allerdings ist es meines Wissens nicht möglich, diesen Variantentyp im aktuellen C++ sicher zu machen, da dies durch Typlöschung funktionieren müsste.
Wie meinen Sie eine reine C/C++-Lösung?
Der Rest-Parameter (...) wird in der C-Laufzeit plattformübergreifend unterstützt.
void myprintf(char* fmt, ...)
{
va_ list args;
va_ start(args,fmt);
printf(fmt,args); ----> This is the fault. vprintf(fmt, args); should have been used.
va_ end(args);
}
If you're just trying to call printf,
there's a printf variant called vprintf that takes
the va_list directly : vprintf(fmt, args);