[Megoldva]C++ cstdarg (va_list,stb) probléma

 ( axt | 2008. október 8., szerda - 15:31 )

Adott egy egyszerű exception osztály, ami változó hosszúságú argumentumlistát fogad! Úgy szeretném megcsinálni, hogy a user csak a konstruktoron keresztül adhassa ezt át, míg a gyerekosztályok kapnak egy protected setMessage() függvényt. Logikus lenne, ha a konstruktor a setMessage() függvényt hívná, de valamiért ilyenkor lenyelődik, azaz inkább felülíródik az első paraméter értéke.

Mivel a 'C'-s vsnprintf függvény hívása problámamentes, ezért gondolom azzal lehet összefüggésben a dolog, hogy a setMessage 'C++' függvény.

Nos ismeri valaki a 'C++'-os függvények hívási specifikációját, vagy megadhatom e egy osztály memberfüggvényére, hogy extern "C", vagy egyéb
ötlete illetve magyarázata van valakinek? :)

Ez a kód:

#include 
#include 
#include 

using namespace std;

#define BUFF_SIZE 	1024

class Exception {

public:
	Exception(const char *format, ...) {
		va_list args;
		va_start (args, format);
		setMessage(format, args);
		va_end (args);
	}

	const char* getMessage() const {
		return message.c_str();
	}

//protected:
	void setMessage(const char *format, ...) {
		char buffer[BUFF_SIZE];
		
		va_list args;
		va_start (args, format);
		
		vsnprintf (buffer,BUFF_SIZE, format, args);
		va_end (args);
		
		message = std::string(buffer);
	}
private:
	std::string message;
};

int main(void)
{
	try {
		throw Exception("1:%s,2:%s,3:%s","1","2","3");
	} catch(Exception e) {
		cerr << "Exception: " << e.getMessage() << endl;		
	}
	
	try {
		Exception e("");e.setMessage("1:%s,2:%s,3:%s","1","2","3");	throw e;
	}
	catch(Exception e) {
		cerr << "Exception: " << e.getMessage() << endl;		
	}

}

Ez a kimenet:

Exception: 1:,2:2,3:3
Exception: 1:1,2:2,3:3

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Szerintem a ... paraméterű függvények nem tudnak va_list típusú változót fogadni. Gondolom, ezért van külön printf és vprintf függvény.

Lehet, hogy a va_copy lesz jó Neked - nézd át, én még nem használtam.

Amúgy nekem semmi gondom nem volt C++-ban az stdarg-gal.

+1

/* protected: */ void setMessage(const char *format, ...) helyett void setMessage(const char *format, va_list args). Egy fv vagy (...)-os vagy va_list-es (akkor a va_list akarhanyadik artumentum lehet, es lehet belole tobb is, elvegre az csak egy pointer), de keverni nem lehet...

Ahha! Asszem értem! Ezekszerint a va_list-es marhaság kb. annyi, hogy ad néhány makrót arra, hogy elővakarja a paramétereket (mondjuk a veremből). Maga a va_list egy pointer (void *) amit a va_start-tal állít be, a va_arg-gal léptet + visszaadja a mutatott értéket, a va_end-del meg megszűntet. De amikor a va_list-tel továbbhívok, akkor már egy olyan függvényem van, aminek a második paramétere a void* pointer, ami az előző hívás stackjében mutat valahova. De mivel megint ráhívtam egy va_startot, úgy kezelem mintha n paramétere lenne, tehát az első paraméter helyére kiírta a va_list pointer értékét, a másik kettő meg még ott volt a stackben mögötte/előtte, ezért merő véletlen volt, hogy megjelent.

Jaja. Ha 1/ tutibiztos hogy nincs inline optimalizacio 2/ a parameterek egy stack szeru" dolgon kerulnek atadasra (i386 arch-on forditott sorrendben vermelve), akkor a va_* makrok igy helyettesitheto"ek:

typedef char * va_list;

#define va_start(list,last)    do { list=(char *)(&last)+sizeof(last) } while(0)
#define va_arg(list,type)      (list+=sizeof(type),*((type *)(list-sizeof(type)))
#define va_end(list)           ((void)0)

csak ugye ezt nem hasznalhatod direkte mert a fordito nem a stack-ra pakolja a parametereket... legalabbis nem biztos.

Erre nem is gondoltam. A vprintf paraméterezéséből tippeltem, hogy így lehet továbbhívni tetszőleges '...' paraméteres függvényre. Mindenesetre 'pure' C-ben sem működik, szóval nem C++ specifikus a dolog, hanem nem így kell továbbhívni! Megyek kiderítem, hogy hogyan ... :)

#include 
#include 

void fn2(char *format, ...) 
{
		va_list args;
		va_start (args, format);
		vprintf(format, args);
		va_end (args);
}

void fn1(char *format, ...) 
{
		va_list args;
		va_start (args, format);
		fn2(format, args);
		va_end (args);
}


int main(void)
{
	fn2("1:%s 2:%s 3:%s\n", "1","2","3");
	fn1("1:%s 2:%s 3:%s\n", "1","2","3");
	return 0;
}

1:1 2:2 3:3
1 2: 3:3

használj [code] tageket, mert könnyen elvesznek részletek a kódból. Most csak az include hiányzik, de ha templatek is lennének...

Használtam! De nekem sem jelenik meg az include ...

Teszt:

#include "cica"
#include 
#include 

[ code ]
#include "cica"
#include <cica>
#include <cica.h>
[ /code ]

A paraméterelcsúszásról jut eszembe, tök más dolog, de mégis jól jöhet itt, ezen a szálon: nézd meg a következő gcc-kiterjesztést is:

függvény (const char *form, ...) __attribute__ ((format (printf, 1, 2)));

figelmeztet a fordító, ha nem a formátumnak megfelelő paramétereket adtad. Kis gond: C++-ban elcsúszik a számozás; gondolom, a láthatatlan this paraméter miatt:

class valami
{
    metódus (const char *form, ...) __attribute__ ((format (printf, 2, 3)));
}

Hasznos infó! Szerintem is a this a ludas itt. Én is a this-re gondoltam, amikor paraméterelcsúszást írtam, de azóta sikerült megfejteni a jelenséget.

void setMessage(const char *format, va_list args) {
		char buffer[BUFF_SIZE];
		vsnprintf (buffer,BUFF_SIZE, format, args);
		message = std::string(buffer);
}

igen, ez lett végül, csak így meg kell még egy void setMessage(const char *format, ...) nevű overload is

Ajánlom figyelmedbe a boost::format-ot: szép, típusbiztos, mi kell még?

Köszi! Ha lesz egy kis időm meg fogom nézni! Mármint az egész boost-ot. Régóta rajta van a todo listámon, csak ritkán szoktam C++-ozni :( ...

Kis darabonként kell haladni pont ezért..

Csak kotozkodes: a drupal megette az #include sorokat, &lt; &gt; korbezaro a megoldas. (<stdio.h> = &lt;stdio.h&gt;)
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.