Ridurre la ripetizione del tipo di ritorno delle funzioni

1

Come esercizio, recentemente ho implementato un semplice lettore di impostazioni class che legge i valori di impostazione da un file INI in un std::map<std::string, std::string> . Il metodo del lettore è simile al seguente:

std::map<std::string, std::string> readSettings() {
    std::map<std::string, std::string> settings;

    for(std::string rawSetting; std::getline(settingsFile, rawSetting); )
        settings.insert(parseRawSettingIntoPair(rawSetting));

    return settings;
}

Mi sento un po 'ridondante specificando il tipo di valore renturn due volte:

  • prima nella dichiarazione del metodo,
  • in secondo luogo quando si dichiara la variabile locale, settings .

Lo so che in il compilatore può dedurre il tipo restituito se utilizzo l'identificatore auto nella dichiarazione di readSettings() , ma questa funzione mi costringerà a mantenere la sua definizione anche nel file di intestazione. C'è un approccio migliore per ridurre tali ripetizioni?

    
posta Akira 29.11.2017 - 15:49
fonte

3 risposte

4

Alcune opzioni:

  • Un alias come using Settings = std::map<std::string, std::string> riduce la digitazione, ma viene comunque ripetuto
Settings readSettings() {
    Settings settings;

    for(std::string rawSetting; std::getline(settingsFile, rawSetting); )
        settings.insert(parseRawSettingIntoPair(rawSetting));

    return settings;
}
  • Costruisci direttamente il valore restituito da una coroutine - ancora abbastanza prolisso
std::map<std::string, std::string> readSettings() {
    using coro_t = boost::asymmetric_coroutine<std::pair<std::string, std::string>>;
    coro_t::pull_type source([&](coro_t::push_type& sink) { 
        for(std::string rawSetting; std::getline(settingsFile, rawSetting); )
            sink(parseRawSettingIntoPair(rawSetting));
    });
    return { begin(source), end(source) };
}
  • Entrambi (perché perché no: P)
risposta data 29.11.2017 - 16:09
fonte
3

Puoi semplicemente utilizzare typedef.

typedef std::map<std::string, std::string> Settings;

Settings readSettings() {
    Settings settings;
    for(std::string rawSetting; std::getline(settingsFile, rawSetting); )
        settings.insert(parseRawSettingIntoPair(rawSetting));
    return settings;
}

... materiale non proprio rivoluzionario, ma fa il lavoro. Per quanto riguarda la ridondanza, è tutta sintassi come la vedo io - non come qualcosa che è soggetto a bug in fase di runtime.

Cercare di rendere il codice più facile da leggere e scrivere possibile non dovrebbe precedere che sia sicuro e collaudato in modo da non doverti preoccupare di cambiarlo continuamente e rileggerlo se me lo chiedi, ma il semplice esempio di typedef sopra è una soluzione rapida e semplice per facilitare lo sforzo senza perdere molto tempo a pensarci o richiedere al compilatore di dedurre il tipo di ritorno dall'implementazione con auto (che richiederebbe l'implementazione per essere visibile a chiamanti come hai indicato).

Finalmente è facile cambiare Settings in un tipo diverso che ha gli stessi requisiti di interfaccia se ne hai mai bisogno - richiede solo di cambiare una riga di codice.

    
risposta data 29.11.2017 - 15:59
fonte
2

Solo per completezza, ecco un approccio che non restituisce il risultato come raccolta STL. Invece, passa i dati in un callback. Questo può o non può soddisfare i requisiti del tuo progetto; è menzionato qui solo per completezza.

Due avvertimenti.

Innanzitutto, la firma per std::function è più brutta di std::map , con parentesi angolate annidate (modello), parentesi (gli argomenti della funzione) e void per nessun vantaggio.

In secondo luogo, se la persona che fornisce il callback fa qualcosa di stupido all'interno del callback (come provare a modificare la raccolta o manipolare la stessa classe), può essere attivato un comportamento non definito. In altre parole, questo approccio di callback è fragile a meno che tutti gli utenti del codice non siano ben informati.

Ancora una volta, questo è fornito per completezza, non come pratica raccomandata.

void readSettings(std::function<
    void(const std::string& key, const std::string& value)> func)
{
    for(std::string rawSetting; std::getline(settingsFile, rawSetting); )
    {
        std::string key;
        std::string value;
        std::tie(key, value) = parseRawSettingIntoPair(rawSetting);
        func(key, value);
    }
}

La motivazione principale per scrivere questa risposta è se il chiamante (consumatore dei dati) è già noto per che rifiuta di utilizzare lo stesso tipo di raccolta STL come funzione. In questo caso, l'insistenza del chiamante nell'usare un tipo diverso significa che il codice della colla è necessario , quindi l'autore della funzione potrebbe anche trasformarsi nel codice della colla.

Tale situazione si verifica quando i due lati del codice sono gestiti da diversi campi di sviluppatori.

    
risposta data 30.11.2017 - 00:11
fonte

Leggi altre domande sui tag