Gerarchia delle eccezioni e uso di quale messaggio per le stringhe di analisi

3

Ho un pezzo di codice che analizza un file di testo riga per riga. Devo raggiungere gli obiettivi: testare la sintassi del testo ed estrapolarne le informazioni. È abbastanza probabile che si verifichino errori di sintassi, quindi voglio fornire informazioni utili su dove e cosa.

Per dare un'idea del problema, ho un file di testo come il seguente (esempio semplificato):

1=\tSTRING\tDevice name
15=\tFLOAT\tSpeed
17=\tINTEGER\tMax Speed
18=INTEGER\tMax Speed

Come puoi immaginare, la sintassi di ogni riga è: <Parameter ID>=\t<Data Type>\t<Description>

Il mio obiettivo è

  • Restituisce un vettore di strutture per ogni parametro.
  • Se si verifica un errore, fornire un messaggio di errore
    • ad esempio: "Errore nella riga 2: il tipo di dati di INTEGER non è consentito"
    • ad esempio: "Errore nella riga 3: scheda mancante"

La mia struttura generale è:

  • Una "funzione": std::vector<ParameterDAO> ParseText (std::string text)
  • Una "funzione secondaria" ParameterDAO ParseTextLine (std::string text)
  • Come puoi immaginare, ParseTextLine è chiamato da ParseText per ogni riga.
  • Alcune "funzioni di subsub" utilizzate da ParseTextLine (controllando gli spazi nel testo, controllando gli elementi per validità / intervallo /...

FYI: le stringhe / sottostringhe si analizzano da sole con le espressioni regolari e alcune operazioni di stringa standard (confronta, ...). Ma questo non è il punto principale della mia domanda.

OK, ora qualche altro dettaglio della mia implementazione:

  1. Qualsiasi mia funzione (ParseText, ParseTextLine, ...) può generare un'eccezione.
  2. lancio sempre l'eccezione standard std::invalid_argument("my error message")
  3. La funzione "ParseText" verifica sempre le eccezioni generate in una delle sottofunzioni per aggiungere il messaggio "Errore in linea x". Questo viene fatto ottenendo il messaggio di eccezione generato, creando una nuova stringa con questo messaggio e le informazioni sulla linea e rilanciando il messaggio:
  4. Il codice che chiama "ParseText" controlla anche le eccezioni. Se si sono verificate eccezioni, verrà visualizzato il messaggio di errore (ad esempio "Errore nella riga 3: scheda mancante" all'utente

Snippet di codice per 3:

try
{
    Parse_HPA_CATEGORY_SingleLine_THROWS;
}
catch ( std::exception e )
{
    std::string l_ErrorMessage = "Error in Line x: ";
    l_ErrorMessage.append ( e.what () );
    throw std::invalid_argument ( l_ErrorMessage.c_str() );
}

Questa struttura funziona e ha il seguente vantaggio:

  • Il messaggio di errore è vicino alla posizione in cui si verifica l'errore (ad esempio vicino a una stringa di confronto o all'espressione regolare).

Ma potrebbero anche esserci alcuni inconvenienti / cose di cui non sono sicuro:

  • Nel test delle unità, devo ripetere la stringa letteralmente (non so se questo è effettivamente cattivo).
  • Ho letto (purtroppo non ricordo dove) che il "quale messaggio" di solito non viene utilizzato per creare direttamente messaggi di errore. Uso improprio il "quale messaggio"? Devo forse deviare una speciale classe di errore di eccezione da std :: exception per ogni caso di eccezione?
  • La funzione ParseText fa una sorta di rethrow. C'è un modo per evitarlo?
posta Semjon Mössinger 02.01.2017 - 12:39
fonte

1 risposta

4

L'utilizzo di exception::what() per il testo che viene mostrato all'utente finale è effettivamente un problema. Supponiamo che il tuo progetto diventi un vero successo e ricevi una richiesta di traduzione in un'altra lingua. Sarà quasi impossibile se il testo che mostri i tuoi utenti viene copiato direttamente da exception::what() .

Il modo corretto è in effetti creare un'eccezione (gerarchia) per i problemi che il parser può incontrare. Ad esempio:

class FormatError : public std::runtime_error
{
public:
  //...
  void set_line_number(int);
  int get_line_number() const;
private:
  int line_number;
};

class InvalidDataType : public FormatError
{
  // ...
private:
  String dataType;
};
// etc.

L'utilizzo di una classe base comune semplifica l'aggiunta delle informazioni sul numero di riga in un secondo momento (in ParseText ) e rilancia la stessa eccezione (con throw; )

    
risposta data 02.01.2017 - 13:15
fonte

Leggi altre domande sui tag