Newsgroup: it.comp.programmare
Titolo: Anomalia del compilatore?

Salve,
Sicuramente conoscerete quegli indovinelli in cui ci sono dei recipienti con 
determinate capacit� di contenimento e tramite versamenti da un recipiente 
all'altro e a svuotamenti, bisogna riuscire a far rimanere un certo 
quantitativo di liquido all'interno di un recipiente.
Esempio: si hanno 2 recipienti, uno con una capacit� di 5 litri e l'altro di 
3 litri. Considerando che non ci sono limiti di acqua e che quindi ogni 
recipiente pu� essere riempito e svuotato quante volte si vuole, in che modo 
si possono far rimanere 4 litri nel recipiente da 5?
Ho scritto  un programma per risolvere rompicapo di questo tipo, ma mi 
succedono delle cose strane.
Uso Windows 7 a 64 bit.
Ho notato che in alcuni casi non funziona, ovvero a parit� di input 
inseriti, a volte funziona altre volte va in crash.
L'ho compilato con l'ultima e la penultima versione di MinGW (porting di gpp 
su Windows) e ho lo stesso problema.
Ma la cosa ancora pi� strana � che usando un altro compilatore (un vecchio 
bcc32 del 2002) ottengo anomalie diverse da quelle ottenute con MinGW.
Posso capire che il bcc32 sia troppo vecchio e poco attendibile, ma 
possibile che dia problemi anche il MinGW? E tra l'altro si verificano solo 
in alcuni casi.
L'assurdit� raggiunge il culmine in certi casi in cui dato un certo input, 
il programma funziona.
Provo a passare altri input e crasha.
Riprovo i precedenti input quelli per cui funzionava ma crasha di nuovo.
Riprovo nuovamente e funziona.
Il tutto provato anche sul PC di un mio amico che ha installato il sistema 
operativo (anche lui Windows 7 a 64 bit) una settimana fa, quindi non credo 
sia un problema di sistema operativo corrotto.
Possibile che ci sia un bug nel compilatore?
O magari da problemi su sistemi a 64 bit?
Per chi volesse provarlo con un altro compilatore, ecco una versione del 
programma in cui non c'� richiesta di input, i dati sono gi� inseriti, e 
l'output deve essere la soluzione dell'esercizio postato precedentemente, 
ovvero:
*** 6 mosse ***
Riempio 1       5(5) 3(0) inf(inf)
Verso 1 in 2    5(2) 3(3) inf(inf)
Svuoto 2        5(2) 3(0) inf(inf)
Verso 1 in 2    5(0) 3(2) inf(inf)
Riempio 1       5(5) 3(2) inf(inf)
Verso 1 in 2    5(4) 3(3) inf(inf)
Dovete solo compilarlo ed eseguirlo. Possibilmente fatelo pi� volte, in modo 
da valutare se il comportamento cambia o meno:
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
vector<vector<int> > Backup;
vector<pair<int, int> > Versamenti;
vector<string> Mosse;
enum {Soluzione = -2, Infinito = -1};
string Int2Str (int i) {
   ostringstream o;
   o << i;
   return o.str();
}
bool Maggiore(vector<int> a, vector<int> b) {
   int i = 0;
   while (i < a.size() && a[i] == b[i])
       i++;
   return i < a.size() && a[i] > b[i];
}
vector<int> DispoR_AdiacDiversi (int n, int k, bool init = false, int pos = 
0) {
   static vector<int> v;
   static vector<int> c;
   static vector<int> Mag;
   if (init) {
       v.clear();
       c.clear();
       Mag.clear();
       for (int i = 0; i < n; i++)
           v.push_back(i);
       for (int i = 0; i < k; i++)
           c.push_back(i % 2);
       Mag.resize(k);
       Mag[0] = -1;
       c[c.size() - 1]--;
   };
   int i = k - 1;
   while ((i >= 0 && c[i] == v[v.size() - 1]) || (i > pos && pos > 0))
       i--;
   c[i]++;
   while (i > 0 && c[i] == c[i - 1]) {
       while (i >= 0 && c[i] == v[v.size() - 1])
           i--;
       c[i]++;
   };
   for (int j = i + 1, h = 0; j < k; j++, h++)
       c[j] = v[h % 2];
   if (Maggiore(c, Mag))
       Mag = c;
   else
       c.resize(0);
   return c;
}
class Recipiente {
   private :
       int cap, cont;
   public :
       Recipiente (int capacita, int contenuto = 0) {
           if (contenuto > capacita)
               contenuto = capacita;
           if (contenuto == Infinito)
               capacita = Infinito;
           cap = capacita;
           cont = contenuto;
       }
       void operator= (Recipiente& c) {
           if (cap != Infinito && c.cap != Infinito) {
               int vuoto = cap - cont;
               cont += c.cont;
               c.cont -= vuoto;
               if (cont > cap)
                   cont = cap;
               if (c.cont < 0)
                   c.cont = 0;
           }
           else
           if (cap != Infinito && c.cap == Infinito) {
               int vuoto = cap - cont;
               cont = cap;
               if (c.cont != Infinito) {
                   c.cont -= vuoto;
                   if (c.cont < 0)
                       c.cont = 0;
               };
           }
           else
           if (cap == Infinito && c.cap != Infinito) {
               if (cont != Infinito)
                   cont += c.cont;
               c.cont = 0;
           };
       }
       int Cap() {
           return cap;
       }
       int Cont() {
           return cont;
       }
       void Svuota() {
           cont = 0;
       }
       string Info() {
           string s = "";
           if (cap == Infinito)
               s = "inf";
           else
               s = Int2Str(cap);
           s += "(";
           if (cont == Infinito)
               s += "inf";
           else
               s += Int2Str(cont);
           s += ")";
           return s;
       }
};
void Salva (vector<Recipiente> r, vector<vector<int> >& v) {
   vector<int> n_upla;
   for (int i = 0; i < r.size(); i++)
       n_upla.push_back(r[i].Cont());
   v.push_back(n_upla);
}
bool Esiste (vector<int> v, vector<vector<int> > b) {
   int con = 0;
   for (int i = 0; i < b.size() - 1; i++) {
       for (int j = 0; j < v.size(); j++) {
           if (v[j] == b[i][j])
               con++;
           if (con == v.size())
               return true;
       };
       con = 0;
   };
   return false;
}
bool Risolto (vector<Recipiente> r, vector<int> vf) {
   bool risolto = true;
   for (int i = 0; i < vf.size(); i++)
       risolto = risolto && r[i].Cont() == vf[i];
   return risolto;
}
int Elabora (vector<Recipiente> r, vector<int> vf, vector<int> v) {
   for (int i = 0; i < v.size(); i++) {
       string stato = "";
       if (v[i] < r.size() * (r.size() - 1)) {
           if ((r[Versamenti[v[i]].first].Cont() < 
r[Versamenti[v[i]].first].Cap() ||
               r[Versamenti[v[i]].first].Cap() == Infinito && 
r[Versamenti[v[i]].first].Cont() >= 0) &&
               r[Versamenti[v[i]].second].Cont() != 0) {
               int cont_second = r[Versamenti[v[i]].second].Cont();
               r[Versamenti[v[i]].first] = r[Versamenti[v[i]].second];
               for (int j = 0; j < r.size(); j++)
                   stato += r[j].Info() + " ";
               if (cont_second > 0)
                   Mosse.push_back("Verso " + 
Int2Str(Versamenti[v[i]].second + 1) + " in " + 
Int2Str(Versamenti[v[i]].first + 1) + "\t" + stato);
               else
                   Mosse.push_back("Riempio " + 
Int2Str(Versamenti[v[i]].first + 1) + "\t" + stato);
           };
       }
       else {
           if (r[r.size() * r.size() - v[i] - 1].Cont() != 0) {
               r[r.size() * r.size() - v[i] - 1].Svuota();
               for (int j = 0; j < r.size(); j++)
                   stato += r[j].Info() + " ";
               Mosse.push_back("Svuoto " + Int2Str(r.size() * r.size() - 
v[i]) + "\t" + stato);
           };
       };
       vector<int> contenuti;
       for (int j = 0; j < r.size(); j++)
           contenuti.push_back(r[j].Cont());
       if (Backup.size() > 0)
           if (Esiste(contenuti, Backup))
               return i;
       Salva(r, Backup);
   };
   if (Risolto(r, vf))
       return Soluzione;
   return 0;
}
int main() {
   int Recip, MinMosse, MaxMosse, capinput, continput, sol;
   bool esci = false;
   vector<Recipiente> R;
   vector<int> VF;
   Recip = 3;
   R.push_back(Recipiente(5, 0));
   R.push_back(Recipiente(3, 0));
   R.push_back(Recipiente(-1, -1));
   Recip = 1;
   VF.push_back(4);
   MinMosse = 1;
   MaxMosse = 6;
   sol = 1;
   cout << "\nStato iniziale capacita'(contenuto)\n" << endl;
   for (int j = 0; j < R.size(); j++)
       cout << (j + 1) << "^ recipiente: " << R[j].Info() << endl;
   cout << endl;
   cout << "Soluzione da trovare: ";
   for (int j = 0; j < VF.size(); j++) {
       if (R[j].Cap() == Infinito)
           cout << "inf";
       else
           cout << R[j].Cap();
       cout << "(" << VF[j] << ") ";
   };
   cout << "\n\n" << endl;
   for (int i = 0; i < R.size(); i++)
       for (int j = 0; j < R.size(); j++)
           if (i != j)
               Versamenti.push_back(pair<int, int>(i, j));
   for (int k = MinMosse; k <= MaxMosse && !esci; k++) {
       cout << "Ricerca della soluzione con " << k << " moss" << (k > 1 ? 
"e" : "a") << "\n" << endl;
       vector<int> v;
       bool init = true;
       int pos = 0;
       do {
           v = DispoR_AdiacDiversi(R.size() * R.size(), k, init, pos);
           init = false;
           Backup.clear();
           Mosse.clear();
           if (v.size() > 0)
               if ((pos = Elabora(R, VF, v)) == Soluzione && Mosse.size() == 
k) {
                   cout << "*** " << k << " mosse ***" << endl;
                   for (int i = 0; i < Mosse.size(); i++)
                       cout << Mosse[i] << endl;
                   cout << endl;
                   esci = true;
                   if (sol == 1)
                       break;
               };
       }
       while (v.size() > 0);
   };
   cout << "Elaborazione terminata" << endl;
   while (cin.get() != '\n');
   return 0;
}
Saluti
 

 da valutare se il comportamento cambia o meno:
"fatelo" intendi il test o la compilazione?

 "fatelo" intendi il test o la compilazione?
La compilazione, per vedere se cambiando compilatore e sistema le cose vanno 
diversamente. 

 MinGW.
Con il compilatore Microsoft da' errore di compilazione, mentre con il 
compilatore Comeau non da' errore di compilazione.
Il compilatore Microsoft sostanzialmente si lamenta che si tenta di 
copiare un oggetto di tipo "vector<Recipiente>" quando non e' definita 
funzione "void Recipiente::operator =(const Recipiente)" ne' funzioni 
convertibili ad essa. In effetti e' definita la funzione "void 
Recipiente::operator =(Recipiente &)", ma non la funzione "void 
Recipiente::operator =(const Recipiente)" ne' la funzione "void 
Recipiente::operator =(const Recipiente &)". La libreria standard 
ammette che o non sia definito nessun "operator =", o che sia definito 
almeno uno degli ultimi due citati, cioe' con l'argomento "const".
Non so chi abbia ragione, nel senso che probabilmente lo standard 
formalmente ammette sintatticamente che sia definito solo la versione di 
"operator =" con operando non "const", tuttavia mi pare che lo standard 
richieda che "vector", per funzionare correttamente, ha bisogno che il 
tipo su cui lavora sia copiabile senza effetti collaterali sull'oggetto 
copiato. E questo non e' il comportamento di Recipiente. Infatti la 
quarta istruzione di "void Recipiente::operator= (Recipiente& c)" e':
   c.cont -= vuoto;
che modifica l'oggetto da copiare.
In conclusione, dovresti o eliminare la funzione "void 
Recipiente::operator= (Recipiente& c)" e fare in modo che non ce ne sia 
bisogno, o sostituirla con una funzione "void Recipiente::operator= 
(const Recipiente& c)" che non modifichi l'oggetto "c".
Comunque, penso che avresti fatto meglio a postare questa domanda su 
it.comp.lang.c++.
-- 
Carlo Milanesi
http://carlomilanesi.wordpress.com/

 Recipiente& c)" che non modifichi l'oggetto "c".
Ma definire una void Copia (Recipiente& c) invece che operator= credo non 
cambi la situazione?
Magari provo a definire quella con const e adattare il codice. 

 Recipiente& c)" che non modifichi l'oggetto "c".
La situazione sembra ancora peggio.
Per provare ho tolto il metodo operator= ed anche la porzione di codice che 
ne fa uso.
Incredibilmente capita che vada in crash lo stesso. 

   };
Speriamo che sia questo il problema.