Verifica della versione durante la lettura di strutture profondamente annidate

0

Devo aprire un file di salvataggio del gioco, leggerlo, magari modificarlo e quindi salvarlo.

La struttura del salvataggio è abbastanza semplice: è un oggetto con alcuni membri, alcuni dei quali sono dati, mentre altri sono altri oggetti, quegli oggetti hanno alcuni membri, alcuni dei quali sono dati, mentre altri sono altri oggetti, vedi dove sta andando ...

Poiché il gioco è ancora in sviluppo e il formato di salvataggio è in continua evoluzione, gli sviluppatori del gioco hanno aggiunto un membro di versione come int a qualsiasi oggetto che viene modificato.

Ho un codice che ho scritto, ma è incorporato nel mio programma, e voglio estrarlo e creare una DLL che, quando viene fornito un flusso, prova a leggerlo, restituisce l'oggetto di salvataggio se tutto va bene, o restituire un errore se non ... ... in qualche modo.

Ora, il flusso è abbastanza semplice:

  1. Apri un oggetto
  2. Verifica se la versione corrisponde a
  3. Se lo fa, continua a leggere fino al completamento, membri o oggetti (nel qual caso goto 1)
  4. In caso contrario, prendi nota delle informazioni (attesa e leggi la versione, il nome della classe, le ho disponibili a quel punto) e interrompi ogni ulteriore lettura del file. È sufficiente restituire un errore.

Il modo in cui lo faccio attualmente è con i macro. La ragione di ciò è che devo controllare la versione più di 40 volte, e non voglio davvero duplicare la stessa logica che ruota intorno a questo, tanto più che un singolo risultato di controllo della versione dovrà essere valutato su ogni livello fino a quando raggiunge la radice dell'oggetto.

La macro ottiene la versione attesa e attuale e se tutto va bene, continuerà il programma, ma se qualcosa non funziona, registrerà il nome della classe e tornerà dalla funzione in cui si trovava, restituendo il versione corrente, mentre altrimenti la funzione continuerà e restituirà 0. Come questo:

#define CHECK_VERSION(currentValue, expectedValue) {int versionCheck = checkVersion(currentValue, expectedValue); if (versionCheck != 0) return versionCheck;}

//checkVersion() is a function that returns 0 if all is good or, if not, logs it to file and returns the currentValue)

int SomeObject::read(...) {
  int objectVersion;
  someReader::readInt(&objectVersion);
  CHECK_VERSION(objectVersion, expectedVersion);
  //read other stuff
  return 0;
}

Ovviamente, ciò significa che ogni volta che un oggetto legge un altro oggetto, deve controllare se il valore restituito è 0 (se così va bene) o meno (in tal caso, restituire immediatamente quel valore) che è un'altra macro (e quale è ripetuto su ogni livello della struttura, per vedere se qualche oggetto interno non è stato letto)

Alla fine, questo rende un sistema contorto che funziona, ma non sono sicuro di come separarlo in una DLL, né pensare che sia la soluzione migliore.

Il problema principale qui è che voglio creare un modo per l'utente finale, quando prova a leggere un salvataggio o ottiene il salvataggio, o in caso di errore ottiene il nome dell'oggetto che ha fallito insieme al numero di versione che ha causato la mancata corrispondenza.

Non posso semplicemente scriverlo su file, ho bisogno di ottenere in qualche modo le informazioni al livello più alto e lasciare che l'utente decida su come gestire il problema e se registrare qualsiasi cosa o meno.

Quindi la domanda è, come posso strutturare questo? Quando controllo la versione di un oggetto e fallisce, cosa devo fare? Provare a catturare con la mia propria eccezione che si propagherebbe all'oggetto root sembrerebbe un'idea terribile, data la profondità dello stack.

Devo fare i classici codici di errore e creare un oggetto che contenga dati di errore e riportarlo semplicemente verso l'alto, analogamente a come ho restituito il numero singolo? Avrei ancora bisogno di usare macro per tornare presto alla funzione, che lo renderà di nuovo brutto, o mi costringerà a ripetere un sacco di codice che ancora non va bene. Ma questa è l'unica opzione a cui posso pensare in questo momento.

C'è un modo migliore per gestirlo che sarebbe più elegante e non richiederebbe l'uso di macro mantenendo la duplicazione del codice al minimo?

Se vuoi controllare il mio codice esatto, puoi vederlo qui . Il collegamento è al commit pertinente, se qualcuno si imbatte in questo in futuro. Le classi che vuoi guardare sono "EntityCreationData" e "VersionCheck". Il problema appare in molti altri, ma quelli dovrebbero dipingere l'immagine abbastanza bene.

    
posta Karlovsky120 05.03.2018 - 22:38
fonte

2 risposte

2

Le eccezioni sono uno strumento eccellente quando la posizione in cui si rileva un errore è molto lontana (in termini di profondità dello stack) dal punto in cui è possibile gestire l'errore in maniera ragionevole.

Potresti scrivere la tua macro come

#define CHECK_VERSION(currentValue, expectedValue) if (checkVersion(currentValue, expectedValue) != 0) throw VersionMismatch(currentValue, expectedValue)

e scrivi un try {...} catch(VersionMismatch) { handle version mismatch } attorno alla funzione di analisi di livello superiore.

Il meccanismo di gestione delle eccezioni di C ++ garantirà che tutti i processi intermedi siano interrotti e tutti i frame dello stack siano puliti per te (inclusa la chiamata a tutti i distruttori necessari).

    
risposta data 06.03.2018 - 12:56
fonte
1

Se ho capito bene, non devi né restituire codici di errore né generare eccezioni. Invece, restituire un oggetto che contiene stato di analisi e metadati (tipo di oggetto, versione) e il contenuto decodificato.

Utilizzare questo oggetto per ricostruire l'albero degli oggetti nel proprio codice. Nodi 'Colore' in base allo stato di analisi. Tutti i nodi bianchi possono essere analizzati correttamente, mentre i nodi neri hanno un numero di versione sconosciuto / illegale e non consentono (presumo) alcun ulteriore spostamento lungo l'albero.

Molto probabilmente, è ancora possibile fornire funzionalità parziali solo usando il nodo bianco mentre non si toccano i nodi neri.

Suggerirei questo approccio poiché la struttura dei dati cambia così rapidamente e ci si possono aspettare degli errori. Pertanto, puoi costruire questo presupposto nella tua architettura per rendere tutto più semplice per te e per i tuoi utenti.

    
risposta data 06.03.2018 - 09:08
fonte

Leggi altre domande sui tag