Funzione C++ che restituisce un oggetto stringa (string).

di il
10 risposte

Funzione C++ che restituisce un oggetto stringa (string).

Salve a tutti, scrivo qui la prima volta.

Vi espongo il mio dubbio: in Java è normale creare delle funzioni che restituiscono oggetti String, mentre se non sbaglio in C++ ciò non andrebbe fatto, e bisognerebbe passare come parametro della funzione un riferimento alla variabile che conterrà il risultato.

Mi sono imbattuto però nel seguente caso:
Abbiamo una funzione in una DLL scritta in C++ (vecchio Visual studio 6). La DLL viene chiamata all'occorrenza da un programma che passa dei parametri e che riceve una string dalla DLL. La string è normale, non static. Per capirci faccio un esempio con la funzione f() che, per comodità non riceve parametri e restituisce sempre la stessa stringa:
//da qualche parte nella DLL...
string f()
{
	string r = "facciamo finta di aver fatto calcoli spettacolari che ora sono riassunti in questa stringa";
	return r; //l'oggetto, non un puntatore
}

//da qualche altra parte nella DLL...
string var_risultato_f = f();   // la variabile risultato è sempre assegnata correttamente!!

//var_risultato_f viene eventualmente modificata e restituita al programma che chiama la DLL.
La funzione funziona, usata migliaia, forse milioni di volte. La stringa restituita da f() è sempre quello che ci si aspetta.
Domanda da un milione di dollari, la funzione è pericolosa? Cioè può capitare che un giorno restituirà un valore errato?

Sul suo funzionamento corretto, le possibili spiegazioni che mi sono venute in mente:
1. il caso che la chiamata a f() restituisca un riferimento ad un oggetto non valido è possibile ma mooolto poco probabile. Segue che bisognerebbe comunque modificarla per evitare rischi inutili (quale è il modo migliore?);
2. il fatto che la funzione sia in una DLL crea le condizioni in memoria affinché la chiamata ad f() sia sempre sicura (diverso se fosse in un .exe);
3. questo tipo di chiamata è sicuro in ogni caso (almeno in C++), chi mi ha detto che non è corretto si è sbagliato. Posso dormire sonni tranquilli;
4. dipende dal compilatore (?!)

altre idee?

10 Risposte

  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    Non funziona se le runtime CRT con cui siano compilati l'exe e la dll siano diversi. Può funzionare ma può bennissimo andare in crash. Infatti è sempre consigliato usare le stesse versioni runtime in fase di compilazione per le dll e l'exe che ne fa uso. Se vedi per garantire il corretto funzionamento (parlo di Windows) Microsoft usa solo dati POD nelle sue funzioni e mai classi.

    leggi quà
    http://stackoverflow.com/questions/11601192/interface-between-exe-and-dll-with-different-c-c-runtime-library

    Quindi come per l'esame della patente se c'è scritto "in ogni caso" è da considerare falso (vedi il tuo punto 3)
  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    Tutto il mio dubbio è dentro la DLL, la comunicazione tra DLL e programma chiamante la considero sicura.

    Da quanto hai scritto, mi pare di capire che sei per la modifica alla funzione:
    
    //DLL...
    void f(string *risultato)
    {
        &risultato = "blabla";
    }
    
    //da qualche altra parte nella DLL...
    string  val_risultato_f;
    f(&val_risultato_f); //faccio modificare la variabile dalla funzione
    
    Anche io credo sia la soluzione migliore, ma vorrei sapere se è possibile lasciare tutto come è, per la difficoltà a reperire MS VisualStudio 6 e per il noto il concetto "non toccare ciò che funziona" .
    Cioè, tralasciando le parole "in ogni caso" del punto 3, e riferendosi solo al mio caso particolare.

    So che forse è chiedere troppo, ma ci provo.
  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    No io sono per un altro tipo di gestione.
    La DLL può essere fatta con qualsiasi compilatore MA (c'è sempre un ma) deve esporre all'esterno solo dati POD e chiedere dati POD.
    In nessun caso deve esporre classi STL compilati con versioni diversi del compilatore.
    Esempio
    
    //DLL
    std::string *ptr = NULL;
    
    DLL_RESULTS GetData(TCHAR * str,size_t *size)
    {
        if(!size)
        {
           return POINTER_ERROR;
        }
        else
        {
            ptr = new std::string(size);
        }
        str = ptr->c_str();
    }
    
    Come vedi nella dll puoi mettere tutte le classi che vuoi ma ciò che l'exe vede sono solo dati POD.
    DLL_RESULTS è un enum.
    Ma se non puoi modificare la dll non ho ancora capito il problema
  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    Un link che spiega la questione:
    http://chadaustin.me/cppinterface.htm

    Tra l'altro tempo fa mi è capitato di metter mani su un programma che esportava funzioni (con dati STL) e classi col metodo Microsoft. Ho dovuto installare lo stesso compilatore e stesso service pack, altrimenti avevo crash ogni 3ns.
  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    Ecco lo stavo cercando anch'io ma non mi ricordavo il sito. Thnx shodan
    A proposito tu come la pensi?
  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    Beh, per il passaggio di parametri tra exe e DLL non c'è molto più da dire di quanto già detto: il problema principale è sempre la stabilità dell'implementazione di ciò che si passa. Per restare nel topic, l'implementazione di std::string di VC2008 è diversa da quella del VC2010 e superiori (in VC2008 non c'è la move semantics).

    Direi che l'unico modo sicuro di procedere è quello di passare solo parametri POD (come in C) mentre se si passano proprie classi alle DLL è meglio che siano interfaccie astratte da implementare nel codice. Xerces ad esempio utilizza questo metodo e funziona molto bene.

    Il bello di quel link poi è che implementa l'operator delete trick col quale è possibile effettuare la delete su un puntatore a una classe esportata, il che non è male in ottica di smart pointer.
    Personalmente ho adottato i suggerimenti di quel link in ancune DLL che ho scritto (e che utilizzano allocatori diversi da quelli di sistema, come il Doug Lea allocator o la TBB allocator) e il programma non fa una piega lato exe.

    Un aspetto non secondario è quello di poter creare e utilizzare una DLL in modalità release senza doverne creare quattro a seconda che si utilizzi la CRT DLL o quella statica. Vero che il codice si duplica (se la DLL è linkata con la CRT statica) e la DLL è più grossa, ma in un'ottica di pura flessibilità, a mio avviso, è più un vantaggio che uno svantaggio. Questo comporta anche che la DLL dev'essere caricata esplicitamente (con LoadLibrary), ma anche questo lo trovo più un vantaggio che uno svantaggio.

    Una cosa importante invece è che la funzione della DLL che si utilizza sia più un wrapper per eventuali eccezioni, che non devono assolutamente uscire dalla DLL (lo srotolamento dello stack può cambiare da compilatore a compilatore e anche tra diverse versioni dello stesso compilatore).
    Di solito utilizzo uno schema più o meno così:
    
    void funzione_dll(const char*  val) {
        try {
            std::string str(val);
            internal_function(str);
        } catch (const std::exception& e) {
            
        } 
    } 
    
    in modo che eventuali eccezioni non escano.
    Per le DLL che ho scritto finora non ho mai avuto problemi con questo sistema.
  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    Condenso varie risposte in una. Temo di non essermi spiegato .
    La comunicazione tra il programma e la DLL non mi interessa, è sicura e fatta secondo i migliori standard.

    All'interno della DLL tutto è ben progettato tranne il fatto che ho quella funzione che restituisce una stringa ad un'altra funzione della DLL stessa: std::string f(...). Poi la dll fa altre cose usando il risultato parziale di f(), ed alla fine restituisce il tutto al programma chiamante, che è ciò che ci si aspetta (la DLL ha funzionato bene).
    Ma vedendo quella funzione famigerata, ho dei dubbi... Mi chiedevo se sarebbe meglio cambiare per evitare una remotissima possibilità che l'oggetto std::string restituito possa essere qualcosa di inaspettato, e se tale possibilità esiste nelle condizioni che ho descritto (MS Visual Studio 6). Ma (visto che è difficile conoscere le differenze tra un compilatore e l'altro, forse anche per chi l'ha creato) anche considerando il caso generale.

    In Java il "return String" è normale ma in C++ un po' meno (o peggio...). Credo che il programmatore che ha scritto quella funzione pensasse in Java.

    Sì, io posso cambiare la DLL ma quello che mi chiedo e se vale davvero la pena sporcarsi le mani ed eventualmente prendersi delle responsabilità che mi eviterei volentieri se i rischi di fallimento non ci sono.
    Ovviamente, c'è anche la curiosità e la voglia di chiarire al meglio la cosa con chi ne sa più di me.
  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    Cicciolinuks ha scritto:


    Condenso varie risposte in una. Temo di non essermi spiegato .
    In Java il "return String" è normale ma in C++ un po' meno (o peggio...).
    Il fatto è che in Java il tipo String ha una precisa implementazione, che mantiene compatibilità binaria tra le varie JVM. In C++ questo non è garantito e dato che esistono diversi compilatori, e che lo standard si è MOLTO evoluto dai tempi di VC++6, il tipo std::string ha cambiato letteralmente implementazione. Inoltre mentre in Java gli oggetti hanno un reference counter interno che tiene conto di quante volte l'oggetto in questione è usato, il C++ non ha niente del genere. Tieni presente che il C++ non ha nessun standard binario a differenza di Java.

    Il fatto che quella funzione restituisca un std::string e non un reference a std::string non solo è normale, ma è indispensabile per non causare crash.

    Se le funzioni della DLL sono chiamate solo all'interno della DLL stessa, non c'è nessun problema sul tipo di parametri che usano: è un mondo a parte che utilizza cose sue.
    Il problema nasce nel momento in cui il tuo programma deve passare o ricevere dati alla/dalla DLL. Utilizzando dei tipi STL (di cui è standard l'interfaccia, ma non l'implementazione) un compilatore VC++ recente riceverà dati corrotti da una std::string (o altro) prodotta dalla DLL, appunto perché l'implementazione è differente.

    Ricapitolando: finché la funzione f() è usata solo all'interno della DLL va tutto bene;
    se la funzione f() è usata anche per restituire dei dati al programma chiamante possono esserci problemi di corruzione dati.
    Credo che il programmatore che ha scritto quella funzione pensasse in Java.
    Pazza idea... Il C++ è una cosa, Java un'altra: è come scambiare dinamite per un petardo.
    Sì, io posso cambiare la DLL ma quello che mi chiedo e se vale davvero la pena sporcarsi le mani ed eventualmente prendersi delle responsabilità che mi eviterei volentieri se i rischi di fallimento non ci sono.
    Quante e quali funzioni il tuo programma si interfaccia alla DLL?
    Riporta i prototipi.
  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    shodan ha scritto:


    (...)
    Il fatto che quella funzione restituisca un std::string e non un reference a std::string non solo è normale, ma è indispensabile per non causare crash.

    Se le funzioni della DLL sono chiamate solo all'interno della DLL stessa, non c'è nessun problema sul tipo di parametri che usano: è un mondo a parte che utilizza cose sue.
    Allora sono a posto!

    Come ho spiegato, a me interessava solo la parte dentro la dll, la comunicazione DLL-programma è sicura. Forse avrei potuto iniziare la discussione senza menzionare il fatto che la funzione fosse in una dll.
    Grazie a tutti
  • Re: Funzione C++ che restituisce un oggetto stringa (string).

    No, hai fatto bene invece ad avere dei dubbi. Il passaggio di argomenti a/da una DLL è un argomento che non viene sollevato spesso e che rischia, se non compreso bene, di rendere il codice compiler dependent e problematico nella manutenzione.
    Spero comunque che tutti i tuoi dubbia siano stati fugati.
Devi accedere o registrarti per scrivere nel forum
10 risposte