문제

변수 수의 인수를 취하는 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 버전.

변수 함수 할 수 있습니다 위험한. 더 안전한 속임수가 있습니다.

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});

웅장한 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 메모리 주소입니다. 그러나 모든 컴파일러와 전화 컨벤션에서 작동하게하는 것은 성가신 일입니다. 따라서 일반적으로 컴파일러와 함께 제공되는 것을 사용하는 것이 더 쉽습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top