Değişken bağımsız değişkenlerini, değişken bağımsız değişken listesini kabul eden başka bir işleve gönderme


114

Yani her ikisinin de benzer argümanları olan 2 fonksiyonum var

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

Şimdi exampleçağırıyor exampleB, ancak değişken bağımsız değişken listesindeki değişkenleri değiştirmeden nasıl iletebilirim exampleB(bu zaten başka yerlerde de kullanıldığından).



2
Pekala, bunun çözümü vprintf kullanıyordu ve burada durum böyle değil.
Mevcut Değil

Bu, önerilen kopya ile ilgilidir, ancak kesinlikle aynı değildir: C'deki bir variadik fonksiyonun çağrısını ilet?
Jonathan Leffler

Yanıtlar:


127

Bunu doğrudan yapamazsınız; aşağıdakileri alan bir işlev oluşturmanız gerekir 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
Böyle bir şey yapmak zorunda Şüpheli, problem örnek işlevi temelde vsprintf için bir sarıcı ve pek başka geçerli: /
Mevcut Değil

@Xeross: Bunun, exampleB'nin yaptığı şeyin harici özelliklerini değiştirmediğine dikkat edin - sadece dahili uygulamayı değiştirir. Sorunun ne olduğundan emin değilim.
Jonathan Leffler

İlk parametre gerekli midir yoksa tüm parametreler değişken olabilir mi?
Qwerty

1
@Qwerty: Sözdizimi, imzada değişken bağımsız değişken listesi alan bir işlev için adlandırılmış bir ilk bağımsız değişken gerektirir , .... Değişken bağımsız değişkenlerini a'ya dönüştürdüyseniz, yalnızca a alan başka bir işleve va_listgeçirebilirsiniz , ancak bu işlevin (veya çağırdığı işlevin) . va_listva_listva_list
Jonathan Leffler

46

Belki burada bir havuza bir kaya fırlatabiliriz, ancak C ++ 11 çeşitli şablonlarla oldukça iyi çalışıyor gibi görünüyor:

#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);
}

En azından birlikte çalışıyor g++.


Kafam karıştı - bu C ++> = 11 kullanan meşru bir yaklaşım mı?
Duncan Jones

@DuncanJones Evet, paket genişletildiargs...
Swordfish

15

bir va_list alan bu işlevlerin sürümlerini oluşturmalı ve bunları iletmelisiniz. vprintfÖrnek olarak bakın :

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

5

Ayrıca printf'i paketlemek istedim ve burada yararlı bir yanıt buldum:

Printf / sprintf'e değişken sayıda bağımsız değişken nasıl iletilir

Performansla hiç ilgilenmedim (eminim bu kod parçası birkaç şekilde geliştirilebilir, bunu yapmaktan çekinmeyin :)), bu sadece genel hata ayıklama için, bu yüzden şunu yaptım:

//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;
}

daha sonra bunun gibi kullanabilirim

Point2d p;

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

C ++ ostreams bazı yönlerden güzeldir, ancak parantez, iki nokta üst üste ve sayılar arasına virgül gibi bazı küçük dizeler ekleyerek böyle bir şey yazdırmak istiyorsanız pratikte korkunç olur.


2

Bu arada, birçok C uygulamasında, IMHO'nun C standardının bir parçası olması gereken dahili bir v? Printf varyasyonu vardır. Kesin ayrıntılar değişebilir, ancak tipik bir uygulama, bir karakter-çıktı fonksiyon göstericisi ve ne olması gerektiğini söyleyen bilgileri içeren bir yapıyı kabul edecektir. Bu, printf, sprintf ve fprintf'in hepsinin aynı 'core' mekanizmasını kullanmasına izin verir. Örneğin, vsprintf şöyle bir şey olabilir:

void s_out (PRINTF_INFO * p_inf, char ch)
{
  (* (p_inf-> destptr) ++) = ch;
  > Sonucu ++ p_inf-;
}

int vsprintf (char * dest, const char * fmt, va_list değiştirgeler)
{
  PRINTF_INFO p_inf;
  p_inf.destptr = dest;
  p_inf.result = 0;
  p_inf.func = s_out;
  core_printf (ve p_inf fmt args);
}

Core_printf işlevi çıktı alınacak her karakter için p_inf-> func'u çağırır; çıktı işlevi daha sonra karakterleri konsola, bir dosyaya, dizgeye veya başka bir şeye gönderebilir. Bir kişinin gerçeklemesi, core_printf işlevini (ve kullandığı kurulum mekanizmasını) açığa çıkarırsa, onu her çeşit varyasyonla genişletebilir.


1

Olası bir yol, #define kullanmaktır:

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

0

Sarmaladığınız yoruma vsprintfve bunun C ++ olarak etiketlendiğine dayanarak, bunu yapmayı denememenizi öneririm, ancak arayüzünüzü bunun yerine C ++ iostreams kullanacak şekilde değiştirin. printTür güvenliği ve printfüstesinden gelemeyecek öğeleri yazdırabilme gibi işlevlere göre avantajları vardır . Şimdi bazı yeniden çalışmalar, gelecekte önemli miktarda acıdan kurtarabilir.


Hangi avantajları kastediyorsunuz?
cjcurrie

@cjcurrie: Avantaj, kullanıcı tanımlı türlerde bile tür güvenliğidir. Elbette C işlevleri kullanıcı tanımlı türleri işleyemez.
Jonathan Leffler

-1

Yeni C ++ 0x standardını kullanarak, bunu çeşitli şablonlar kullanarak yapabilir veya hatta bu eski kodu hiçbir şeyi bozmadan yeni şablon sözdizimine dönüştürebilirsiniz.


ne yazık ki bu her durumda mümkün değil - sadece lambdaları kullanmayı deneyin
serup

-1

Bunu yapmanın tek yolu bu .. ve bunu yapmanın da en iyi yolu ..

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.