Autore Topic: [C++] differenze nelle implementazioni dei compilatori  (Letto 2411 volte)

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
[C++] differenze nelle implementazioni dei compilatori
« il: 07 Luglio 2011, 16:55:00 »
Giusto un piccolo "rant" che mi frullava per la testa: le implementazioni attuali di C++ (ho avuto la sfortuna di lavorare su progetti multipiattaforma compilati con 3 compilatori diversi) sono discordanti in alcuni punti, soprattutto hanno differenze nel definire lo scope degli identifier delle enum e dentro i template.

Ad esempio, con le enum ho notato queste differenze di implementazione:

Codice: [Seleziona]
class Prova
{
  public:
  enum Ciao
  {
    UNO,
    DUE
  }
};

Prova::Ciao x = Prova::Ciao::UNO; // ok con MSVC2005, errore con GCC e SNC
Prova::Ciao y = Prova::UNO; // ok con tutti

Prova::Ciao z = x+1; // ok con MSVC2005 e GCC (però da un warning), errore con SNC

Con i template invece ho notato questa differenza tra i compilatori:
Codice: [Seleziona]

template <typename T>
class Cane<T>
{
    protected:
    T mCoda;
};

template <typename T>
class Prova<T> : public Cane<T>
{
    Prova<T>(const T& parametro)
    {
      mCoda = parametro; // Errore su GCC, ok su MSVC2005 e su SNC
      this.mCoda = parametro; // Ok su tutti
    }
};

Su progetti molto grossi, con un'architettura composta da decine di migliaia di classi, ed uso estensivo dei template e delle enum, queste differenze hanno comportato non pochi grattacapi... In particolare lo sviluppo avveniva prettamente su MSVC2005 (target build Windows e XBox360) e se tutto andava a buon fine, si ricompilava per GCC e SNC (target build PS3). Successivamente abbiamo droppato il supporto a GCC, e usato soltanto SNC perchè più veloce a compilare e produce codice più ottimizzato (testato ad "alto livello" tramite un profiler).
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re: [C++] differenze nelle implementazioni dei compilatori
« Risposta #1 il: 07 Luglio 2011, 17:16:58 »
per il discorso delle enum, anch'io preferisco l'implementazione di MSVC.

per i template, invece, dovrebbero valere le stesse regole di scoping che valgono nelle normali classi. In particolare le variabili globali hanno la priorità più bassa in assoluto.

I parametri mascherano le variabili membro (diretti o ereditati), e le variabili membro mascherano le variabili globali.
L'uso del "this" dovrebbe quindi essere implicito, e difatti lo diventa se quello stesso codice lo implemento come classe normale, mentre si incazza se lo implemento come template.

inoltre si incazza solo per i membri ereditati, mentre rende correttamente il this implicito per i membri diretti.

Misteri...  :shock:
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re: [C++] differenze nelle implementazioni dei compilatori
« Risposta #2 il: 07 Luglio 2011, 17:47:48 »
Citazione da: "dsar"
Onestamente mi sembra contortissima questa gestione degli scope

sullo Stroustrup ci sono tipo 100 pagine dedicate solo alla risoluzione degli scope... per evitare problemi mi sono autovietato di usare "using namespace" e uso SEMPRE il full scope di ogni cosa, e buona notte...

sarà più prolisso da scrivere, ma evita qualsiasi bug subdolo dovuto a name clashing...
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re: [C++] differenze nelle implementazioni dei compilatori
« Risposta #3 il: 07 Luglio 2011, 18:51:15 »
Citazione da: "dsar"
Citazione da: "TheKaneB"
Citazione da: "dsar"
Onestamente mi sembra contortissima questa gestione degli scope

sullo Stroustrup ci sono tipo 100 pagine dedicate solo alla risoluzione degli scope... per evitare problemi mi sono autovietato di usare "using namespace" e uso SEMPRE il full scope di ogni cosa, e buona notte...

sarà più prolisso da scrivere, ma evita qualsiasi bug subdolo dovuto a name clashing...

Anche il caso da te detto prima andrebbe messo sotto scope esplicito, non ho capito perché dare alle variabili globali il livello più basso,
In caso di name clashing, infatti, DEVI usare la sintassi
::identificatoreGlobale
per specificare che vuoi la variabile globale e non quella della classe, proprio perchè queste hanno priorità minima.
Citazione

ci sono casi semplici in cui non vale la pena usare le classi. Sarà che ho sempre usato linguaggi OOP con il module as manager (Ada) e non il module as type (come il class del C++).
Per come è fatto il C++, meglio usare SEMPRE le classi, anche per tipi triviali (anche un banale array, ad esempio).
Infatti, se in un secondo momento volessi fare qualcosa di più complesso (ad esempio usare un custom allocator) puoi ridefinire gli operatori di quella classe, cosa che non potresti fare con i normali tipi base.
Spesso è conveniente ridefinire i costruttori per copia e l'operatore di assegnazione, soprattutto quando si ha a che fare con tipi in forma di array/matrici, per gestire meglio la memoria ad esempio se si vuole implementare un meccanismo di copy-on-write, oppure se si vuole implementare il reference counting oppure ancora per implementare dei "weak pointers" (cioè puntatori ad oggetti sacrificabili, magari ricreabili a partire da altri dati, usati soprattutto per implementare sistemi di cache temporanea con garbage collecting).

Tutta roba che sarebbe comodo avere lato linguaggio, e invece con il C++ mi è toccato implementarlo manualmente. Tuttavia forse è anche questo uno dei lati positivi di questo linguaggio, ovviamente contestualizzando la cosa allo sviluppo dei videogames dove bisogna avere un controllo necessariamente manuale delle risorse (per tutta una sfilza di motivi che non sto qua a citare).
Citazione
In ogni caso il discorso è semplice, tu non devi impossibilitare i tuoi client l'uso dei nomi delle variabili che vogliono perché la tua classe li occupa. Questo non vale solo per le variabili globali, ma anche per chi vuole estendere la tua classe.
Per questo hanno introdotto lo "using namespace", così dici al compilatore di privilegiare un namespace al posto di un altro, scavalcando il name scoping automatico. Tuttavia preferisco usare il full scope esplicito proprio per evitare che un programmatore "burlone" faccia porcherie con i nomi dei membri. Chiaramente considera il caso di un progetto sviluppato da 40 persone con livelli di preparazione, diciamo, "eterogenei" :-D Meglio imporgli una regola strict (e bacchettarli quando sgarrano, visto che i commit sul server sono tracciati per username), piuttosto che lasciargli usare tutte le caratteristiche di un linguaggio infernale come il C++...

Citazione
Comunque tornando al discorso dei template e classi, in effetti è molto incoerente

In GCC ho trovato tanti altri bug sui template (almeno fino alla versione 4.2), ad esempio c'era un bug bastardo sui template di classi virtuali pure che non generava errori a compile time, ma creava codice errato che andava a referenziare una virtual table inesistente, generando quindi un errore di null pointer a runtime (fortunatamente era solo un null pointer, ma poteva anche essere memoria random!).
Adesso non ricordo esattamente come replicare il bug, il codice era un po' contorto, però in sostanza bisognava creare una classe template B derivata da una classe template virtuale pura A (sostanzialmente, una Interface in gergo Java), e poi non ricordo in che modo si creava questo bug a runtime quando si chiamava un metodo virtuale di C, che derivava da B implementando l'interfaccia di A.

Forse si scatenava con l'ereditarietà multipla, non ne sono sicuro, visto che comunque usavamo queste classi virtuali pure template alla stessa maniera delle Interfaces del Java, quindi capitava di avere classi del tipo:

Codice: [Seleziona]
class Pilota : public Subscriber<PhysicsEventPublisher>, public Subscriber<NetworkEventPublisher>
{
...
};

per evitare tali problemi con quel bug di GCC, il codice veniva aggiustato in questo modo:
Codice: [Seleziona]
class Pilota
{
private:
  Subscriber<PhysicsEventPubliser> mPhysicsSubscriber;
  Subscriber<NetworkEventPublisher> mNetworkSubscriber;
};
In pratica nel secondo caso, evitando l'ereditarietà multipla, le virtual table venivano generate correttamente da GCC e funzionava tutto.
Ovviamente questo comportava piccole modifiche al codice di gestione degli eventi, ma roba trascurabile.
MSVC invece funzionava correttamente in entrambi i casi...
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] differenze nelle implementazioni dei compilatori
« Risposta #4 il: 07 Luglio 2011, 20:27:53 »
Citazione da: "TheKaneB"
per il discorso delle enum, anch'io preferisco l'implementazione di MSVC.


Illuminatemi: perchè non dovrebbe essere corretto l'esempio che hai riportato con gli enum? Per me (e per le mie poche conoscenze) è "logico", non capisco perche GCC non lo compili...
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] differenze nelle implementazioni dei compilatori
« Risposta #5 il: 07 Luglio 2011, 20:30:40 »
Citazione da: "TheKaneB"
Citazione da: "dsar"
Onestamente mi sembra contortissima questa gestione degli scope

sullo Stroustrup ci sono tipo 100 pagine dedicate solo alla risoluzione degli scope... per evitare problemi mi sono autovietato di usare "using namespace" e uso SEMPRE il full scope di ogni cosa, e buona notte...

sarà più prolisso da scrivere, ma evita qualsiasi bug subdolo dovuto a name clashing...

No aspetta..a me lo GCC mi dà dei warning (o degli errori, ads non ricordo) nel caso di ambiguità...per cui ho continuato a usare "using namespace" e usare il full scope SOLO nel caso di ambiguità...
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] differenze nelle implementazioni dei compilatori
« Risposta #6 il: 07 Luglio 2011, 20:37:41 »
Citazione da: "dsar"
Citazione da: "clros"
Illuminatemi: perchè non dovrebbe essere corretto l'esempio che hai riportato con gli enum? Per me (e per le mie poche conoscenze) è "logico", non capisco perche GCC non lo compili...

Perché lo standard C++ non prevede uno scope per gli enum,[...]

Ok, ads è chiaro...
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] differenze nelle implementazioni dei compilatori
« Risposta #7 il: 07 Luglio 2011, 20:38:32 »
Citazione da: "dsar"
Che version di GCC hai? Considera che molti vendor di hardware e molte distribuzioni usano l'ultima versione GPL-2, ovvero la 4.2.

No, io uso la 4.5.2 sotto Linux Ubuntu...
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re: [C++] differenze nelle implementazioni dei compilatori
« Risposta #8 il: 07 Luglio 2011, 22:54:03 »
Citazione da: "clros"
Citazione da: "TheKaneB"
Citazione da: "dsar"
Onestamente mi sembra contortissima questa gestione degli scope

sullo Stroustrup ci sono tipo 100 pagine dedicate solo alla risoluzione degli scope... per evitare problemi mi sono autovietato di usare "using namespace" e uso SEMPRE il full scope di ogni cosa, e buona notte...

sarà più prolisso da scrivere, ma evita qualsiasi bug subdolo dovuto a name clashing...

No aspetta..a me lo GCC mi dà dei warning (o degli errori, ads non ricordo) nel caso di ambiguità...per cui ho continuato a usare "using namespace" e usare il full scope SOLO nel caso di ambiguità...

Non so essere più preciso perchè vado a memoria, ti parlo di esperimenti fatti in azienda un anno fa. Adesso non ho più la possibilità di rifare i test e valutare tutti i casi. E' probabile che una versione diversa di GCC dia diversi risultati, o magari una particolare combinazione di flag di compilazione. Non so quanto questo possa influire, ma il GCC in questione era la versione cross-compiler per playstation 3, quindi non so nemmeno quali patch abbiano applicato quelli di SCE-DEV (Sony Computer Enterteinment).
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] differenze nelle implementazioni dei compilatori
« Risposta #9 il: 08 Luglio 2011, 08:48:59 »
Citazione da: "TheKaneB"

Non so essere più preciso perchè vado a memoria, ti parlo di esperimenti fatti in azienda un anno fa. Adesso non ho più la possibilità di rifare i test e valutare tutti i casi. E' probabile che una versione diversa di GCC dia diversi risultati, o magari una particolare combinazione di flag di compilazione. Non so quanto questo possa influire, ma il GCC in questione era la versione cross-compiler per playstation 3, quindi non so nemmeno quali patch abbiano applicato quelli di SCE-DEV (Sony Computer Enterteinment).

Anche io sto andando a memoria, ma ricordo abbastanza bene di alcuni warning/errori quando ad esempio, c'era ambiguità tra due funzioni con lo stesso nome e namespace differenti. In quel caso lì mi è bastato mettere il namespace di apparteneza solo davanti al nome di funzione che il compilatore non sapeva bene da quale namespace prendere...
(ecco, se non sbaglio trattavasi della funzione (manipolatore??) std::hex. Ad un certo punto, avendo incluso anche una funzione con lo stesso nome contenuta in boost::filesystem (se non ricordo male) il compilatore si lamentò non sapendo a quale funzione mi riferissi quando scrivevo solo hex()...
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Tags: