함수를 가변 길이 인수로 래핑하려면 어떻게 해야 합니까?
저는 이것을 C/C++로 하려고 합니다.
Variable Length Arguments를 알게 되었습니다만, 이것은 libffi를 사용한 Python & C의 솔루션을 나타내고 있습니다.
자, 이제 제가 마무리를 하고 싶다면printf
와 기능하다.myprintf
제가 하는 일은 다음과 같습니다.
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;
}
하지만 결과는 기대했던 대로가 아니야!
This is a number: 1244780 and
this is a character: h and
another number: 29953463
내가 뭘 놓쳤지?
문제는 'printf'를 va_va_vs와 함께 사용할 수 없다는 것입니다.변수 인수 목록을 사용하는 경우 vprintf를 사용해야 합니다.vprint, vsprintf, vfprintf 등 (Microsoft의 C 런타임에는 버퍼 오버런 등을 방지하는 '안전한' 버전도 있습니다.)
작업의 샘플은 다음과 같습니다.
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;
}
C++11 에서는, 다음과 같은 방법으로 해결할 수 있습니다.
template<typename... Args>
void myprintf(const char* fmt, Args... args )
{
std::printf( fmt, args... ) ;
}
편집
@rubenvb가 지적한 바와 같이 고려해야 할 트레이드오프가 있습니다.예를 들어 각 인스턴스의 코드를 생성하면 코드가 부풀어오르게 됩니다.
순수하다는 게 무슨 뜻인지 나도 잘 모르겠어
C++에서는 사용
#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);
...
}
사실, 이 함수가 없는 함수를 호출하는 방법이 있습니다.va_list
를 참조해 주세요.어셈블러를 사용하여 스택 내의 인수를 건드리지 않고 함수 반환 주소를 임시로 대체하는 것이 아이디어입니다.
Visual C x86의 예. call addr_printf
콜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
}
}
C 또는 C++ 중 어느 쪽을 사용하십니까?다음 C++ 버전인 C++0x는 이 문제에 대한 해결책을 제공하는 다양한 템플릿을 지원합니다.
또 다른 회피책은 다음과 같은 구문을 얻기 위해 교묘한 연산자 오버로드로 실행할 수 있습니다.
void f(varargs va) {
BOOST_FOREACH(varargs::iterator i, va)
cout << *i << " ";
}
f(args = 1, 2, 3, "Hello");
이걸 작동시키기 위해서 반에서varargs
를 덮어쓰려면 구현해야 합니다.operator =
프록시 오브젝트를 반환하고, 프록시 오브젝트는 이 오브젝트를 덮어씁니다.operator ,
다만, 현재의 C++에서는 타입 삭제로 동작해야 하기 때문에, 이 타입을 안전하게 하는 것은 제가 알기론 불가능합니다.
순수 C/C++ 솔루션이라니?
rest 파라미터(...)는 C 런타임에서 플랫폼 간에 지원됩니다.
http://msdn.microsoft.com/en-us/library/kb57fad8.aspx
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);
언급URL : https://stackoverflow.com/questions/41400/how-to-wrap-a-function-with-variable-length-arguments
'programing' 카테고리의 다른 글
도커 컨테이너로 실행 중인 MariaDB 업그레이드 방법 (0) | 2022.11.18 |
---|---|
mysql에서 글로벌 sql_mode 설정 (0) | 2022.11.18 |
인코딩을 사용해야 합니까?URI 또는 인코딩URL 인코딩을 위한 URIC 구성 요소 (0) | 2022.11.18 |
python 추적 분할 결함 (0) | 2022.11.18 |
Java에서는 상수 클래스를 어떻게 정의합니까? (0) | 2022.11.18 |