Go Down

Topic: Probabile BUG del compilatore ... (Read 7695 times) previous topic - next topic

gpb01

Apr 13, 2014, 01:45 pm Last Edit: Apr 13, 2014, 01:47 pm by gpb01 Reason: 1
Oggi, mentre stavo giocherellando con l'IDE di Arduino, mi sono imbattuto in uno strano errore, fortunatamente, facilmente riproducibile ... ;)

Notare che il BUG è presente sia nella 1.0.5 che nella 1.5.6-r2.

Aprite l'IDE e caricate l'esempio della libreria Wire -> master_reader ...

Se lo compilate così come è vedrete che non c'è alcun errore e la compilazione riesce. Provate ora a renderlo più generico sostituendo l'indirizzo fisso del device I2C, nella  Wire.requestFrom() con una valore costante.

Dichiarate, ad inizio programma, una costante di tipo uint8_t :

Code: [Select]
const uint8_t myAddr = 0x02;

e correggete la riga nel loop() per utilizzare tale valore costante :

Code: [Select]
Wire.requestFrom(myAddr, 6);    // request 6 bytes from slave device #2

ricompilate e ... sorpresa :

Code: [Select]

master_reader.ino: In function 'void loop()':
master_reader.ino:25: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
/Applications/Arduino 1.5.6-r2.app/Contents/Resources/Java/hardware/arduino/avr/libraries/Wire/Wire.h:58: note: candidate 1: uint8_t TwoWire::requestFrom(int, int)
/Applications/Arduino 1.5.6-r2.app/Contents/Resources/Java/hardware/arduino/avr/libraries/Wire/Wire.h:56: note: candidate 2: uint8_t TwoWire::requestFrom(uint8_t, uint8_t)


... un errore a cui non trovo alcuna giustificazione, anche perché, se andate a vedere dentro la libreria Wire, scoprite che il metodo requestFrom() è così definito :

Code: [Select]
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)

La cosa più assurda è che vedrete scomparire l'errore (... ma non so se poi il tutto funzioni correttamente) facendo un casting sbagliato ! Difatti, se mettete :

Code: [Select]
Wire.requestFrom((uint16_t)myAddr, 6);    // request 6 bytes from slave device #2

la compilazione va a buon fine ...  :smiley-eek: :smiley-eek: :smiley-eek:


L'unico modo, ovviamente, per parametrizzare la cosa è NON usare una costante (... metodo che invece è sempre consigliabile), ma una #define. Difatti, se invece del const, mettete :

Code: [Select]
#define myAddr 0x02

... il valore viene semplicemente sostituito e tutto viene compilato correttamente ...   :smiley-mr-green: :smiley-mr-green: :smiley-mr-green:


A rafforzare l'dea di un BUG nel compilatore presente nell'IDE di Arduino e il fatto che ... se si sostituise la Toolchain con la nuova (Toolchain 3.4.3, avr gcc 4.8.1) ... l'errore scompare ...  ]:D ]:D ]:D

Guglielmo
Search is Your friend ... or I am Your enemy !

PaoloP

#1
Apr 13, 2014, 02:09 pm Last Edit: Apr 13, 2014, 02:20 pm by PaoloP Reason: 1
Allora la soluzione sarà presto disponibile con la 1.5.7. Per tua gioia.  :smiley-mr-green: :smiley-mr-green:

Comunque a me il warning del casting lo da ugualmente anche con la TC 3.4.3.

Impostando
Code: [Select]
byte address  = 2;
ottengo
Code: [Select]
In file included from master_reader.ino:15:0:
D:\arduino-1.5.5-TC343\hardware\arduino\avr\libraries\Wire/Wire.h: In function 'void loop()':
D:\arduino-1.5.5-TC343\hardware\arduino\avr\libraries\Wire/Wire.h:58:13: note: candidate 1: uint8_t TwoWire::requestFrom(int, int)
    uint8_t requestFrom(int, int);
            ^
D:\arduino-1.5.5-TC343\hardware\arduino\avr\libraries\Wire/Wire.h:56:13: note: candidate 2: uint8_t TwoWire::requestFrom(uint8_t, uint8_t)
    uint8_t requestFrom(uint8_t, uint8_t);
            ^

Lo sketch usa 6.030 byte (21%) dello spazio disponibile per i programmi. Il massimo è 28.672 byte.
Le variabili globali usano 360 byte (14%) di memoria dinamica, lasciando 2.200 byte liberi per le variabili locali. Il massimo è 2.560 byte.


Anche con const byte naturalmente.
Mentre char e const char non ottengo nessun avviso.
Questo perché nella libreria è definito in ingresso uint8_t e non int8_t che corrisponde a char e non a byte (che è più usato).
Il compilatore non fa casting automatico e non trova corrispondenza con la libreria.
Non so se è voluto o è un errore; io propendo per il fatto che sia un comportamento voluto.
Col casting automatico se sbagli a passare un valore ad una funzione con overload potresti involontariamente eseguire una funzione diversa da quella voluta. Bene fa il compilatore a segnalarti l'errore.
E' il programmatore che deve conoscere il formato richiesto in ingresso dalle librerie.  :smiley-mr-green:

Cosa ne pensi?

gpb01

... ne penso ... che non ho capito il tuo discorso ...  :smiley-mr-green: :smiley-mr-green: :smiley-mr-green:

La funzione è definita come :

Code: [Select]
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)

quindi si aspetta degli uint8_t !!!

Se tu definisci una costante di tipo uint8_t e compili con l'IDE standard ... il compilatore segnala ERRORE e non va avanti, se compili con la nuova toolchain (gcc 4.8.1), il compilatore segnala WARNING e va avanti compilando il codice.

Tolte quindi tutte le altre "chiacchiere" ... c'è evidentemente un BUG nel vecchio compilatore ... ed ho anche capito da cosa è dovuto ...  XD

Basta difatti aggiungere un cast (uint8_t) anche al secondo parametro e tutto va a posto ... indovina un po' perché ?  :smiley-mr-green: :smiley-mr-green: :smiley-mr-green:

Guglielmo
Search is Your friend ... or I am Your enemy !

testato

- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

nid69ita


Tolte quindi tutte le altre "chiacchiere" ... c'è evidentemente un BUG nel vecchio compilatore ... ed ho anche capito da cosa è dovuto ...  XD
Basta difatti aggiungere un cast (uint8_t) anche al secondo parametro e tutto va a posto ... indovina un po' perché ?  :smiley-mr-green: :smiley-mr-green: :smiley-mr-green:
Guglielmo


E no, il quiz della domenica. Quanto tempo abbiamo per trovare la risposta?     :smiley-mr-green:
(Dai, su, diccelo, son curioso come un gatto) 
my name is IGOR, not AIGOR

gpb01


E no, il quiz della domenica. Quanto tempo abbiamo per trovare la risposta?     :smiley-mr-green:
(Dai, su, diccelo, son curioso come un gatto) 


Dai nid che è facile ... guarda i messaggi di errore/warning e t'accorgi che ... è un problema di "overloding" ... non previsto :

Code: [Select]
candidate 1: uint8_t TwoWire::requestFrom(int, int)
candidate 2: uint8_t TwoWire::requestFrom(uint8_t, uint8_t)


... ma non considera un "cadidate 3" e "4" con le altre due possibili combinazioni :smiley-mr-green: :smiley-mr-green: :smiley-mr-green:

Guglielmo
Search is Your friend ... or I am Your enemy !

nid69ita

#6
Apr 13, 2014, 05:48 pm Last Edit: Apr 13, 2014, 05:59 pm by nid69ita Reason: 1
Ma perchè con il primo parametro a cui passi un const uint_8 cercherebbe:
Code: [Select]
uint8_t TwoWire::requestFrom(const uint8_t, uint8_t)
e siccome non lo trova ha in alternativa gli altri due e nel dubbio si pianta ?  
Nella nuova toolchain han cambiato qualcosa nel compilatore per questi casi allora.

Oppure perchè il primo (tralasciando const) è uint8_t e il secondo è 6 e quindi viene intepretato come int, perciò
Code: [Select]
uint8_t TwoWire::requestFrom(uint8_t, int)  
non è definito ?

Per assurdo se è nel secondo caso allora con const e cast sul secondo dovrebbe compilare senza warning ?!?
Code: [Select]
Wire.requestFrom(myAddr, (uint8_t)6);
my name is IGOR, not AIGOR

gpb01

Sicuramente SI ...

... il vecchio avr gcc si pianta con un bel ERRORE, il nuovo ha almeno il buon gusto di dare un WARNING (... che comunque è in buona compagnia con tutti gli altri che genera il core di Arduino) e di andare avanti regolarmente con la compilazione ;)

Guglielmo
Search is Your friend ... or I am Your enemy !

nid69ita

#8
Apr 13, 2014, 06:11 pm Last Edit: Apr 13, 2014, 06:21 pm by nid69ita Reason: 1
Confermo che così compila anche con IDE 1.0.5
Code: [Select]
const uint8_t MyAddr=22;
void loop()
{ Wire.requestFrom(MyAddr,(uint8_t)6);


Quindi non è il const di MyAddr a dargli fastidio, ma considera il secondo parametro come int.

Però forse non è meglio l'errore della versione vecchia?  Con la nuova compila, ma quale delle due sceglierà?
la int,int o la uint8_t,uint8_t  ??? 
my name is IGOR, not AIGOR

gpb01

... NO, è meglio che vada avanti, visto che comunque poi all'interno la funzione tratta quel valore come un uint8_t e trasmette solo 7 bit di address (più uno di R/W) ... prenderà il byte meno significativo dell'int e lo userà :)

La cosa giusta, in mancanza del corretto overload, è il warning che comunque ti avvisa ;)

Guglielmo
Search is Your friend ... or I am Your enemy !

PaoloP


Con la nuova compila, ma quale delle due sceglierà?



... NO, è meglio che vada avanti, visto che comunque poi all'interno la funzione tratta quel valore come un uint8_t e trasmette solo 7 bit di address (più uno di R/W) ... prenderà il byte meno significativo dell'int e lo userà :)

La cosa giusta, in mancanza del corretto overload, è il warning che comunque ti avvisa ;)


Non sono d'accordo perché non sai quale funzione userà. E se in base al parametro decido di fare cose diverse?
A quel punto non so come si comporterebbe il mio programma.

gpb01


Non sono d'accordo perché non sai quale funzione userà. E se in base al parametro decido di fare cose diverse?
A quel punto non so come si comporterebbe il mio programma.


Paolo di cosa parli ???  :smiley-eek:

Chi non sai che funzione userà ?

Stiamo parlando di un metodo, il metodo requestFrom(), della classe Wire e si sa esattamente cosa farà (... deve richiede N bytes al device indirizzato dai 7 bit di address) e come si comporterà il tuo programma ...

Guglielmo
Search is Your friend ... or I am Your enemy !

testato

i vari candidati, candidate 1, e 2 fanno parte del codice della libreria o del compilatore ?
se sono una cosa scritta in libreria basta aggiornare la libreria alle 4 possibili varianti
- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

gpb01

Nel .cpp della libreria troviamo :

Code: [Select]

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
{
  // clamp to buffer length
  if(quantity > BUFFER_LENGTH){
    quantity = BUFFER_LENGTH;
  }
  // perform blocking read into buffer
  uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop);
  // set rx buffer iterator vars
  rxBufferIndex = 0;
  rxBufferLength = read;

  return read;
}

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity)
{
  return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}

uint8_t TwoWire::requestFrom(int address, int quantity)
{
  return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}

uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop)
{
  return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop);
}


... ovvero due casi per due parametri e due casi per tre parametri, mancano i casi incrociati (unit8_t, int) e (int, uint8_t), quindi, volendo è facile aggiungerli.

E' anche chiaramente visibile che, comunque vengano scelti, alla fine sempre a uint8_t vengono portati, qualunque caso si sceglie (... e ci mancherebbe altro). :)


Comunque, lo scopo del thread era un altro ... mostrare il comportamento di una versione di gcc rispetto alla versione successiva e mettere quindi in guardia sul tipo di problematica.  :smiley-roll:

Nel caso della libreria specifica ... senza impazzire con tante modifiche ... basta un banale cast  :smiley-mr-green:

Guglielmo


Search is Your friend ... or I am Your enemy !

nid69ita


Comunque, lo scopo del thread era un altro ... mostrare il comportamento di una versione di gcc rispetto alla versione successiva e mettere quindi in guardia sul tipo di problematica.  :smiley-roll:

Chiaro. Mi domando cosa e/o come hanno modificato il comportamento del compilatore in questi casi che una certa ambiguità la creano.
my name is IGOR, not AIGOR

Go Up