Newsgroup: it.comp.lang.c++
Titolo: chiamata ricorsiva di funzione con ellissi (numero variabile argomenti)

Come da oggetto, o quasi, ho fatto una funzione esterna, o cmq 
di una certa classe X, ma statica, che prende in input vari 
parametri fissi e una lista indefinita, potenzialmente vuota, di 
parametri "stringa AsciiZ" (ossia char *), e crea un array di 
puntatori e lo popola allocando e copiando le stringhe medesime.
Ora in una classe Y diversa da X, vorrei poter usare un 
costruttore con ellissi (sin qui poco male), e dato che 
sostanzialmente fa il medesimo compito della funzione descritta 
prima, ho pensato inizialmente di chiamare la stessa.
Potrei copiare il codice ma, a parte riadattare alcuni dettagli 
interni, non è che ami molto duplicare il codice inutilmente, 
specie se fa esattamente lo stesso compito.
Solo che non so bene come impostare una chiamata delegata, da 
dentro un costruttore che come lista di parametri ha la ELLISSI 
(...) ad una funzione esterna, static, che essa stessa prevede 
la ELLISSI.
Non avendo idea di come funzioni, non ho fatto ancora nessun 
tentativo. Volevo prima cercare di capire come verrebbe vista 
una simile situazione prima ancora di vedere se compila, se 
funge (magari per caso e non sempre etc etc).
Vorrei capire, se qualcuno può spiegarmelo, se e in che modo un 
simile schema può fungere.
Una volta tanto, anche se potrei venirne a capo manipolando lo 
stack, vorrei evitare accrocchi in asm e/o implementare stacks 
esterni al compilato standard.
Quindi se c'è un modo pulito, sono contento, se non c'è e/o è 
persino inconcepibile, chiedo lumi su come potrei risolvere 
altrimenti.
Già in sé l'idea delle ellissi non è una cosa che mi entusiasmi 
molto, ma tant'è è inevitabile. Volevo mantenere accentrato su 
un unico punto (farcito di controlli vari) la gestione delle 
ellissi imprevedibili, e ovunque appoggiarmi su questa unica 
funzione "sicura".
ciao e grazie in anticipo
Soviet:Mario

 funzione esterna, static, che essa stessa prevede la ELLISSI.
Se non ho capito male, quello che stai cercando e' va_start, va_end e 
va_list.
--
Marco 


...che sono definiti nell'header <stdargs.h> o <cstdargs>. Attenzione
che interagiscono male con i costrutti tipici del C++, a cominciare
dalle classi con costruttori e/o distruttori non banali.
Ciao,
Nicola

 (senza averla toccata) di parametri ricevuti
Porta pazienza, perchè potrei non avere capito (vista l'ora e la 
mancanza di caffeina in circolo).
Io ho una classe del genere:
class Ippopotamo
{
public:
  std::string secureprintf(const char * format, ...);
private:
  std::string m_internal_buffer;
};
La funzione si preoccupa di allocare dimensionalmente il buffer da 
usare, e alla fine della fiera chiama la printf con i paremtri in 
ingresso più il buffer. E' questo che ti interessa?
-- 
mai più senza: 
http://www.genux.com/scheda.asp?idprod=47974
la cultura è importante: 
http://www.libreriadellautomobile.it


Premetto di aver lavorato poco con le ellissi (e cercato di evitarle).
Vedi se questo esempio puo' esserti utile (puoi modificare o
overloadare la tua ExtSommatoria() per prendere il va_list anziché le
ellissi).
Lo riporto intero anche se non servirebbe giusto per completezza
(carica da file la query)
//----------------------------------------------------------------------------
char* my_sqlite3_mprintf(const char* buff, va_list ap)
{
  char* sql = sqlite3_vmprintf(buff, ap);
  delete []buff;
  return sql;
}
//----------------------------------------------------------------------------
static LPCSTR makeQueryByParam(LPCTSTR file, ... )
{
  P_File pf(file, P_READ_ONLY);
  if(!pf.P_open())
    return 0;
  uint dim = (uint)pf.get_len();
  LPSTR buff = new char[dim + 1];
  pf.P_read(buff, dim);
  buff[dim] = 0;
  va_list ap;
  va_start( ap, file );
  buff = my_sqlite3_mprintf(buff, ap);
  va_end(ap);
  return buff;
}
//----------------------------------------------------------------------------

 ingresso più il buffer. E' questo che ti interessa?
azz, stavolta non sono sicuro di avere capito io.
Ecco quel che ho capito.
La tua funzione secure print ha effettivamente un numero X di 
parametri, li esplora e li copia, formattandoli, in un suo 
buffer, poi lo passa a printf con DUE parametri, di cui il 
secondo è la stringa di testo costruita e il primo un "%s", 
suppongo.
Ebbene, se ho inteso giusto, non era quel che volevo.
Io avrei voluto che, senza nemmeno esplorarli (anche perché 
inutile se l'esplorazione serviva solo a delegare una chiamata), 
la tua secure print potesse passare pari pari a printf la lista 
di X parametri che aveva ricevuti essa stessa.
Ma ora probabilmente mi sono reso conto che è una situazione che 
esula dal potere di un compilatore, perché la chiamata attuale 
di printf dentro secureprintf, come potrebbe sapere quali 
parametri mettere sullo stack se non sono scritti a mano ?
ah, già che ci sono, la versione monostadio, che in teoria 
dovrebbe funzionare, non mi gira per niente anche se compila 
senza nessun errore.
La posto cmq per chi fosse interessato a capire come mai. Ho 
provato a guardare il disassemblato, ma non l'ho capito molto, 
ci sono tante costanti che non riconosco.
Inoltre non so perché ma nel caso in cui la chiamata alla 
funzione "ellittica" venga fatto con dei literals stringa (testo 
virgolettato), invece degli indirizzi delle costanti il 
compilatore copiava sullo stack dei puntatori nulli, e anche il 
debugger mi dava indirizzo nullo a runtime ... mistero. Che sia 
una forma di protezione, un filtro anti-modifica delle costanti 
? Non saprei.
Ecco la routine (è un po' menosa perché ho messo una gestione 
cauta dell'allocazione e liberazione memoria)
Sorry per la formattazione che va persa con gli acapo automatico
ciao
Soviet_Mario
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "stdafx.h"
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <stdio>
#include <stdarg>
#include <string>
#include <memory>
//  #include <varargs>
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
char * FzBuildStringArrayFromVariableStringList (unsigned int 
ArgNumStrings, char * * & ArgRefPtrStringsArray, unsigned int * 
& ArgRefArrStringsSizes, unsigned int & RefTotalBytesNeeded, ...)
{
	if (ArgNumStrings == 0)
		return ("BuildTemplateStringVector : errore = passato un 
numero (dichiarato) nullo di argomenti a stringa. Passare una 
lista valida");
	// parametro scansione argomenti variabili
	va_list marker;
	// prima scansione argomenti variabili
	va_start (marker, RefTotalBytesNeeded);     /* Initialize 
variable arguments. */
	unsigned int StringsScanned = 0;
	unsigned int CurrSize;
	PtrChar CurrString;
	unsigned int J;
	RefTotalBytesNeeded = 0;
	while (1)
	{
		if (StringsScanned >= ArgNumStrings)
			break;
		CurrString = va_arg (marker, PtrChar);
		if (CurrString == 0)
			break;
		if ((CurrSize = strlen (CurrString)) == 0)
			break;
		StringsScanned ++;
		RefTotalBytesNeeded += (CurrSize + 1);
	};
	va_end (marker);              /* Reset variable arguments.      */
	if (StringsScanned != ArgNumStrings)
		return ("BuildTemplateStringVector : errore = inconsistenza 
tra il numero dichiarato di argomenti passati e quelli passati 
effettivamente. Controllare le eventuali stringhe nulle o gli 
zeri, NON ammessi.");
	RefTotalBytesNeeded += (ArgNumStrings * (sizeof (unsigned int) 
+ sizeof (char *)));
	// allocazione memoria di STRUTTURA
	// alloca : array di puntatori e con le dimensioni delle 
singole stringhe
	try
	{
		if ((ArgRefPtrStringsArray = new char * [ArgNumStrings]) == 0)
			return ("BuildTemplateStringVector : errore = memoria non 
sufficiente per l'array di puntatori alle stringhe.");
	}
	catch (...)
	{
		if (ArgRefPtrStringsArray == 0)
			return ("BuildTemplateStringVector : errore = memoria non 
sufficiente per l'array di puntatori alle stringhe.");
	};
	// allocazione array di dimensioni ed on-fail distruzione 
precedente array di puntatori
	try
	{
		if ((ArgRefArrStringsSizes = new unsigned int [ArgNumStrings]) 
== 0)
		{
			delete [] ArgRefPtrStringsArray;
			return ("BuildTemplateStringVector : errore = memoria non 
sufficiente per l'array con le singole dimensioni delle stringhe.");
		};
	}
	catch (...)
	{
		if (ArgRefArrStringsSizes == 0)
		{
			delete [] ArgRefPtrStringsArray;
			return ("BuildTemplateStringVector : errore = memoria non 
sufficiente per l'array con le singole dimensioni delle stringhe.");
		};
	};
	// seconda scansione : ricalcolo dimensioni ed
	// allocazione memoria sparsa delle singole stringhe
	StringsScanned = 0;
	va_start (marker, RefTotalBytesNeeded);     /* Re-Initialize 
again variable arguments. */
	while (1)
	{
		if (StringsScanned >= ArgNumStrings)
			break;
		if ((CurrString = va_arg (marker, char *)) == 0)
			break;
		if ((ArgRefArrStringsSizes [StringsScanned] = strlen 
(CurrString)) == 0)
			break;
		try
		{
			if ((ArgRefPtrStringsArray [StringsScanned] = new char [++ 
ArgRefArrStringsSizes [StringsScanned]]) == 0)
			{
				for (J = StringsScanned; J != 0; J --)
					delete [] ArgRefPtrStringsArray [J - 1];
				delete [] ArgRefArrStringsSizes;
				delete [] ArgRefPtrStringsArray;
				return ("BuildTemplateStringVector : errore = memoria non 
sufficiente per allocare i dati effettivi di una delle singole 
stringhe.");
			};
 
		}
		catch (...)
		{
			if (ArgRefPtrStringsArray [StringsScanned] == 0)
			{
				for (J = StringsScanned; J != 0; J --)
					delete [] ArgRefPtrStringsArray [J - 1];
				delete [] ArgRefArrStringsSizes;
				delete [] ArgRefPtrStringsArray;
				return ("BuildTemplateStringVector : errore = memoria non 
sufficiente per allocare i dati effettivi di una delle singole 
stringhe.");
			};
		};
		StringsScanned ++;
	};
	va_end (marker);              /* Reset variable arguments.      */
	// tutta la memoria è stata allocata
	// terza e ultima scansione : copia fisica dei dati delle 
singole stringhe
	va_start (marker, RefTotalBytesNeeded);     /* Re-Initialize 
again variable arguments. */
	for (J = 0; J < ArgNumStrings; J ++)
	{
		if ((CurrString = va_arg (marker, char *)) == 0)
			break;
		// copia fisica a dimensione prefissata
		memcpy (ArgRefPtrStringsArray [J], CurrString, 
ArgRefArrStringsSizes [J]);
		// appends il null finale
		(ArgRefPtrStringsArray [J])[ArgRefArrStringsSizes [J] - 1] = 0;
	};
	va_end (marker);              /* Reset variable arguments.      */
	return 0;
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
	char * * ArrStr = 0;
	char * Errore = 0;
	unsigned int TotalBytesNeeded = 0;
	unsigned int * ArrSizes = & TotalBytesNeeded;
	char * Pippo = "Pippo";
	char * AHIA = "AHIA";
	char * O356 = "0356";
	char * pluto = "pluto";
	char * O1357909731 = "1357909731";
	char * EMPTY = "";
	char * Null = (char *) 0;
	Errore = FzBuildStringArrayFromVariableStringList (unsigned int 
(5), ArrStr, ArrSizes, TotalBytesNeeded, Pippo, AHIA, O356, 
pluto, O1357909731, EMPTY, Null);
	Errore = FzBuildStringArrayFromVariableStringList (unsigned int 
(5), ArrStr, ArrSizes, TotalBytesNeeded, (char *)"Pippo", (char 
*)"AHIA", (char *)"0356", (char *)"pluto", (char *)"1357909731", 
(char *)"", (char *)0);
	if (Errore != 0)
	{
		printf ("\n Errore : \n%s", Errore);
		return 0;
	};
	for (unsigned J = 0; J < 5; J ++)
		printf ("\nStringa [%d], Size [%d], Testo <<%s>>", J, ArrSizes 
[J], ArrStr [J]);
	printf ("\n\nDimensioni originali [33] bytes; \nBytes totali 
usati array e struttura usata [%d].", TotalBytesNeeded);
	return 1;
};
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 (carica da file la query)
ciao ... grazie.
Me la studio un po', non avevo mai visto passare un argomento di 
tipo va_list (che in effetti pensavo essere una macro ! ! piů 
che un tipo vero e proprio).
Intrigante cmq, per quel che ho capito alla prima occhiata.
Magari provo a vedere di nuovo il disassemblato, anche se mi 
sono accorto di capirci sempre meno .
Ciao e grazie di nuovo.
Soviet


 Io avrei voluto che, senza nemmeno esplorarli 
Infatti è quel che circa faccio. Guardando meglio però il *circa* mi 
rendo conto che probabilmente non è quello che cerchi.
namespace Ippopotamo
{
   class StrPrintf
   {
   public:
      std::string CDECLARE format(const char *, ...);
   private:
      enum eIncrement{VALUE = 1024};
      char buffer_piccolo[Stringhe::StrPrintf::VALUE];
   };
}
/**********************************************************************\
* Uso       data una stringa di formattazione e dei parametri crea una
*           stringa (funzionamento simile alla sprintf)
*
* Avvertenze Da usare con cautela come la printf.
*
* Parametri IN
*  formato  = formato formattazione
*  ...      = eventuali parametri aggiuntivi
*
* Ritorna   stringa formattata
\**********************************************************************/
std::string CDECLARE Ippopotamo::StrPrintf::format(const char * 
formato, ...)
{
   va_list marker;
   va_start( marker, formato );
	int nBuf;
   std::string sRet;
   unsigned int dim_grande = Ippopotamo::StrPrintf::VALUE;
   char * buffer_grande = 0;
	//provo con il buffer statico
#if (_MSC_VER >= 1400) //VS 2005 o superiore
	nBuf = vsnprintf_s (buffer_piccolo, dim_grande, dim_grande-1, formato, 
marker);
#else
	nBuf = _vsnprintf (buffer_piccolo, dim_grande-1, formato, marker);
#endif
	if(nBuf >= 0)
   {
      sRet = buffer_piccolo;
   }
   else
   {
	   while(true)
	   {
         if(buffer_grande)
            free(buffer_grande);
         dim_grande += Ippopotamo::StrPrintf::VALUE;
         buffer_grande = (char*)malloc(dim_grande);
         if(!buffer_grande)
            break;
#if (_MSC_VER >= 1400) //VS 2005 o superiore
			nBuf = vsnprintf_s (buffer_grande, dim_grande, dim_grande-1, 
formato, marker);
#else
			nBuf = _vsnprintf (buffer_grande, dim_grande-1, formato, marker);
#endif
		   if(nBuf >= 0)
         {
            sRet = buffer_grande;
            break;
         }
      }
   }
	va_end(marker);
   if(buffer_grande)
      free(buffer_grande);
	return sRet;


 vero e proprio).
C'e' un intero insieme di funzioni che della libreria standard che si 
passa delle va_list, proprio per ovviare al tipo di problemi che hai tu :)
(vprintf, vsprintf, vfprintf ...)
Bye,
  Gabry

 quasi.
vedo, non avevo capito del tutto bene.
Mi pare che anche tu riesca a usare va_list come una black-box 
da delegare (passare intonsa a routine terze).
Devo impossessarmi anche io di questa tecnica, studierò il tuo e 
quell'altro codice che mi hanno già dato (era LogicMind, vero ? 
Mi pare di si)
grazie
ciao
Soviet_Mario
P.S.
per adesso studio questo materiale, eventualmente torno a 
chiedere in seguito.
P.P.S.
c'è anche la questione della funzione che non gira, ma questa è 
una grana più fastidiosa ....


 seguito.
Volendo potresti anche passare un puntatore a puntatori a void, o un 
vettore di puntatori a void. Con un po' di lavoro probabilmente riesci 
a mascherare bene la cosa con una classe che contiene i puntatori.
Chi si occupa di recordset ha questo tipo di problemi. Comunque dipende 
sopratutto dai tuoi gusti.
Do per scontato che non hai usato tecniche che facevano uso dei 
template perché può capitare che i parametri siano *molti*. Per esempio 
per comodità mi sono fatto l'estensione per poveri del pair
template<typename _Ty1,	typename _Ty2 = _Ty1, typename _Ty3 = _Ty2, 
typename _Ty4 = _Ty3, typename _Ty5 = _Ty4> struct quintuple
{
	typedef _Ty1 first_type;
	typedef _Ty2 second_type;
	typedef _Ty3 third_type;
	typedef _Ty4 quarter_type;
	typedef _Ty4 fifth_type;
	quintuple(): first(_Ty1()), second(_Ty2()), third(_Ty3()), 
quarter(_Ty4()), fifth(_Ty5())	{		}
	quintuple(const _Ty1& _Val1, const _Ty2& _Val2, const _Ty3& _Val3, 
const _Ty4& _Val4, const _Ty5& _Val5)
		: first(_Val1), second(_Val2), third(_Val3), quarter(_Val4), 
fifth(_Val5)		{		}
	_Ty1 first;
	_Ty2 second;
	_Ty3 third;
	_Ty4 quarter;
	_Ty5 fifth;
};
E il tipo lo puoi sapere, per esempio, attraverso funzioni del genere:
namespace is_T
{
	template <typename T> inline
	bool IsSimpleType(T*) {return false;}
	template <> inline
	bool IsSimpleType<signed long>(signed long*) {return true;}
	template <> inline
	bool IsSimpleType<unsigned long>(unsigned long*) {return true;}
	template <> inline
	bool IsSimpleType<signed int>(signed int*) {return true;}
	template <> inline
	bool IsSimpleType<unsigned int>(unsigned int*) {return true;}
	template <> inline
	bool IsSimpleType<signed short>(signed short*) {return true;}
	template <> inline
	bool IsSimpleType<unsigned short>(unsigned short*) {return true;}
	template <> inline
	bool IsSimpleType<signed char>(signed char*) {return true;}
	template <> inline
	bool IsSimpleType<unsigned char>(unsigned char*) {return true;}
