Tecniche di scrittura

Forum dedicato alla programmazione.

Moderatore: Staff

Regole del forum
1) Citare in modo preciso il linguaggio di programmazione usato.
2) Se possibile portare un esempio del risultato atteso.
3) Leggere attentamente le risposte ricevute.
4) Scrivere i messaggi con il colore di default, evitare altri colori.
5) Scrivere in Italiano o in Inglese, se possibile grammaticalmente corretto, evitate stili di scrittura poco chiari, quindi nessuna abbreviazione tipo telegramma o scrittura stile SMS o CHAT.
6) Appena registrati è consigliato presentarsi nel forum dedicato.

La non osservanza delle regole porta a provvedimenti di vari tipo da parte dello staff, in particolare la non osservanza della regola 5 porta alla cancellazione del post e alla segnalazione dell'utente. In caso di recidività l'utente rischia il ban temporaneo.
Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Tecniche di scrittura

Messaggio da nuitari »

Ecco questa è una funzione del mio "vecchio" server di gaming che mostra l'uso delle IOCP sul Network:

Codice: Seleziona tutto

//o----------------------------------------------------------------------------------
//|	Function		-	DWORD WINAPI WorkerThread (LPVOID ThreadContext)
//|	Date			-	
//|	Author			-	Nuitari
//|	Modified		-
//o----------------------------------------------------------------------------------
//|	Purpose			-	Thread per i socket connessi
//o----------------------------------------------------------------------------------
DWORD WINAPI cNetwork::WorkerThread (LPVOID ThreadContext)
{
	SEH_BEGIN("DWORD WINAPI WorkerThread (LPVOID ThreadContext)")
    cNetwork		*ContextServer = (cNetwork *)ThreadContext;
	BOOL			bSuccess;
    DWORD			dwIoSize = 0, dwRecvNumBytes = 0, dwFlags = 0, dwSendNumBytes = 0;
    cSocket			*lpSocket = NULL;
    LPOVERLAPPED	lpOverlapped = NULL;
	PIO_CONTEXT		lpIoContext = NULL;
	INT				rc = 0;

	BOOL	bCtxtSect = FALSE, bOutBuffSect = FALSE, bCrSectClient = FALSE;


	__try
	{
		InterlockedExchangeAdd(&ContextServer->lWorkThreads,1);
		while (TRUE) 
		{
			if (bOutBuffSect)
			{
				LeaveCriticalSection(&lpSocket->CrSect_OutBuffer);
				bOutBuffSect = FALSE;
			}
			if (bCrSectClient)
			{
				LeaveCriticalSection(&ContextServer->CrSect_Client);
				bCrSectClient = FALSE;
			}

			bSuccess = GetQueuedCompletionStatus(ContextServer->hWorkIOCP, &dwIoSize, (LPDWORD)&lpSocket, &lpOverlapped, INFINITE);
			InterlockedExchangeAdd(&ContextServer->lBusyWorkThreads,1);

			if (ContextServer->lBusyWorkThreads == ContextServer->lWorkThreads && ContextServer->lWorkThreads < ContextServer->lMaxWorkThreads)
			{
				//Gui->MessageLog(L"NETWORK: Spawning New Thread");
				DebugLog("NETWORK: Spawning New Thread");
				HANDLE  hThread;
				DWORD   dwThreadId;
			
				hThread = CreateThread(NULL, 0, WorkerThread, ContextServer, 0, &dwThreadId);
				if (hThread == NULL) 
				{
					//Gui->MessageLog(L"NETWORK: ERROR Spawning New Thread");
					DebugLog("NETWORK: ERROR Spawning New Thread");
				}
			}
			
			if (ContextServer->bEndServer)
				break;

			if (!bSuccess || (bSuccess && (0 == dwIoSize)))
			{
				__try
				{
					__try
					{
						if (lpSocket)
						{
							rc = GetLastError();
							lpSocket->bClose = TRUE;
							lpIoContext = CONTAINING_RECORD(lpOverlapped, IO_CONTEXT, Overlapped);
							if (lpIoContext)
							{
								if (lpIoContext->IOOperation == ClientIoWrite)
								{
									//lpSocket->ClearOut();
									lpSocket->isSending = FALSE;
								}
								lpSocket->CtxtDeAllocate(lpIoContext);
							}
							InterlockedExchangeAdd(&lpSocket->IoPending,-1);
						} else {
							//Gui->MessageLog(L"NETWORK: Invalid Socket in Worker Thread");
							DebugLog("NETWORK: Invalid Socket in Worker Thread");
						}
					}
					__except(EXCEPTION_EXECUTE_HANDLER)
					{
						//Gui->MessageLog(L"NETWORK: DISCONNECT EXCEPTION");
						DebugLog("NETWORK: DISCONNECT EXCEPTION");
					}
				}
				__finally
				{
					if (bCrSectClient)
					{
						LeaveCriticalSection(&ContextServer->CrSect_Client);
						bCrSectClient = FALSE;
					}
				}
				InterlockedExchangeAdd(&ContextServer->lBusyWorkThreads,-1);
				continue;
			}
			
			lpIoContext = CONTAINING_RECORD(lpOverlapped, IO_CONTEXT, Overlapped);
	
			switch(lpIoContext->IOOperation)
			{
				case ClientIoRead:
					InterlockedExchangeAdd(&lpSocket->IoPending,-1);
					lpIoContext->nRecvBytes += dwIoSize;
					lpSocket->AddIn(lpIoContext);
					lpSocket->isWaitRecv = FALSE;
					GetLocalTime(&lpSocket->LastRecv);
					break;

				case ClientIoWrite:
					// a write operation has completed, determine if all the data intended to be
					// sent actually was sent.
					InterlockedExchangeAdd(&lpSocket->IoPending, -1);
					lpIoContext->nSentBytes  += dwIoSize;
					dwFlags = 0;
					if (lpIoContext->nSentBytes < lpIoContext->nTotalBytes) 
					{
						// the previous write operation didn't send all the data,
						// post another send to complete the operation
						//DebugLog("NETWORK: Resending incomplete data");
						//Gui->MessageLog(L"NETWORK: Resending incomplete data..");
						lpIoContext->wsabuf.buf = lpIoContext->Buffer + lpIoContext->nSentBytes;
						lpIoContext->wsabuf.len = lpIoContext->nTotalBytes - lpIoContext->nSentBytes;
						//lpIoContext->nTotalBytes = lpIoContext->wsabuf.len;
						InterlockedExchangeAdd(&lpSocket->IoPending, 1);
						rc = WSASend (
							lpSocket->sd,
							&lpIoContext->wsabuf, 1, &dwSendNumBytes,
							dwFlags,
							&lpIoContext->Overlapped, NULL);
						if (SOCKET_ERROR == rc && (ERROR_IO_PENDING != WSAGetLastError())) 
						{
							rc = WSAGetLastError();
							DebugLog("NETWROK: RESEND ERROR");
							//ContextServer->Disconnect(lpSocket);
							lpSocket->pClient->Disconnect();
						}
					}
					else
					{
						// previous write operation completed for this socket, delete context
						EnterCriticalSection(&lpSocket->CrSect_OutBuffer);
						bOutBuffSect = TRUE;
						lpSocket->CtxtDeAllocate(lpIoContext);
						if (lpSocket->OutBuffer.empty())
						{
							lpSocket->isSending = FALSE;
						} else {
							InterlockedExchangeAdd(&lpSocket->IoPending, 1);
							lpIoContext = *lpSocket->OutBuffer.begin();
							lpSocket->OutBuffer.erase(lpSocket->OutBuffer.begin());
							rc = WSASend(
								lpIoContext->pSocket->sd,
								&lpIoContext->wsabuf, 1, &dwSendNumBytes,
								dwFlags,
								&lpIoContext->Overlapped, NULL);
						}
						if (SOCKET_ERROR == rc)
						{
							rc = WSAGetLastError();
							if (rc != ERROR_IO_PENDING)
							{
								DebugLog("NETWORK: SEND ERROR");
								//ContextServer->Disconnect(lpSocket);
							}
						}
						LeaveCriticalSection(&lpSocket->CrSect_OutBuffer);
						bOutBuffSect = FALSE;
					}
					break;
			}
			InterlockedExchangeAdd(&ContextServer->lBusyWorkThreads,-1);
		}
	}
	__finally
	{
		if (bOutBuffSect)
			LeaveCriticalSection(&lpSocket->CrSect_OutBuffer);
		InterlockedExchangeAdd(&ContextServer->lWorkThreads,-1);
	}
	return FALSE;
	SEH_END_FUNC
}
Per inciso, ricordo che non ho potuto usare le features complete delle IOCP ai tempi perchè il client non le supportava, s'inchidava invariabilmente perchè non riusciva a gestire l'invio non sequenziale dei dati... ma d'altronde quello non l'avevo mica programmato io :P

Avatar utente
Blizzard
Master
Master
Messaggi: 1509
Iscritto il: mar 2 gen 2007, 22:53
Nome Cognome: Giovanni Santostefano
Slackware: 12.2
Kernel: 2.6.27.7-smp
Desktop: Fluxbox
Contatta:

Re: Tecniche di lettura

Messaggio da Blizzard »

ciao,
ho passato il codice alla velocità della rotellina del mouse :P (dopo 2 ore di algoritmi e dati distribuiti ecc... non ho un cervello freschissimo)

A dargli un occhiata meglio comunque spero che ci capirò qualcosa :P (a partire dal fatto che non ho basi di winapi ecc...) in ogni caso mi piace un casino la stilistica e finalmente vedo i blocchi di codice in stile C

Codice: Seleziona tutto

if()
{
}
e non in stile java

Codice: Seleziona tutto

if(){
}
che è uno stile che fondamentalmente odio... perchè comunque avere indentato il blocco insieme alle parentesi di inizio e chiusura aiuta moltissimo.

ciao
Gio

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Re: Tecniche di lettura

Messaggio da nuitari »

Eheh si lo odio pure io... grazie comunque, anch'io adoro leggere il codice così. L'unico prob è che incollandolo nel forum mi ha sballato i tab . dentro l'ide di M$ è tutto + ordinato .

Avatar utente
gallows
Staff
Staff
Messaggi: 3470
Iscritto il: lun 20 set 2004, 0:00
Slackware: 64-current
Kernel: 5.10.7
Località: ~/
Contatta:

Re: Tecniche di lettura

Messaggio da gallows »

Quel modo di sistemare i blocchi non è Java, il K&R è scritto in quel modo... quindi se vogliamo lo stile ufficiale è proprio quello

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Re: Tecniche di lettura

Messaggio da nuitari »

gallows ha scritto:Offtopic: Quel modo di sistemare i blocchi non è Java, il K&R è scritto in quel modo... quindi se vogliamo lo stile ufficiale è proprio quello
Mah, che io sappia quella è la formattazione standard in ANSI C...

Ho provato ad usare la formattazione proposta nei GNU Coding Standards, ma l'ho trovata veramente SCOMODA (ebbene si).

Codice: Seleziona tutto

For the body of the function, we prefer code formatted like this:

if (x < foo (y, z))
  haha = bar[4] + 5;
else
  {
    while (z)
      {
        haha += foo (z, z);
        z--;
      }
    return ++x + bar ();
  }


Avatar utente
gallows
Staff
Staff
Messaggi: 3470
Iscritto il: lun 20 set 2004, 0:00
Slackware: 64-current
Kernel: 5.10.7
Località: ~/
Contatta:

Re: Tecniche di lettura

Messaggio da gallows »

nuitari ha scritto:Mah, che io sappia quella è la formattazione standard in ANSI C...
Non lo sapevo, non ho mai letto direttamente gli standard.
Ho provato ad usare la formattazione proposta nei GNU Coding Standards, ma l'ho trovata veramente SCOMODA (ebbene si).
Concordo, è orribile. Per citare Linus Torvalds:
First off, I'd suggest printing out a copy of the GNU coding standards,
and NOT read it. Burn them, it's a great symbolic gesture.
Io uso quella di linux, quindi con i blocchi alla K&R. Sebbene da un po' usi l'indentazione a 4 (stile Stroustrup), la cosa buona è che rientri tranquillamente nelle 79 colonne, però l'indentazione a 8 la trovo molto più facile da leggere e infatti credo che tornerò a quella.

Ps. non sopporto la caratteristica del codice BSD di scrivere le funzioni in questa maniera:

Codice: Seleziona tutto

static int
f(char *foo, size_t bar)
{
}
(Ah, io l'asterisco lo applico al tipo, non al nome della variabile.)

Avatar utente
Blizzard
Master
Master
Messaggi: 1509
Iscritto il: mar 2 gen 2007, 22:53
Nome Cognome: Giovanni Santostefano
Slackware: 12.2
Kernel: 2.6.27.7-smp
Desktop: Fluxbox
Contatta:

Tecniche di scrittura

Messaggio da Blizzard »

Codice: Seleziona tutto

For the body of the function, we prefer code formatted like this:

if (x < foo (y, z))
  haha = bar[4] + 5;
else
  {
    while (z)
      {
        haha += foo (z, z);
        z--;
      }
    return ++x + bar ();
  }
Sai che jed utilizza direttamente questo tipo di formattazione? all'inizio non capivo e credevo che sballasse da qualche parte #-o
comunque anche IMHO è brutta forte :P
Io uso quella di linux, quindi con i blocchi alla K&R. Sebbene da un po' usi l'indentazione a 4 (stile Stroustrup), la cosa buona è che rientri tranquillamente nelle 79 colonne, però l'indentazione a 8 la trovo molto più facile da leggere e infatti credo che tornerò a quella.
Io di norma utilizzo quella a 8... anche se aimè a volte se una riga sfora la 79 vado oltre...
(Ah, io l'asterisco lo applico al tipo, non al nome della variabile.)
Io lo facevo nei programmi in C++. Scrivendo per classi definivo gli attributi uno per linea e allora mi sembrava più giusto leggere il puntatore come """tipo"""
Type* ptr;
Poi però mi sono accorto che comunque è una cosa sbagliatissima... perchè comunque in quanto abitudine poteva indurmi all'errore o allorquando avevo più variabili dichiarate sulla stessa riga e soprattutto era poco leggibile da parte di altri. Adesso anche quando scrivo OO nelle classi l'asterisco lo attacco comunque al nome e non al tipo.

ciao
Gio

P.S.
Ho dato un'occhiata al comparto grafico e di input di SDL e ho deciso di sviluppare il gioco interamente mediante queste librerie.
Solo per quanto riguarda il comparto sonoro sono ancora deciso ad affrontarlo... diciamo in maniera più dummy affidandomi a librerie tipo audiere che non devi fare altro che aprire un device caricare un file audio e dirgli di riprodurlo... senza impiccarti tra mixer , sampler e cavoli vari.

Avatar utente
Blizzard
Master
Master
Messaggi: 1509
Iscritto il: mar 2 gen 2007, 22:53
Nome Cognome: Giovanni Santostefano
Slackware: 12.2
Kernel: 2.6.27.7-smp
Desktop: Fluxbox
Contatta:

Re: Tecniche di lettura

Messaggio da Blizzard »

ah! altra cosa che ho poco chiara del GNU coding style...

Codice: Seleziona tutto

foo = (char *) malloc (sizeof *foo);
     if (foo == 0)
       fatal ("virtual memory exhausted");
Qua alla malloc fanno un cast esplicito... e lo fanno praticamente sempre... io lo facevo in c++, una volta mi trovavo su comp.lang.c e scrivo per caso una cosa del genere. Giustamente mi fanno notare che il C non richiede, anzi era addirittura deleterio, il cast esplicito e giustamente trovo su wikipedia questa descrizione:
malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. Note that because malloc returns a void pointer, it needn't be explicitly cast to a more specific pointer type: ANSI C defines an implicit coercion between the void pointer type and other pointer types. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in modern C code.[2][3] However, omitting the cast creates an incompatibility with C++, which requires it.
Dove opportunamente spiega che in ANSI C la malloc restituisce un void* e pertanto non è necessario fare un cast.
Perchè loro lo fanno?????
In teoria l'avrei fatto anch'io magari solo per chiarezza... però dopo quella discussione è uscito fuori che la cosa poteva creare anche problemi, quindi personalmente in C non utilizzo più quella scrittura.

Avatar utente
gallows
Staff
Staff
Messaggi: 3470
Iscritto il: lun 20 set 2004, 0:00
Slackware: 64-current
Kernel: 5.10.7
Località: ~/
Contatta:

Re: Tecniche di lettura

Messaggio da gallows »

Quella è una cosa che non capirò mai... Peraltro può creare problemi di questo tipo:
Se hai:

Codice: Seleziona tutto

struct Fred* foo =  malloc(sizeof(struct Fred));                                      
E non hai incluso l'header con il prototipo di malloc() il compilatore ti avverte con un warning del casting da un intero al puntatore.
Invece, se scrivi:

Codice: Seleziona tutto

struct Fred* foo = (struct Fred*) malloc(sizeof(struct Fred));
Per lui è tutto normale, perché fai il casting esplicito. Un compilatore non è detto che ti avverta dell'errore!
gcc, con le flag di default, lo fa, perché malloc() è built-in ma non è detto che io voglia compilare con gcc..
Infatti, anche usando gcc, se passi la flag -fno-builtin lui non dice nulla...

ps. Uhm, siamo passati da tecniche di lettura a tecniche di scrittura... mi sa che ciò andrebbe copiato in un thread specifico...

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Re: Tecniche di lettura

Messaggio da nuitari »

However, omitting the cast creates an incompatibility with C++, which requires it.
Credo stia tutto qui.
Personalmente, trovo errato che non sia richiesto fare un casto esplicito fra (void *) e (char *), in quanto seppur è vero che i pointer sono tutte dword, è concettualmente errato.

Avatar utente
conraid
Staff
Staff
Messaggi: 13630
Iscritto il: gio 14 lug 2005, 0:00
Nome Cognome: Corrado Franco
Slackware: current64
Desktop: kde
Località: Livorno
Contatta:

Re: Tecniche di lettura

Messaggio da conraid »

Blizzard ha scritto: A dargli un occhiata meglio comunque spero che ci capirò qualcosa :P (a partire dal fatto che non ho basi di winapi ecc...) in ogni caso mi piace un casino la stilistica e finalmente vedo i blocchi di codice in stile C

Codice: Seleziona tutto

if()
{
}
e non in stile java

Codice: Seleziona tutto

if(){
}
che è uno stile che fondamentalmente odio... perchè comunque avere indentato il blocco insieme alle parentesi di inizio e chiusura aiuta moltissimo.
Ognuno ha il suo "stile" :-)
Io odio il primo metodo, sarà che ho imparato dal K&R, sarà che sono tardo a modificare una cosa ormai assimilata, ma odio quel tipo di formattazione.
Io come gallows uso il sistema K&R, ma con l'indentazione a 4

Avatar utente
Blizzard
Master
Master
Messaggi: 1509
Iscritto il: mar 2 gen 2007, 22:53
Nome Cognome: Giovanni Santostefano
Slackware: 12.2
Kernel: 2.6.27.7-smp
Desktop: Fluxbox
Contatta:

Re: Tecniche di scrittura

Messaggio da Blizzard »

Personalmente, trovo errato che non sia richiesto fare un casto esplicito fra (void *) e (char *), in quanto seppur è vero che i pointer sono tutte dword, è concettualmente errato.
Si anch'io penso che in linea logica il cast andrebbe messo... tuttavia:
AnticitizenOne wrote:
> On 29 Lug, 14:39, Richard Heathfield <r...@see.sig.invalid> wrote:
>> AnticitizenOne said:

>> > On 29 Lug, 14:04, santosh <santosh....@gmail.com> wrote:
>> >> AnticitizenOne wrote:

>> <snip>

>> >> > b=(struct Sample*)malloc(sizeof(struct Sample));

>> >> The cast isn't recommended in C.

>> > This is really new for me o_O
>> > I use the cast a lot...

>> Why?

>> > why in C is not recommended?

>> Several reasons, but before we get into those reasons, let's find out
>> your reasons for using the cast.

> I use the cast as referenced in C-Language (Kerningan & Ritchie)

That book was written before the first Standard for C was published. At that
point, there was no void * as a generic pointer type, but instead that
purpose was served by the char * type. Since then it's not necessary in C
to cast between a void * and another pointer type. Any pointer can be
converted to a void * and back again without any loss of information.

Infact casting the return value of malloc can prevent your compiler from
warning you when you fail to include stdlib.h, which declares malloc's
prototype. This can lead to possible undefined behaviour.

The only real reason for casting the return of malloc in C is if you're
forced to compile it under a C++ compiler or in a mixed C and C++
environment. That's pretty rare.
Questo è un estratto della discussione che dicevo
discussione originale
si fanno dei riferimenti anche a probabili comportamenti indefiniti #-o
Ognuno ha il suo "stile" :-)
in effetti come darti torto :-k

ciao
Gio

Avatar utente
Blizzard
Master
Master
Messaggi: 1509
Iscritto il: mar 2 gen 2007, 22:53
Nome Cognome: Giovanni Santostefano
Slackware: 12.2
Kernel: 2.6.27.7-smp
Desktop: Fluxbox
Contatta:

Re: Tecniche di scrittura

Messaggio da Blizzard »

ciao,
sto progettando in C++ l'editor per un tile engine.
Devo scrivere la classe per i tiles... ora, per quello che mi è stato sempre riferito, è buona norma definire gli attributi di classe come private e lasciare a metodi Get/Set il compito di prelevare/settare i valori.
A conti fatti tuttavia avendo parecchi accessi agli attributi di quella classe non mi piace per niente l'idea di invocare una funzione per ogni volta che necessito di quel particolare valore...
che ne dite se lo setto come public????

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Re: Tecniche di scrittura

Messaggio da nuitari »

Che è comodo ma non giusto.
L'utilizzo di funzioni get/set permette in seguito di cambiare l'implementazione conservando l'interfaccia, tra le altre cose.

Sappi comunque una cosa. Nei giochi non si programma quasi mai con il paradigma ad oggetti puro, per via delle performance. I Videogames sono il ricettacolo di ogni tecnica di programmazione, dalle più sporche alle più sofisticate.

Avatar utente
Blizzard
Master
Master
Messaggi: 1509
Iscritto il: mar 2 gen 2007, 22:53
Nome Cognome: Giovanni Santostefano
Slackware: 12.2
Kernel: 2.6.27.7-smp
Desktop: Fluxbox
Contatta:

Re: Tecniche di scrittura

Messaggio da Blizzard »

Sappi comunque una cosa. Nei giochi non si programma quasi mai con il paradigma ad oggetti puro, per via delle performance. I Videogames sono il ricettacolo di ogni tecnica di programmazione, dalle più sporche alle più sofisticate.
è questo il punto!
ho una mappa con x tiles... di queste tiles ad una risoluzione di 640x480 ne devo mandare 300 a schermo. Senza contare che ho diversi livelli di tiling!
Le tiles sono in un contenitore statico e a seconda di quali mostrare le prelevo una ad una (e gia qua la vedo brutta) poi devo prelevarne le proprietà associate al tileset :P (se mi metto a chiamare le funzioni non mi spiccio più)
Gia per lo scrolling mi ero cominciato a porre qualche problema... se a livello di prestazioni sto combinando un casino... penso che agirò in questo modo:
Ho 4 livelli di tiles sortate in base all'altezza. Di base le tiles a livello 0 sono il terreno crudo, quelle al livello 1 sono le strade. Quelle al 2 sono gli oggetti sullo stesso piano del player, cui può urtare e il 3 sono tutti gli oggetti (tipo la parte attraversabile di un ponte) cui il player può passare sotto.
Avendo questi 4 livelli se devo disperatamente rinunciare allo scrolling allora quasi quasi li prerenderizzo in 4 surfaces in caricamento e poi li renderizzo come 4 sfondi sovrapposti.
Comunque questa è una questione di poi.
Penso che in virtù di avere un sistema quantopiù veloce rinuncerò se posso ai virtuosismi di una buona programmazione OO (virtualizzazione ecc...).
Spero di non dovermene pentire in fase di scrittura e revisione del codice...

ciao
Gio

Rispondi