변수 인수 목록을 허용하는 다른 함수에 변수 인수 전달


114

그래서 나는 둘 다 비슷한 인수를 갖는 2 개의 함수를 가지고 있습니다.

void example(int a, int b, ...);
void exampleB(int b, ...);

이제를 example호출 exampleB하지만 수정하지 않고 변수 인수 목록의 변수를 어떻게 전달할 수 있습니까 exampleB(이미 다른 곳에서도 사용됨).



2
그 해결책은 vprintf를 사용하는 것이었고 여기서는 그렇지 않습니다.
사용할 수 없음

이것은 제안 된 복제와 관련이 있지만 확실히 동일하지는 않습니다. C에서 가변 함수 호출을 전달 하시겠습니까?
Jonathan Leffler 19 년

답변:


127

직접 할 수는 없습니다. 다음을받는 함수를 만들어야합니다 va_list.

#include <stdarg.h>

static void exampleV(int b, va_list args);

void exampleA(int a, int b, ...)    // Renamed for consistency
{
    va_list args;
    do_something(a);                // Use argument a somehow
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

void exampleB(int b, ...)
{
    va_list args;
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

static void exampleV(int b, va_list args)
{
    ...whatever you planned to have exampleB do...
    ...except it calls neither va_start nor va_end...
}

2
내가 이와 같은 일을해야만했다고 의심됩니다. 문제는 예제 함수가 기본적으로 vsprintf에 대한 래퍼이고 다른 것은별로 없다는 것입니다. /
사용할 수 없음

@Xeross : 이것은 exampleB가하는 일의 외부 사양을 변경하지 않습니다. 단지 내부 구현 만 변경합니다. 문제가 무엇인지 잘 모르겠습니다.
Jonathan Leffler

첫 번째 매개 변수가 필수입니까, 아니면 모든 매개 변수가 가변적 일 수 있습니까?
Qwerty

1
@Qwerty : 구문에는 , ...서명에 포함 된 변수 인수 목록을 사용하는 함수에 대한 명명 된 첫 번째 인수가 필요합니다 . 변수 인수를으로 변환 한 경우 va_list를를받는 va_list다른 함수에 전달할 수 va_list있지만 해당 함수 (또는 호출하는 함수)는 va_list.
Jonathan Leffler

46

아마도 여기 연못에 바위를 던질 수 있지만 C ++ 11 가변 템플릿으로 꽤 괜찮은 것 같습니다.

#include <stdio.h>

template<typename... Args> void test(const char * f, Args... args) {
  printf(f, args...);
}

int main()
{
  int a = 2;
  test("%s\n", "test");
  test("%s %d %d %p\n", "second test", 2, a, &a);
}

최소한 g++.


혼란 스럽습니다-이것이 C ++> = 11을 사용하는 합법적 인 접근 방식입니까?
Duncan Jones

@DuncanJones 예, 팩이 확장됩니다args...
Swordfish

15

va_list를 받아 전달하는 이러한 함수의 버전을 만들어야합니다. 봐 vprintf예를 들어 :

int vprintf ( const char * format, va_list arg );

5

또한 printf를 감싸고 싶었고 여기에서 유용한 답변을 찾았습니다.

가변 개수의 인수를 printf / sprintf에 전달하는 방법

저는 성능에 전혀 관심이 없었습니다 (이 코드는 여러 가지 방법으로 개선 될 수 있다고 확신합니다. 자유롭게 수행 할 수 있습니다. :)), 이것은 일반적인 디버그 인쇄에만 해당하므로 이렇게했습니다.

//Helper function
std::string osprintf(const char *fmt, ...)
{
    va_list args;
    char buf[1000];
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args );
    va_end(args);
    return buf;
}

그런 다음 이렇게 사용할 수 있습니다

Point2d p;

cout << osprintf("Point2d: (%3i, %3i)", p.x, p.y);
instead of for example:
cout << "Point2d: ( " << setw(3) << p.x << ", " << p.y << " )";

C ++ ostream은 일부 측면에서 아름답지만 숫자 사이에 괄호, 콜론 및 쉼표와 같은 작은 문자열을 삽입하여 이와 같은 것을 인쇄하려는 경우 실제로 끔찍합니다.


2

부수적으로 많은 C 구현에는 IMHO가 C 표준의 일부 여야하는 내부 v? printf 변형이 있습니다. 정확한 세부 사항은 다양하지만 일반적인 구현은 문자 출력 함수 포인터와 발생해야 할 일을 알려주는 정보를 포함하는 구조체를 허용합니다. 이렇게하면 printf, sprintf 및 fprintf가 모두 동일한 '코어'메커니즘을 사용할 수 있습니다. 예를 들어, vsprintf는 다음과 같을 수 있습니다.

무효 s_out (PRINTF_INFO * p_inf, char ch)
{
  (* (p_inf-> destptr) ++) = ch;
  p_inf-> result ++;
}

int vsprintf (char * dest, const char * fmt, va_list args)
{
  PRINTF_INFO p_inf;
  p_inf.destptr = 대상;
  p_inf.result = 0;
  p_inf.func = s_out;
  core_printf (& p_inf, fmt, args);
}

core_printf 함수는 출력 할 각 문자에 대해 p_inf-> func를 호출합니다. 그런 다음 출력 함수는 문자를 콘솔, 파일, 문자열 또는 다른 것으로 보낼 수 있습니다. 구현이 core_printf 함수 (및 사용하는 설정 메커니즘)를 노출하면 모든 종류의 변형으로 확장 할 수 있습니다.


1

가능한 방법은 #define을 사용하는 것입니다.

#define exampleB(int b, ...)  example(0, b, __VA_ARGS__)

0

래핑 vsprintf하고 있으며 이것이 C ++로 태그가 지정되어 있다는 주석을 기반 으로이 작업을 시도하지 않고 대신 C ++ iostreams를 사용하도록 인터페이스를 변경하는 것이 좋습니다. print유형 안전 및 printf처리 할 수없는 항목을 인쇄 할 수있는 것과 같은 기능 라인에 비해 이점 이 있습니다. 일부 재 작업은 향후 상당한 고통을 덜어 줄 수 있습니다.


어떤 이점을 언급하고 있습니까?
cjcurrie

@cjcurrie : 장점은 사용자 정의 유형에서도 유형 안전성입니다. 물론 C 함수는 사용자 정의 유형을 전혀 처리 할 수 ​​없습니다.
Jonathan Leffler 2013

-1

새로운 C ++ 0x 표준을 사용하면 가변 템플릿을 사용하여이 작업을 수행 할 수 있으며, 아무것도 손상시키지 않고 이전 코드를 새 템플릿 구문으로 변환 할 수도 있습니다.


불행히도 모든 경우에 이것이 가능하지는 않습니다. 람다를 사용해보십시오
serup

-1

이것이 유일한 방법이고 .. 최선의 방법이기도하다 ..

static BOOL(__cdecl *OriginalVarArgsFunction)(BYTE variable1, char* format, ...)(0x12345678); //TODO: change address lolz

BOOL __cdecl HookedVarArgsFunction(BYTE variable1, char* format, ...)
{
    BOOL res;

    va_list vl;
    va_start(vl, format);

    // Get variable arguments count from disasm. -2 because of existing 'format', 'variable1'
    uint32_t argCount = *((uint8_t*)_ReturnAddress() + 2) / sizeof(void*) - 2;
    printf("arg count = %d\n", argCount);

    // ((int( __cdecl* )(const char*, ...))&oldCode)(fmt, ...);
    __asm
    {
        mov eax, argCount
        test eax, eax
        je noLoop
        mov edx, vl
        loop1 :
        push dword ptr[edx + eax * 4 - 4]
        sub eax, 1
        jnz loop1
        noLoop :
        push format
        push variable1
        //lea eax, [oldCode] // oldCode - original function pointer
        mov eax, OriginalVarArgsFunction
        call eax
        mov res, eax
        mov eax, argCount
        lea eax, [eax * 4 + 8] //+8 because 2 parameters (format and variable1)
        add esp, eax
    }
    return res;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.