ส่งผ่านอาร์กิวเมนต์ตัวแปรไปยังฟังก์ชันอื่นที่ยอมรับรายการอาร์กิวเมนต์ตัวแปร


114

ดังนั้นฉันมี 2 ฟังก์ชันที่ทั้งสองมีอาร์กิวเมนต์ที่คล้ายกัน

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

ตอนนี้exampleเรียกexampleBแต่ฉันจะส่งผ่านตัวแปรในรายการอาร์กิวเมนต์ตัวแปรโดยไม่แก้ไขได้อย่างไรexampleB(เนื่องจากมีการใช้ที่อื่นแล้วด้วย)



2
วิธีแก้ปัญหานั้นคือการใช้ vprintf และนั่นไม่ใช่กรณีนี้
ไม่มีให้บริการ

สิ่งนี้เกี่ยวข้อง แต่ไม่เหมือนกับการทำซ้ำที่เสนอ: ส่งต่อการเรียกใช้ฟังก์ชันตัวแปรใน C?
Jonathan Leffler

คำตอบ:


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
สงสัยว่าฉันต้องทำอะไรแบบนี้ปัญหาคือฟังก์ชั่นตัวอย่างโดยพื้นฐานแล้วเป็น wrapper สำหรับ vsprintf และอื่น ๆ อีกไม่มาก: /
ไม่สามารถใช้งานได้

@Xeross: โปรดทราบว่าสิ่งนี้ไม่ได้เปลี่ยนข้อกำหนดภายนอกของสิ่งที่ exampleB ทำ - เพียงแค่เปลี่ยนการใช้งานภายใน ฉันไม่แน่ใจว่าปัญหาคืออะไร
Jonathan Leffler

พารามิเตอร์แรกจำเป็นหรือไม่หรือพารามิเตอร์ทั้งหมดสามารถแปรผันได้หรือไม่
Qwerty

1
@Qwerty: ไวยากรณ์ต้องการอาร์กิวเมนต์แรกที่ตั้งชื่อให้กับฟังก์ชันที่ใช้รายการอาร์กิวเมนต์ตัวแปร, ...ในลายเซ็น หากคุณแปลงอาร์กิวเมนต์ของตัวแปรเป็น a va_listคุณสามารถส่งผ่านva_listไปยังฟังก์ชันอื่นที่ใช้เวลาเพียง a 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...
นาก

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 จำนวนมากมีรูปแบบ v? printf ภายในซึ่ง IMHO ควรเป็นส่วนหนึ่งของมาตรฐาน C รายละเอียดที่แน่นอนแตกต่างกันไป แต่การใช้งานทั่วไปจะยอมรับโครงสร้างที่มีตัวชี้ฟังก์ชันเอาต์พุตอักขระและข้อมูลที่บอกว่าจะเกิดอะไรขึ้น ซึ่งช่วยให้ printf, sprintf และ fprintf ทั้งหมดใช้กลไก 'core' เดียวกัน ตัวอย่างเช่น vsprintf อาจเป็นดังนี้:

เป็นโมฆะ s_out (PRINTF_INFO * p_inf, ถ่าน ch)
{
  (* (p_inf-> destptr) ++) = ช;
  p_inf-> ผล ++;
}

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

จากนั้นฟังก์ชัน core_printf จะเรียกใช้ p_inf-> func เพื่อให้อักขระแต่ละตัวถูกส่งออก จากนั้นฟังก์ชันเอาต์พุตสามารถส่งอักขระไปยังคอนโซลไฟล์สตริงหรืออย่างอื่นได้ หากการนำไปใช้งานเปิดใช้งานฟังก์ชัน core_printf (และกลไกการตั้งค่าใดก็ตามที่ใช้) เราสามารถขยายได้ด้วยรูปแบบต่างๆ



0

ตามความคิดเห็นที่คุณกำลังตัดvsprintfและสิ่งนี้ถูกแท็กเป็น C ++ ฉันขอแนะนำว่าอย่าพยายามทำสิ่งนี้ แต่เปลี่ยนอินเทอร์เฟซของคุณเป็นใช้ C ++ iostreams แทน มีข้อได้เปรียบเหนือprintสายงานเช่นความปลอดภัยในการพิมพ์และความสามารถในการพิมพ์รายการที่printfไม่สามารถจัดการได้ การทำใหม่บางอย่างในตอนนี้อาจช่วยลดความเจ็บปวดได้มากในอนาคต


คุณอ้างถึงข้อดีอะไรบ้าง?
cjcurrie

@cjcurrie: ข้อดีคือความปลอดภัยของประเภทแม้ว่าผู้ใช้กำหนดเอง แน่นอนว่าฟังก์ชัน C ไม่สามารถรองรับประเภทที่ผู้ใช้กำหนดได้เลย
Jonathan Leffler

-1

ด้วยการใช้มาตรฐาน C ++ 0x ใหม่คุณอาจสามารถทำได้โดยใช้เทมเพลตตัวแปรหรือแม้กระทั่งแปลงรหัสเก่านั้นเป็นไวยากรณ์ของเทมเพลตใหม่โดยไม่ทำลายอะไรเลย


น่าเสียดายที่นี่เป็นไปไม่ได้ในทุกกรณี - ลองใช้ lambdas
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.