문제
변수 수의 인수를 취하는 C 함수가 있다고 가정 해보십시오. 내부에서 변수 수의 인수를 기대하는 다른 함수를 어떻게 호출하고, 첫 번째 함수로 전달 된 모든 인수를 전달할 수 있습니까?
예시:
void format_string(char *fmt, ...);
void debug_print(int dbg_lvl, char *fmt, ...) {
format_string(fmt, /* how do I pass all the arguments from '...'? */);
fprintf(stdout, fmt);
}
해결책
타원을 전달하려면 VA_LIST로 변환하고 두 번째 기능에서 해당 VA_LIST를 사용해야합니다. 구체적으로;
void format_string(char *fmt,va_list argptr, char *formatted_string);
void debug_print(int dbg_lvl, char *fmt, ...)
{
char formatted_string[MAX_FMT_SIZE];
va_list argptr;
va_start(argptr,fmt);
format_string(fmt, argptr, formatted_string);
va_end(argptr);
fprintf(stdout, "%s",formatted_string);
}
다른 팁
장난 꾸러기와 포트할 수없는 트릭에 들어가고 싶지 않다면 (예 : Printf)를 통과 할 수있는 방법은 없습니다.
일반적으로 사용되는 솔루션은 항상 대체 형태의 vararg 기능을 제공하는 것입니다. printf
가지다 vprintf
a va_list
대신 ...
. 그만큼 ...
버전은 주위의 래퍼 일뿐입니다 va_list
버전.
웅장한 C ++ 0x에서는 변수 템플릿을 사용할 수 있습니다.
template <typename ... Ts>
void format_string(char *fmt, Ts ... ts) {}
template <typename ... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts ... ts)
{
format_string(fmt, ts...);
}
기능 호출에 인라인 어셈블리를 사용할 수 있습니다. (이 코드에서는 인수가 문자라고 가정합니다).
void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
{
va_list argumentsToPass;
va_start(argumentsToPass, fmt);
char *list = new char[numOfArgs];
for(int n = 0; n < numOfArgs; n++)
list[n] = va_arg(argumentsToPass, char);
va_end(argumentsToPass);
for(int n = numOfArgs - 1; n >= 0; n--)
{
char next;
next = list[n];
__asm push next;
}
__asm push fmt;
__asm call format_string;
fprintf(stdout, fmt);
}
당신은 또한 매크로를 시도 할 수 있습니다.
#define NONE 0x00
#define DBG 0x1F
#define INFO 0x0F
#define ERR 0x07
#define EMR 0x03
#define CRIT 0x01
#define DEBUG_LEVEL ERR
#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...) if((DEBUG_LEVEL & X) == X) \
DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)
int main()
{
int x=10;
DEBUG_PRINT(DBG, "i am x %d\n", x);
return 0;
}
Formatter를 먼저 버퍼에 저장하여 포맷터를 통과 할 수는 있지만 스택이 필요하며 언젠가는 문제가 될 수 있습니다. 나는 팔로우를 시도했고 잘 작동하는 것 같습니다.
#include <stdarg.h>
#include <stdio.h>
void print(char const* fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vprintf(fmt, arg);
va_end(arg);
}
void printFormatted(char const* fmt, va_list arg)
{
vprintf(fmt, arg);
}
void showLog(int mdl, char const* type, ...)
{
print("\nMDL: %d, TYPE: %s", mdl, type);
va_list arg;
va_start(arg, type);
char const* fmt = va_arg(arg, char const*);
printFormatted(fmt, arg);
va_end(arg);
}
int main()
{
int x = 3, y = 6;
showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
showLog(1, "ERR");
}
도움이 되었기를 바랍니다.
로스의 솔루션이 약간 청소했습니다. 모든 args가 포인터 인 경우에만 작동합니다. 또한 언어 구현은 이전 쉼표를 지원해야합니다. __VA_ARGS__
비어 있습니다 (Visual Studio C ++ 및 GCC DO).
// pass number of arguments version
#define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}
// NULL terminated array version
#define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}
당신이 쓴 전형적인 변박 기능이 있다고 가정 해 봅시다. Variadic이라는 주장보다 적어도 하나의 주장이 필요하기 때문입니다. ...
, 당신은 항상 추가 논쟁을 사용해야합니다.
아니면?
매크로로 다양한 기능을 감싸면 우선 arg가 필요하지 않습니다. 이 예를 고려하십시오 :
#define LOGI(...)
((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
매번 초기 인수를 지정할 필요가 없기 때문에 이것은 훨씬 더 편리합니다.
이것이 모든 컴파일러에 맞는 지 잘 모르겠지만 지금까지 저를 위해 작동했습니다.
void inner_func(int &i)
{
va_list vars;
va_start(vars, i);
int j = va_arg(vars);
va_end(vars); // Generally useless, but should be included.
}
void func(int i, ...)
{
inner_func(i);
}
원한다면 ... inner_func ()에 추가 할 수 있지만 필요하지 않습니다. VA_START는 주어진 변수의 주소를 시작점으로 사용하기 때문에 작동합니다. 이 경우 func ()의 변수에 대한 참조를 제공합니다. 따라서 해당 주소를 사용하고 스택에서 그 후 변수를 읽습니다. inner_func () 함수는 func ()의 스택 주소에서 읽습니다. 따라서 두 기능 모두 동일한 스택 세그먼트를 사용하는 경우에만 작동합니다.
VA_START 및 VA_ARG 매크로는 일반적으로 출발점으로 VAR을 제공하는 경우 일반적으로 작동합니다. 따라서 원한다면 다른 기능에 포인터를 전달하고 그 기능도 사용할 수 있습니다. 자신의 매크로를 충분히 쉽게 만들 수 있습니다. 모든 매크로는 typecast 메모리 주소입니다. 그러나 모든 컴파일러와 전화 컨벤션에서 작동하게하는 것은 성가신 일입니다. 따라서 일반적으로 컴파일러와 함께 제공되는 것을 사용하는 것이 더 쉽습니다.