[ Nedeljko @ 16.09.2008. 12:28 ] @
Zašto C++ nema podrazumevanu konverziju iz tipa T*& u const T*&, već moram ručno da vršim konverziju? Primer:

Code:

#include <iostream>

using namespace std;

void f(const int *&x) {
    cout << *x << endl;
}

int main() {
    const int *a = new int(5);
    int *b = new int;
    
    f(a);
    *b = 3;
//    f(b);                   Greska!
    f((const int*&) b); //  OK
    
    return 0;
}
[ Branimir Maksimovic @ 16.09.2008. 13:09 ] @
Zato sto kad stavis const a imas i * i &, kompajler podrazumeva referencu na pointer na konstantni int ;)
Kako je iz konverzije T* u const T* rezultat temporary, referenca se na to ne moze
bindovati pa kompajler prikazuje gresku.

recimo ako umesto funkcije stavis ovaj template, proci ce ti bez greske
zato sto tada kompajler zna da se radi o const referenci:

Code:

template <typename T>
void f(const T& x) {
    cout << *x << endl;
    }
// sintaksu za const pointer, const referencu mozes i preko typedefa da resis.


Ovo je kako je pisac kompajlera razmisljao.
E sad kako bi trebalo da radi po standardu, i da li je to po standardu ostavljam
nekome drugom ;)

Pozzz!


[Ovu poruku je menjao Branimir Maksimovic dana 16.09.2008. u 14:22 GMT+1]
[ yaelo @ 16.09.2008. 13:10 ] @
const int*& - to ti je referenca na tip podataka, koji je u ovom slucaju pointer na const int variablu, sto nije isto kao referenca na pointer na int variablu. Moze da se predaje int* kao parametar funkciji koja prima int const*.
[ Nedeljko @ 16.09.2008. 16:16 ] @
Kada je parametar tipa T*&, to znači da mogu da menjam ne samo vrednost objekta na koji se pokazuje, nego i sam pokazivač na njega. Sa druge strane, kada je parametar tipa const T*&, to znači da sam objekat na koji se pokazuje funkcija ne menja, ali da sam pokazivač može. Na primer:

Code:

void obrisi(const int *&x) {
    if (x != 0) {
        delete x;
        x = 0;
    }
}

void razdvoj(int *&x, int *&y) {
    if (&x == &y) {
        throw("Razdvajanje nije moguce");
    }
    if (x == y) {
        if (y != 0) {
            y = new int;
            *y = *x;
        }
        return;
    }
    if (x == 0) {
        x = new int;
        *x = *y;
        return;
    }
    if (y == 0) {
        y = new int;
        *y = *x;
    }
}


Primeri nemaju previše praktičnog smisla, služe samo kao ilustracija. Kada definišem pomenljivu tipa T*, ona pokazuje na objekat tipa T, koji se može menjati. Ako ja imam objekat kome dozvolim menjanje, a funkcija garantuje da ga neće menjati, u čemu je problem?

Ne bih rekao da prilikom konverzije bilo kog tipa A u bilo koji tip B, pri čemu su oba reference na nešto ima bilo kakvih privremenih objekata.
[ Branimir Maksimovic @ 16.09.2008. 18:31 ] @
Prvo, ne postoji konverzija iz tipa reference u tip reference, zato sto su one aliasi za odredjeni tip.
Dakle kad bacis funkciji referencu na neki objekat u stvari to je isto kao da si bacio sam objekat.
Drugo, ne bacas referencu na objekat, nego sam objekat u tvom primeru, ali sa obzirom na prvo sto rekoh,
to ti dodje na isto.
Trece, rezultat svake konverzije ti je temp objekat. Naravno ne kreira kompjaler to stvarno,
osim ako nije neophodno.
No da ne duzim pricu, probaj sledece:

Code:

#include <iostream>

using namespace std;

typedef const int* T;
void f(const T& x) {
    cout << *x << endl;
    }

int main() {
  T a = new int(5);
// T& a = new int(5) , non const referenca ne moze na temp
  int* b = new int;      
  const T& c = new int(6); // const referenca moze na temp
                
  f(a); // nema konverzije
  *b = 3;
  f(b); // konverzija (int*) -> (const int*), 
  // ali temp ide na const referencu pa se kompajler ne buni
  return 0;
}


Pozdrav!
[ Nedeljko @ 17.09.2008. 09:07 ] @
Code:

#include <iostream>

using namespace std;

void postavi_na_nulu(const int *&x)
{
    x = 0;
}

int main()
{
    int x, *y = &x;
    postavi_na_nulu((const int*&) y); // bez konverzije je greska

    if (y==0)
        cout << "Nula" << endl;

    return 0;
}


Privremeni objekat o kome pricas moze biti referenca (to jest adresa) promenljive tipa int*, to jest, na masinskom jeziku je ekvivalentan sa int**, a ne sa int* (znam da masinski jezik ne zna za tipove). No, kao sto i sam kazes, temp moze na const&, pa zasto onda u ovom primeru ne moze bez konverzije, jer se svakako ide na const&?

Razlika izmedju mog i tvog primer je u tome sto deklaracija parametra sa int*& garantuje da se nece menjati objekat tipa int na koji pokazuje preneti pokazivac, a sam pokazivac moze da se preusmeri na drugu adrtesu, a kod tebe se garantuje da su i objekat na koji se pokazuje i sam pokazivac nepromenljivi unutar funkcije. To nije isto.
[ Branimir Maksimovic @ 17.09.2008. 16:07 ] @
Masinski jezik zna za tipove. Privremeni objekat ne moze biti c++ referenca. No moze biti tipa int**
no kakve sad to ima veze sa ovim?
U tvom primeru ne ide na const& i bas zato imas gresku dva puta sam vec objasnio.

Code:

postavi_na_nulu((const int*&) y); // bez konverzije nije greska


ovde ne dolazi do konverzije vec jednostavno forsirano dodajes const na int* tip.
Problem je sto po defaultu dolazi do konverzije a kad kastujes u referencu na const int*, ne dolazi
do konverzije. U stvari c++ ima 4 vrste kastova, c style cast jednostavno leze na jedan od
ta cetiri, sto ce reci u zavisnosti od slucaja cast povlaci konverziju ili reinterpretaciju.

Medjutim,

Code:


postavi_na_nulu((const int*) y); // sa konverzijom je greska i ovo se desava po defaultu
// pa i da ne stavis cast operator



Mislim da tebe buni sto ne dolazi do inicijalizacije iz int* u const int*& po defaultu
nego moras da kastujes.
Pa zato sto na taj nacin mozes da modifikujes const objekte bez kastovanja

Na pr:

Code:


int main()
{
  const int a = 5;
  int *b;
//  const int*& c = b; ne moze jer to je posredno bypassovanje const korektnosti
  const int*&c = const_cast<const int*&>(b); // opasno, ali bar se vidi kao upozorenje
  c = &a; // ne valja, sada b pokazuje na const objekat
  *b = 42; // undefined behavior, bum tras
}




Jel sad jasno?

Pozdrav!

[ Nedeljko @ 19.09.2008. 11:19 ] @
Mašinski jezik ne zna za tipove, ali nema veze, nije to ovde tema.

Napisao si

Code:

postavi_na_nulu((const int*&) y); // bez konverzije nije greska


Evo, kompajliram bez konverzije i dobijam grešku. Nemoj da se ljutiš, ali zamolio bih te da proveriš kod pre nego što ga okačiš. Ako ne dobiješ grešku, reci kojim kompajlerom kompajliraš kod. Ja korictim g++ (trenutno sam pod Linux-om).
[ Branimir Maksimovic @ 19.09.2008. 16:51 ] @
Masinski jezik ima tipove, imas ono dword qword word byte i slicno. I to leze na masinske
instrukcije koje operisu sa tim tipovima. Imas signed i unsigned operacije te operacije koje
rade sa stringovima. Potom masinski ima i float double i long double tipove
Ovde mislim na masinski za x86.

Kada castujes iz (int*) -> (const int*&) tada nema konverzije. To sam napisao
i vidi se iz citata.
Jel ti to mene uopste ne razumes?
E sad da se razumemo po c++ standardu imas dve vrste konverzija.
Jedna je kada konerzija za rezultat daje rvalue a druga lvalue.
Kada kastujes u referencu tada je rezultat konverzije lvalue, a to ja ne
zovem konverzijom vec reinterpretacijom.
I sta si kompajlirao posto iz onog citata definitivno nisi mogao
bilo sta da kompajliras jer to je jedna linija iz tvog koda sa izmenom komentara?

Pozdrav!

edit: cemu ova diskusija? objasnio sam ti sto ne mozes inicijalizovati referencu
na const int* iz pointera na int i to je to.




[Ovu poruku je menjao Branimir Maksimovic dana 19.09.2008. u 18:07 GMT+1]
[ Nedeljko @ 26.09.2008. 08:42 ] @
Citat:
Branimir Maksimovic: I sta si kompajlirao posto iz onog citata definitivno nisi mogao
bilo sta da kompajliras jer to je jedna linija iz tvog koda sa izmenom komentara?


Kompajlirao sam onaj program koji sam priložio sa jedinom izmenom što je izbačena konverzija. To se ne kompajlira na g++/Linux kombinaciji. Ako si taj kod kompajlirao uspešno nečim drugim, onda bi mogao da napišeš i čime si ispeo da kompajliraš ovaj program:

Code:
#include <iostream>

using namespace std;

void postavi_na_nulu(const int *&x)
{
    x = 0;
}

int main()
{
    int x, *y = &x;
    postavi_na_nulu(y);

    if (y==0)
        cout << "Nula" << endl;

    return 0;
}


Mašinski jezik je jezik nula i jedinica, a ono o čemu si pričao je asembler. No, na mašinskom jeziku postoje operacije nad sadržajima datih adresa, tako da nad sadržajem na istoj adresi možeš da vršiš sve operacije. Komp te niučemu ne sprečava. Kad na C-u napišeš struct {int x,y;} t = 2.7; dobićeš poruku o grešci. Znam da i u C-u možeš tipove na silu da konvertuješ, kao i da koristiš unije, ali nekakva kontrola tipova postoji - u mešinskom jeziku ne.
[ Branimir Maksimovic @ 26.09.2008. 15:57 ] @
Jel ti mene zezas?
Izmenio sam komentar, rekoh objasnio n puta o cemu se radi? Nisam rekao da bez casta radi
odakle ti to i sto mi to inputiras. REKAO SAM DA SA CASTOM U REFERENCU NEMA KONVERZIJE i izmenio komentar
a ne da ce bez casta da kompajlira. I sto je sad to bitno ;) ?
Sa konverzijom tj kad ne castujes u referencu dolazi do greske, to sam napisao.

Sto se masinskog tice, preporucio bih ti da procitas Intel® 64 and IA-32 Architectures
Software Developer’s Manual

tu ces naci zanimljive stvari kao sto su:

CHAPTER 4
DATA TYPES
4.1 FUNDAMENTAL DATA TYPES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-1
4.1.1 Alignment of Words, Doublewords, Quadwords, and Double Quadwords . . . . . . . . . . . . 4-2
4.2 NUMERIC DATA TYPES. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-3
4.2.1 Integers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-4
4.2.1.1 Unsigned Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-4
4.2.1.2 Signed Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-4
4.2.2 Floating-Point Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-5
4.3 POINTER DATA TYPES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-8
4.3.1 Pointer Data Types in 64-Bit Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-8
4.4 BIT FIELD DATA TYPE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-9
4.5 STRING DATA TYPES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-9
4.6 PACKED SIMD DATA TYPES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-10
4.6.1 64-Bit SIMD Packed Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-10
4.6.2 128-Bit Packed SIMD Data Types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4-11
4.7 BCD AND PACKED BCD INTEGERS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-12
4.8 REAL NUMBERS AND FLOATING-POINT FORMATS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-13

a kao direktni demant ovo mi se svidja jer kao da je intelov pisac hteo direktno da te demantuje
ovom recenicom ;)

8.2 X87 FPU DATA TYPES
The x87 FPU recognizes and operates on the following seven data types (see Figures
8-13): single-precision floating point, double-precision floating point, double
extended-precision floating point, signed word integer, signed doubleword integer,
signed quadword integer, and packed BCD decimal integers.

Ovde se opsirno govori o tipovima podataka koje podrzavaju *intelovi procesori*.
Asembler je isto sto i masinski, tj obicno 1 asembler mnemonik mapira na 1 masinsku instrukciju.
Naravno makro asembler kombinuje vise instrukcija u jednu a imas i hla, Randy Hide-ov
asembler koji sa sintaksom imitira vise jezike.
No kakve sad veze ima asembler sa ovim o cemu pricamo ;)
Asembler i ne moze da radi sa tipovima podataka koje cpu ne podrzava inace bi bio visi jezik ;)

Pozdrav!

edit: naravno procesor ne radi type checking ili si i to ocekivao ;)
ali to nema veze sa tvojom izjavom da masinski ne zna za tipove podataka ;)

edit2: u stvari masinski je slican dinamicki tipovanim jezicima gde se u slucaju da dodje do
greske baca exception u run time-u radje nego u compile time-u kod staticki tipovanih jezika

[Ovu poruku je menjao Branimir Maksimovic dana 26.09.2008. u 17:34 GMT+1]

[Ovu poruku je menjao Branimir Maksimovic dana 26.09.2008. u 20:16 GMT+1]
[ Goran Arandjelovic @ 28.09.2008. 17:40 ] @
Nedeljko, biće ti najlakše, kao što ti reče Branimir, da (za početak) typedef-uješ, pa da raščlaniš jedno po jedno...
Pazi sledeću stvar, možda će biti jasnije:

Code:

#include <iostream>
using namespace std;

int main()
{
  int x = 5;

  int *a = &x; // pokazivač na int
  const int *b = &x; // nekonstantni pokazivač na konstantni int
  const int * const c = &x; /* konstantni pokazivač na konstantni int (ne možeš da menjaš ni
                                     [b]vrednost[/b] pokazivača) */
}



U tvom primeru iz prve poruke, argument funkcije f je: nekonstantna referenca na (const int*)
Međutim, sa f(b) desila se konverzija (int*)->(const int*), dobio si r-vrednost i potrebna ti je konstantna referenca.

Dakle, bez typedef-ova, deklaracija f-je f bi trebalo da bude

void f(const int * const &x);

Sve je ovo manje-više Branimir pomenuo.

Inače, ne znam koju verziju g++-a koristiš, ali bi trebalo bi da počevši od v4.3 imaš rvalue reference.

Pozdrav.
[ Branimir Maksimovic @ 28.09.2008. 21:06 ] @
Gorane hvala na podrsci, medjutim nemoj Nedeljka da mislidujes sa rvalue referencama ;)


Da pocnem, razlog zbog cega je zabranjeno bindovanje referenca na rvalue, odnosno temporary objekte
je ustvari ovakav kod:

Code:

    void f(int& a) {a++;}
     
    void g()
    {
        double b = 1;
        f(b);
    }



Stvar je u tome da su prvi c++ kompajleri ovo dozvoljavali.
Rezultat je bilo gomila bagova jer niko nije kapirao da se desava konverzija koja za rezultat
ima tmp, i ono sto se menja je tmp objekat a ne sam parametar. To je Bjarne objasnio
u svojoj knjizi Design and Evolution of C++.
Medjutim danas se pojavila potreba za tako necim jer u slucaju da referenca nije output
parametar, vec ukoliko postoji potreba da se petlja sa tmp objektom u samoj funkciji,
(ono sto zovu "move semantics") onda je sasvim legalno imati referencu na rvalue.

I to je glavni razlog sto se uvela nova sintaksa u jezik?

E sad to u gcc-u jel nesto eksperimentalno ili su vec teorijski zaokruzili i izbacili
novu verziju jezika, odnosno standarda za jezik?

Pozdrav!

[ Goran Arandjelovic @ 28.09.2008. 23:07 ] @
Ono što treba Nedeljku je već napisano.

----

Da, generalno se javila potreba za novom sintaksom da bi postojala podrška (move semantika) i za primitivne tipove, dok je kod korisnički definisanih tipova moguće svaki put implementirati Move konstruktor, ali je i to poprilično dosadno (eleganto, kratko, ali dosadno :))

Neke stvari iz novog standarda su implementirane (u GCC-u recimo postoje rvalue reference), ali ih ljudi ne koriste previše zato što neke njima ortogonalne još uvek nisu implementirane...

Što se tiče povratnih vrednosti...

Code:

const T& f()
{
  T obj;
  return(obj); //ups!!!
}


Code:

T f()
{
  T obj;
  return(obj); //ovo je ok
}


Code:

...
T x = f(); //ok, x možemo da menjamo, ali smo jedanput više bespotrebno kopirali objekat (*)
const T &y = f(); //jednom manje kopiran objekat, ali je read-only
T &&z = f(); //rvalue referenca :)
...


(*) Teorijski, to jeste tako, praktično nije, zato što se vrši NRVO optimizacija.

U principu, pojava rvalue referenci je najviše možda posledica potrebe za perfect forwarding-om, ali kako nisu svi kompajleri implementirali variadic templejte, to i ne postoji za sada preterana potreba za njima.
[ Branimir Maksimovic @ 28.09.2008. 23:58 ] @
Imam samo dve primedbe.

Prvo zadnji vazeci standard je revizija onog iz 98. tj ovaj iz 2003.

Ono sto ce se zvati c++0x ce biti uradjeno sledece godine i zvace se valjda c++09.
Za sada je to na nivou predloga koji ce mozda biti prihvaceni a mozda i nece.

Drugo,

Code:

T f()
{
  T obj;
  return(obj); //ovo je ok
}
.....
const T &y = f(); //jednom manje kopiran objekat, ali je read-only
.....


ovo je ok zato sto postoji specijalno pravilo da temp bindovan na const referencu zivi dok
zivi sama referenca inace zivi do kraja izraza ili funkcije.
medjutim,
Code:

T &&z = f(); //rvalue referenca :)


za ovaj slucaj nisam siguran. u predlogu za prosirenje jezika sa rvalue referencama (koji sam video ),
nigde ne stoji zahtev da se jezik izmeni u tom smislu. U stvari vec vidim gomilu problema
sa zivotom tmp objekata bindovanih na rvalue reference.
No o tome cemo sledece godine kad izadje bar draft verzija papira ;)


Pozdrav!
[ Goran Arandjelovic @ 29.09.2008. 00:58 ] @
Slažem se, ali recimo, diskusije radi, zašto bi bilo šta bilo loše što se tiče života tmp objekta? Pojasni, na šta misliš?
[ Branimir Maksimovic @ 29.09.2008. 02:15 ] @
Mislim na gomilu slucaja koji ce morati da se pokriju ukoliko bude potrebno vezivati
zivot tmp objekta za zivot rvalue reference. Hocu da kazem,
da ce recimo T&& f(T&& p){ return p; } biti veoma cest bug zbog analogije sa T& f(T& p){return p;}.
Drugo kako ce tu sta da se rezolvuje pojma nemam ;) no imamo g++ 4.3 pa se moze videti.
Trece, sam predlog uopste ne razmatra zivot tmp-a u odnosu na rvalue referencu,
no opet ko proba kako je implementirano sa gcc -om moze videti sta tu moze da se ocekuje.
Mislim da bi se ozbiljno diskutovalo o ovome potrebno je dosta vremena ;)

Pozdrav!



[ Goran Arandjelovic @ 29.09.2008. 02:26 ] @
Da, ono što si napomenuo može biti bug, ali šta ćeš, ljudi će se navići da ne greše. Razlika u sintaksi je jasnija.

Što se života tmp objekta tiče, čini mi se da je on uvek lokalan (zapravo, skoro 100% sam siguran, napisaću primer ako bude trebalo...sada je malo kasno...), tako da treba da živi u scope-u u kom je kreiran, a da se posle toga briše. To je to.
[ Nedeljko @ 29.09.2008. 13:38 ] @
Javiću se još jednom, čisto da bih objasnio da nikoga nisam zezao. Ako neko smatra da ga zezam, ne mora da se nervira i odgovara ili može da mi napiše da sam budala, ne ljutim se, nema veze. U nastavku ovog posta ću pisati o tome šta me je zanimalo, a u sledećem postu kakve sam odgovore dobio i kako sam reagovao (čisto da bi oni koji ne žele da čitaju jedno od to dvoje mogli lakše da preskoče onaj deo koji ih ne zanima).

Inače, sav kod koji ovde okačim je testiran g++ kompajlerom (gcc version 4.2.2) pod Linux sistemom. Ako se nekome rezultati ne poklope sa onim što napišem (u smislu komnpajliranja / nekompajliranja i / ili pucanja / nepucanja i / ili drugačijih rezultata) molim da napiše čime kompajlira kod. Ako neko bude želeo da odgovara, molim da testira kod koji kači, a bilo bi lepo i da napiše čime kompajlira.

Jano je meni da neke stvari ne mogu da radim, jer me kompajler grdi. Ja svoje prktične probleme uspešno rešavam i vrlo dobro znam šta su lvrednosti, šta dvrednosti i šta su privremeni objekti.

Međutim, ono što je mene zanimalo je zašto kreatori jezika / pisci kompajlera nisu razmišljali na sledeći način: da postoji automatska konverzija iz int*& u const int*&, tako što se adresa pokazivačke promenljive baca na stek, jer konceptualno (sa stanovišta projektovanja jezika) ne bi trebalo da bude problema.

Ja se ne bih bunio da int objekat koji šaljem Peri po referenci (u stvari, šaljem pokazivač na int po referenci) bude promenjen, ali mi Pera obeća da ga neće menjati (onaj int na koji se pokazuje), ja mu kažem, sve i da ga promeni, meni to nije problem itd. Zbog čega je meni opasno da Peri dam takvu pošiljku? Sa stanovišta implementacije, samo se šible adresa pokazivača na stek i gotovo.

Kod nepokazivačkih tipova ovo baš tako i radi. Sledeći program radi ispravno

Code:
#include <iostream>

using namespace std;

void ispisi(const int &x)
{
    cout << x << endl;
}

int main()
{
    int a = 5;
    ispisi(a);
    
    return 0;
}


Kod pokazivačkih tipova situacija nije ista, jer const int* znači da se int na koji se pokazuje neće menjati, a da sam pokazivač može dobiti neku drugu vrednost (recimo, 0 ili da bude preusmeren na neki drugi int). No, iz sličnih razloga ne vidim smisao uvođenja zabrane zbog koje sledeći program ne može da se kompajlira

Code:
#include <iostream>

using namespace std;

void ispisi(const int *&x)
{
    if (x==0)
    cout << "NULL" << endl;
    else
        cout << *x << endl;
}

int main()
{
    int *a = new int(5);
    ispisi(a);
    
    return 0;
}

p.cpp: In function ‘int main()’:
p.cpp:16: error: invalid initialization of reference of type ‘const int*&’ from expression of type ‘int*’
p.cpp:5: error: in passing argument 1 of ‘void ispisi(const int*&)’


Sa druge strane, sledeći program radi

Code:
#include <iostream>

using namespace std;

void ispisi(const int *&x)
{
    if (x==0)
    cout << "NULL" << endl;
    else
        cout << *x << endl;
}

int main()
{
    int *a = new int(5);
    ispisi((const int*&) a);
    
    return 0;
}


No, posle je Branimir napisao za liniju koda koji proizvodi grešku (na mom kompajleru) da ne proizvodi grešku. Naravno, to je bila linija koda iz programa koji sam okačio, pa se podrazumevao taj kontekst.
[ Nedeljko @ 29.09.2008. 13:44 ] @
Citat:
Branimir Maksimovic: No da ne duzim pricu, probaj sledece:

Code:

#include <iostream>

using namespace std;

typedef const int* T;
void f(const T& x) {
    cout << *x << endl;
    }

int main() {
  T a = new int(5);
// T& a = new int(5) , non const referenca ne moze na temp
  int* b = new int;      
  const T& c = new int(6); // const referenca moze na temp
                
  f(a); // nema konverzije
  *b = 3;
  f(b); // konverzija (int*) -> (const int*), 
  // ali temp ide na const referencu pa se kompajler ne buni
  return 0;
}



E, ad. Na to sam odgovorio sa

Citat:
Nedeljko: Razlika izmedju mog i tvog primer je u tome sto deklaracija parametra sa int*& garantuje da se nece menjati objekat tipa int na koji pokazuje preneti pokazivac, a sam pokazivac moze da se preusmeri na drugu adrtesu, a kod tebe se garantuje da su i objekat na koji se pokazuje i sam pokazivac nepromenljivi unutar funkcije.


Sad vidim da sam se zeznuo. U oba slučaja je zabranjeno menjati objekat, a pokazivač se može preusmeravati. Branimirov primer je bio dobar, ali nije bio ono što je mene zanimalo.

Potom je Branimir okačio tačan i ilustrativan kod.

Code:
int main()
{
  const int a = 5;
  int *b;
//  const int*& c = b; ne moze jer to je posredno bypassovanje const korektnosti
  const int*&c = const_cast<const int*&>(b); // opasno, ali bar se vidi kao upozorenje
  c = &a; // ne valja, sada b pokazuje na const objekat
  *b = 42; // undefined behavior, bum tras
}


Priznajem da ga nisam pročitao, jer prethodni komentar (da se kompajlira nešto što se ne kompajlira) nije bio tačan. No, ipak ne smatram da sam ikoga zezao.
[ Nedeljko @ 29.09.2008. 13:53 ] @
Poenta mog pitanja je bila (ne mora niko da odgovori ako neće, nemam nameru nikoga da nerviram):

Zašto se lepo ne prati semantika: const int *p je pokazivač na konstantan objekat u smislu da se neće objekat moći menjati preko pokazivača p (možda će jga menjati neko drugi) i slično za reference na pokazivače? Druga je stvar kada esencijalno imamo konverziju (binarni sadržaji nisu isti, kao kod int i float) ili je drugi pokazivač na tabelu virtuelnih metoda i slično, pa privremeni objekat mora da se pravi, pa ne može referenca na privremeni objekat itd.
[ Goran Arandjelovic @ 29.09.2008. 13:54 ] @
Ma nije da si nekog zezao, nego, ne znam da li si video ono što sam ti ja posle toga napisao...

Postoji razlika između sledeće dve stvari:

Code:

void ispisi(const int * const &x);
...
...
...
ispisi(a);


i

Code:

void ispisi(const int *&x);
...
...
...
ispisi((const int*&) a);


Sve će ti biti jasno ako razlikuješ ove dve stvari...
[ Nedeljko @ 29.09.2008. 14:12 ] @
Ma, znam sve to. To se valjda vidi i iz mojih postova da razlikujem const int *p i int * const q.

Code:
int main() {
    const int *p = new int(22);
    // *p = 77; error: assignment of read-only location
    delete p;
    p = 0;
    int const *q = new int(5); // primer koji se sustinski ne razlikuje od prethodnog
    // *q = 77; error: assignment of read-only location
    delete q;
    q = 0;
    int * const r(new int(7));
    *r = 77;
    // r = 0; error: assignment of read-only variable r
    return 0;
}


No, ovde je došlo do nesporazuma oko toga šta ja zapravo pitam.
[ Branimir Maksimovic @ 29.09.2008. 14:52 ] @
Tvoje pitanje je jasno mada ne postoji konverzija iz reference u referencu, medjutim

Code:

int a;
float b;
const int& ra = a;
const int& rb = b; // da, mozes i ovo


je analogno

Code:

int* a=0;
int* const &cra = a;


a ne

Code:

int* b=0;
//const int* &rcb = b;


jer ovde imas non const referencu na const int*, a u prvom slucaju imas const referencu na int* ,
mislim da je u tome glavna stvar nesporazuma.

Dakle kada napises ovo drugo,dolazi do konverzije int* u const int* i stvaranja tmp objekta.

Pozdrav!
[ Nedeljko @ 30.09.2008. 08:19 ] @
Citat:
Branimir Maksimovic: Tvoje pitanje je jasno mada ne postoji konverzija iz reference u referencu


E to je ono sto me je zanimalo. Zasto projektanti jezika nisu odlucili da omoguce konverziju reference u referencu, kada je tip isti, do na const manipulator, gde non-const ide na const? Sta bi bilo lose u takvom C++ -u? U tom slucaju nema esencijalne potrebe za privremenim objektom, a nema ni int& -> float& perverzija. No, nema veze necu vise da smaram.
[ Branimir Maksimovic @ 30.09.2008. 10:47 ] @
Mislim da pogresno zamisljas reference. To nisu pointeri nego aliasi. Ne mozes imati referencu na referencu,
pointer na referencu ili cak ni niz referenci, jer one same za sebe ne zahtevaju bilo kakav storage.

To sto implementacija moze i uglavnom koristi pointere da implementira reference nema veze sa njihovom semantikom.
Uvedene su u jezik da bi mogla da se ostvari sintaksa izraza u kojima objekti funkcionisu bas kao
i osnovni tipovi.

E sad sto se tice konverzija i tmp objekata, to je sve proisteklo iz toga da bi se takodje omogucila
elegantna sintaksa izraza i overloadovanje operatora itd itd. Na kraju krajeva kada napises
neki izraz tu ima potencijalno gomila medju-rezultata.

Sa referencama ne treba razmisljati u smislu konverzije nego vezivanja objekta za alias
jer referenca semanticki nije varijabla i ne nosi vrednost kao recimo pointeri
pa je ne mozes ni konvertovati u nesto.

Drugo, da si pitao, sto ne mogu da implicitno konvertujem int** u const int**, tu bi odgovor bio
jasan, jer ovo je zabranjeno zbog const korektnosti (naveo sam i to za svaki slucaj)
zato sto su pointeri varijable koje imaju vrednost,
a u ovom slucaju vrednost bi bila rezultat konverzije.
Medjutim takve zabrane kod referenci nema jer se reference uvek binduju za nesto, a ako
dodje do konverzije binduju se za rezultat konverzije sto je tmp, pa onda i ne mozes menjati
preko reference vrednost originalnog objekta nego njegove konvertovane kopije.

Ne znam kako da ti objasnim bolje ;)
U c++, standardna konverzija se definise kao formiranje tmp objekta t iz izraza e : T t(e);
Dakle zamisli da je referenca nesto sto ne postoji nego je samo sintaksna zamena za neku
varijablu ili konstantu, dakle rezultat konverzije se ne smesta kao vrednost u referencu
nego se referenca binduje(vezuje na nasem?) za taj rezultat.
Za svaki kast ce naci da ako se nesto kastuje u referencu rezultat konverzije je lvalue,
a u suprotnom dolazi do standardne konverzije lvalue to rvalue, array to pointer, function to pointer.
E sad zasto ne mozes non const referencu bindovati za tmp objekat.
Pa zato sto mnogi generalno gledano smatraju da bi svi tmp objekti trebali biti
konstante. No sa novitetom koji dolazi sledece godine, Goran je to napomenuo,
kao sto su rvalue reference, stvar se menja. Neki rvalue objekti su
konstante a neki nisu te ima smisla menjati vrednost tmp objekata u slucaju nekih optimizacija.

Pozdrav!
[ Nedeljko @ 30.09.2008. 12:10 ] @
Citat:
Branimir Maksimovic: Drugo, da si pitao, sto ne mogu da implicitno konvertujem int** u const int**, tu bi odgovor bio
jasan, jer ovo je zabranjeno zbog const korektnosti (naveo sam i to za svaki slucaj) zato sto su pointeri varijable koje imaju vrednost, a u ovom slucaju vrednost bi bila rezultat konverzije.


Upravo je to suština mog pitanja. int** je pokazivač na pokazivač na int, a const int** je pokazivač na pokazivač na konstantan int. Jasno je meni da obrnuta konverzija (const int** -> int**) ne sme da se dozvoli, jer bi to bilo narušavanje const korektnosti, ali ne vidim kako bi konverzija int** -> const int** naručila const korektnost. Takođe, sa stanovišta implentacije jezika, u tom slučaju nije neophodno stvarati privremeni objekat. E, sad, ako su se dogovorili da svaka konverzija rezultuje privremenim objektom (a jesu), opet ne vidim smisao tog pravila. Postoje konverzije kod kojih je pravljenje privremenog objekta neophodno i one gde to nije neophodno.

Zašto se ne može referenca vezati za privremeni objekat, jasno je.

Code:

int* f(int& x) {
    return &x;
}
...
int x = 2, y = 3, *p = f(x+y);


Na šta pokazuje p posle ovoga? Na bum tras prethodno oslobodjen prostor i to naravno nema nikakvog smisla.
[ Branimir Maksimovic @ 30.09.2008. 13:38 ] @
Sto se tice int** -> const int**, vec sam ti odgovorio u primeru sa referencom.

Ista stvar, nakon konverzije pokazivacu na int* posredno mozes dodeliti vrednost da pokazuje
na const int i samim tim narusavas const korektnost.

Primer iz standarda:

Citat:

[Note: if a program could assign a pointer of type T** to a pointer of type const T** (that is, if line //1
below was allowed), a program could inadvertently modify a const object (as it is done on line //2). For
example,
int main() {
const char c = ’c’;
char* pc;
//1: not allowed
const char** pcc = &pc;
*pcc = &c;
//2: modifies a const object
*pc = ’C’;
}
—end note]


Pozdrav!

[ Nedeljko @ 30.09.2008. 15:11 ] @
U tom li čuču zeci grm, što ja mogu da promenim vrednost const char c i da mu zayebem const. Sad mi je kristalno jasno! Hvala veliko.

Napisao sam odgovarajući primer za reference na pokazivače

Code:

#include <iostream>

using namespace std;

int main() {
    const char c = 'A';
    char *pc;
    const char* &pcr = (const char*&)(pc);
    pcr = &c;
    *pc = 'B';
    
    if (&c==pc)
        cout << "Addresses of c and *pc are equal.\n";
    else
        cout << "Addresses of c and *pc are different.\n";
    
    cout << "c            : " << c << endl;
    cout << "*pc          : " << *pc << endl;
    cout << "*((char*)&c) : " << *((char*)&c) << endl;
    
    return 0;
}

Output:

c            : A
*pc          : B
*((char*)&c) : B


Dakle, cout << c; ipak stampa slovo 'A', čak i kada program prevodim bez optimizacije (g++ -O0 p.cpp).
[ Goran Arandjelovic @ 30.09.2008. 16:25 ] @
Da, napomenuo sam da će se koristiti rvalue reference, pogotovu kada je u pitanju TMP ili optimizacija nekih STL algoritama. Naime, nađi negde na Netu implementaciju vector klase (i nekih algoritama nad njim) pomoću rvalue referenci...videćeš već u kom kontekstu se javljaju tamo. Dogodila su se značajna ubrzanja jer više ne moraš da kopiraš tmp objekat prvo u neki lvalue objekat pa da tek onda operišeš nad njim.

Inače, što se tiče prethodne priče, Branimir je lepo objasnio da su to samo aliasi i ništa više i još jedna semantička razlika postoji između referenci i pokazivača (posledica tretiranja referenci kao aliasa):

Code:

const int x = 10;
const int y = 77;

const int *px = &x;

cout << *px << endl; // rezultat: 10
px = &y; // ovo je dozvoljeno, naravno...
cout << *px << endl; // rezultalt: 77



const int &rx = x;
/* E sada, ovde je referenca rx (u nekom smislu) implicitno postala konstantna nakon
inicijalizacije, tj, ovo je ekvivalentno kao da si za px rekao: const int * const px = &x */



// rx = y; 
/* Zato ovo sada i nema smisla kao što je imalo smisla za px, jer je ovde rx
(semantički) isto što i x (tj. alias), a x je const int! */




E da, a i jedan od razloga što si gore opet dobio rezultat 'A' je to što je ta konstanta u data segmentu (read-only delu data segmenta)
[ Branimir Maksimovic @ 30.09.2008. 18:09 ] @
Samo da prokomentarisem za ono sto je rezultat dobio 'A' sa neposrednim a 'B' sa posrednim pristupom.

Prosta stvar, u C++-u konstante ne moraju da imaju rezervisano mesto u memoriji.
Kad je Nedeljko uzeo adresu konstante onda je kompajler rezervisao mesto u memoriji
i odatle cita kad ide preko pointera. Kad ide neposredno onda kompajler jednostavno
'upokuje' konstantu i uopste ne gleda sta je u memoriji.

Naravno mogao je i da zastiti var za write, onda bi dobio segfault prilikom pokusaja upisa.

Kad je ub sve je dozvoljeno.

Pozdrav!

edit:
izgleda da ce i rvalue reference imati pravilo kao i const reference sto se tice zivota tmp objekata.

ovo sam nasao ovde http://www.artima.com/cppsource/rvalue.html

a primer je :

Citat:

An rvalue reference behaves just like an lvalue reference except that it can bind to a temporary (an rvalue), whereas you can not bind a (non const) lvalue reference to an rvalue.

A& a_ref3 = A(); // Error!
A&& a_ref4 = A(); // Ok


by Howard E. Hinnant, Bjarne Stroustrup, and Bronek Kozicki

Dakle, samo je pitanje vremena kad ce da zaokruze kako ce sve to izgeldati u standardu.
Inace Howard je lik koji je dao proposal i koji je pomogao u g++ 4.3 implementaciji .



[Ovu poruku je menjao Branimir Maksimovic dana 30.09.2008. u 19:36 GMT+1]
[ Goran Arandjelovic @ 30.09.2008. 19:43 ] @
Mislio sam upravo na tu... "upokovanu" konstantu, ona jeste u data segmentu. Ona je literal.
[ Branimir Maksimovic @ 30.09.2008. 20:13 ] @
Pa vidi mozda je malo besmisleno ovo oko cega raspravljamo, ali da je ona upakovana u read only data segmentu
onda write sigurno ne bi prosao kao sto je slucaj kod const char* literala.
A opet da kompiler dva puta pravi storage jednom u read only a onda u read/write
segmentu za jednu konstantu to je opet gubitak prostora.
U stvari mislim da constante nemaju linkage osim ako nisu deklarisane/definisane sa extern?

Pozdrav!

edit: mislim na osnovne tipove kao konstante, naravno nesto komplikovanije zahteva
storage.

edit2: proverio sam, kad konstanta ima static ili global scope compiler je smesta u read only
data segment i onda dolazi do segfault-a prilikom upisa.
edit3: sto je i logicno jer kad je konstanta auto kompajler je verovatno smesta u stek frame
koji je r/w

[Ovu poruku je menjao Branimir Maksimovic dana 01.10.2008. u 09:38 GMT+1]

[Ovu poruku je menjao Branimir Maksimovic dana 01.10.2008. u 10:09 GMT+1]
[ Nedeljko @ 01.10.2008. 08:32 ] @
Ja sam onaj primer testirao sa sledecim kompajlerima:

1. GNU g++ 4.2.2 za Linux (ukljucijuci i prevodjenje sa -O0),
2. MinGW 5.1.13,
3. Borland C++ 5.5. freecommandlinetools,
4. Visual C++ 9.0

i svi su dali isti rezultat. Ja sam to shvatio da konstanta ima i svoju kopiju ciju adresu daje, a da je adresa same konstante nedostupna. No, izjasnjava li se standard o ovome?

Drugo, ja sam na ovoj temi prvi put video &&, a da to nije binarni operator (logicka konjunkcija). Sta je to? Dosta mi je i link. Ne morate da se zamarate kucanjem.
[ Branimir Maksimovic @ 01.10.2008. 08:46 ] @
Nema kopiju nego kompajler jednostavno radi isto kao kad opalis #define A 4 recimo,
kad pristupas neposredno, a posredno naravno mora da napravi var u memoriji.

Drugo, to sa && je nova sintaksa za rvalue reference.
Verujem da ces tada moci da kompajliras nesto ovako:

Code:

void f(const int* &&a)
{
  a=0;
}

int main()
{
  int *b = new int(5);
  f(b); // b nece promeniti vrednost posto ce rvalue biti menjana
}


No nemam g++ 4.3 da proverim teroriju ;)

Link imas u jednom od prethodnih postova da vidis o cemu se radi.

Pozdrav!
[ Nedeljko @ 01.10.2008. 10:41 ] @
Čak i kada napises int a = 5;, kompajler mora tu peticu da ima negde, bilo da je ona sastavni deo mašinske instrukcije, bilo da je dovlači sa neke adrese. Peticu će lako da ugradi direktno u kod. Međutim, šta ako je tip preglomazan da bi stao u mašinsku instrukciju kao parametar? Kada sam na početku prethodnog programa stavio typedef char T[10], svuda tip char zamenio sa T i konstanti c dodelio vrednost "A", na izlazu sam dobio tri puta slovo B.

Code:

#include <iostream>

using namespace std;

int main() {
    typedef char T[10];
    char x[10];
    const T c = "A";
    T *pc;
    const T* &pcr = (const T*&)(pc);
    pcr = &c;
    **pc = 'B';
    
    if (&c==pc)
    cout << "Addresses of c and *pc are equal.\n";
    else
    cout << "Addresses of c and *pc are different.\n";
    
    cout << "c            : " << c << endl;
    cout << "*pc          : " << *pc << endl;
    cout << "*((char*)&c) : " << *((char*)&c) << endl;
    
    return 0;
}

Output :

Addresses of c and *pc are equal.
c            : B
*pc          : B
*((char*)&c) : B


Da li su ove zavrzlame sastavni deo standarda ili je to do implementacije kompajlera? Indikativno je da su reyultati isti na rayli;itim kompajlerima.
[ Branimir Maksimovic @ 01.10.2008. 11:17 ] @
Naravno, niz od 10 karaktera nece moci da pokuje po kodu ;) pa to uvek vuce sa steka
koji je r/w.

Medjutim, probaj ovo pa vidi koji ces rezultat dobiti.

Code:

#include <iostream>

using namespace std;

    typedef char T[10];
    const T c = "A";

int main() {
    char x[10];
    T *pc;
    const T* &pcr = (const T*&)(pc);
    pcr = &c;
    **pc = 'B';

    if (&c==pc)
    cout << "Addresses of c and *pc are equal.\n";
    else
    cout << "Addresses of c and *pc are different.\n";

    cout << "c            : " << c << endl;
    cout << "*pc          : " << *pc << endl;
    cout << "*((char*)&c) : " << *((char*)&c) << endl;

    return 0;
}



edit: po standradu modifikovanje konstanti je ub pa kompajler moze da radi sta mu je volja, tj
ne moze se predvideti tacno ponasanje u svim slucajevima

Pozdrav!
[ Nedeljko @ 02.10.2008. 08:55 ] @
OK, kad su toliko zabrinuti za stvari koje se smatraju sitnicama (da li ce nesto sto se inace moze lepo isprogramirati i bez toga, moze jos malkice optimizovati), jesu li se setili da ubace i neki tip podataka za novcane iznose?
[ itf @ 02.10.2008. 09:05 ] @
Citat:
Nedeljko: OK, kad su toliko zabrinuti za stvari koje se smatraju sitnicama (da li ce nesto sto se inace moze lepo isprogramirati i bez toga, moze jos malkice optimizovati), jesu li se setili da ubace i neki tip podataka za novcane iznose?


Oprosti, ali čemu fantaziranje? Novčani iznosi su potpuno opisivi realnim brojevima definiranim u C(++)u.
[ Nedeljko @ 02.10.2008. 13:22 ] @
Nisu jer postoji round off error.

http://www.elitesecurity.org/t303962-0-Novi-excel-bug

Od 6 odbi 30 puta po 0.2 i neces dobiti nulu.

Za novcane iznose je bolje koristiti cele brojeve, ili jos bolje posebno dizajniranu klasu zasnovanu na celobrojnoj aritmetici. Nisu BCD brojevi i tipovi za novcan iznose (koji postoje u mnogim jezicima i drugim sw proizvodima) izmisljeni bez potrebe.
[ Branimir Maksimovic @ 02.10.2008. 16:05 ] @
Roundoff error postoji na svakom harveru. Definise se kao greska nastala usled neke aritmeticke
operacije u odnosu na preciznost masine.
Preciznost masine je najmanji decimalni broj te masine koji dodat na 1.0 daje nesto drugo od 1.0 ;).
Drugo je pitanje konverzije, tj 0.1 u binarnom valjda ima beskonacno decimala dok u dekadnom ima
1 pa kompjuter ne moze to da prikaze kako treba.

E sad, naravno za predstavljane novcanih iznosa na racunu zbog toga je pozeljno da
se vrednost cuva u dekadnom brojnom sistemu ;)

No kalkulacije se i dalje vrse u float ili fixed pointu sa time da se numericka analiza
bavi uracunavanjem greske u proracune.

Evo imas recimo http://finance.bi.no/~bernt/gcc_prog/index.html finansijske algoritme u c++
gde se koristi floating point.

Pozdrav!


[ Dragi Tata @ 02.10.2008. 18:22 ] @
Citat:
Nedeljko: OK, kad su toliko zabrinuti za stvari koje se smatraju sitnicama (da li ce nesto sto se inace moze lepo isprogramirati i bez toga, moze jos malkice optimizovati), jesu li se setili da ubace i neki tip podataka za novcane iznose?


Intel ima biblioteku koja to pokriva: http://software.intel.com/en-u...al-floating-point-math-library
[ Nedeljko @ 02.10.2008. 20:36 ] @
Citat:
Branimir Maksimovic: Roundoff error postoji na svakom harveru. Definise se kao greska nastala usled neke aritmeticke operacije u odnosu na preciznost masine.


I usled konverzije iz jednog pozicionog sistema u drugi. Matematičku podlogu greške zaokrugljivanja u računarskoj aritmetici sam detaljno opisao na citiranoj temi, tako da ne znam zašto meni ovo pišeš, ali nema veze. Na citiranj temi imam nekoliko postova. Možeš da ih pročitaš.

Ne kažem da je neki veliki problem ako su finansijski izveštaji samo približno tačni, ali možda ipak nije loše postići "knjigovodstvenu tačnost" da neko ne bi cpao što je 0.754999 zaokrugljeno na 0.76 umesto na 0.75. Pod "knjigodstvenom tačnošću" podrazumevam računanje kod kojeg je svaka pojedinačna operacija vršena sa dogovorenom tačnošću uz korektnu primenu pravila zaokrugljivanja. Primera radi, ako računamo na dve decimale iza decimalne tačke, jedini "knjigovodstveno tačan" rezultat računanja izraza 100/3+100/3+100/3 je 99.99 (rezultat 100.00 nije prihvatljiv).



Hvala, već sam napisao C++ biblioteku za to. Šta misliš, odakle mi ove silne cifre?

http://www.elitesecurity.org/p2008383
http://www.elitesecurity.org/p2012445
http://www.elitesecurity.org/p2012460

Biblioteka koju sam napisao, pored ostalog omogućava tačno izračunavanje (u dekadnom sistemu) vrednosti memorisane u tipovima float, double i long double.

No mene je zanimalo, kada se ekipa koja standardizuje C++ toliko brine za rvalue reference, brine li se bar upola toliko za neke "prizemnije" stvari, koje su u široj upotrebi.
[ Branimir Maksimovic @ 02.10.2008. 21:57 ] @
Ona intelova biblioteka za decimalnu kalkulaciju je vec u g++ 4.3, a mislim da ce pored gomile stvari
koje ce dodati u c++09 tu biti i podrska za ovo sto te zanima.

http://software.intel.com/en-u...l-floating-point-math-library/


Pozdrav!
[ Nedeljko @ 03.10.2008. 08:51 ] @
Kad smo već debelo otišli u off topic, zna li se zašto C++ nikako da dobije standardizovanu podršku za GUI, za baze, Web itd., kao što recimo ima Java. Java programeri retko imaju potrebe da izlaze van API-ja. To su uglavnom neke specijalizovane stvari. Sa druge strane C++ programeri malo toga mogu unutar API-ja da urade. Valjda zato što se želi da C++ pude blizak kako mašini, tako i OS-u.
[ Goran Arandjelovic @ 03.10.2008. 10:09 ] @
Citat:
Nedeljko: Kad smo već debelo otišli u off topic, zna li se zašto C++ nikako da dobije standardizovanu podršku za GUI, za baze, Web itd., kao što recimo ima Java. Java programeri retko imaju potrebe da izlaze van API-ja. To su uglavnom neke specijalizovane stvari. Sa druge strane C++ programeri malo toga mogu unutar API-ja da urade. Valjda zato što se želi da C++ pude blizak kako mašini, tako i OS-u.


Jeste želja da C++ bude blizak mašini.
Java je sasvim drugačija priča u startu u odnosu na C++. Potreba je da bude binarno portabilna, zato i ima toliko veliki API. Iz te potrebe direktno sledi da ona mora da ima i podršku za GUI, baze...itd.
Komitet za standardizaciju C++-a i dizajneri jezika se teško odlučuju da usvoje neku novinu u samom jeziku (tako je sada, iako nekom komplikovan, C++ možda najbolje dizajniran jezik), a sve ostale stvari se ostavljaju piscima biblioteka.

Imaš dosta biblioteka koje pokrivaju sve ovo što si nabrojao..ali evo, uzmimo za primer GUI:
Ako hoćeš recimo neke (L)GPL biblioteke (na Linuxu je najinteresantniji GTK i Qt, zar ne?:)
Qt - i Windows i Linux
gtkmm (GTK C++ API) - i Windows i Linux

Na Windowsu, VCL, Windows Forms (.NET, C++/CLI), itd... + imaš odlične RAD alate.
--
E sada, znam da tebe zanima zašto se recimo GUI bibliotekama ne bave ljudi koji standardizuju sam jezik. Mnogo je teže razvijati takve biblioteke koje treba da "gađaju" više operativnih sistema (bez postojanja bilo kakve VM). Lako (lakše) je razviti portabilan API za Javu kad ona već ima VM na više OS-a.
C++ teži da ostane jezik opšte namene (ozbiljne igre sigurno nećeš pisati u Javi, bilo kakve time critical aplikacije gde je usput možda neophodno i racionalnije koristiti memoriju (gde ne želiš GC), drajveri (naravno, neće ti trebati OOP, ali templejti i bolji type sistem verovatno da))
[ Nedeljko @ 03.10.2008. 11:09 ] @
Ipak mislim da je glavni razlog taj što C++ standardizuje gomila ljudi iz celog sveta, a Javu samo jedna firma. Naravno da je moguće osnovne GUI elemente standardizovati. Kad je moguće napisati prenosivi Qt, moguće je i razviti standardizovanu prenosivu podršku za GUI, web, klijent/server... Ali, onda treba ogroman komitet da se nateže i složi oko nečega.

Čak i da je toliki problem standardizovati GUI, u čemu je problem sa klasama tipa BigInt? Zar nije u matematici "standardizovano" šta je zbir, šta razlika, šta proizvod dva cela broja, i šta su količnik i ostatak pri euklidskom delenju dva cela broja? Potpuno je jasno šta treba da bude output za neki input.
[ Goran Arandjelovic @ 03.10.2008. 11:36 ] @
Bitno je imati sve te stvari standardizovane ako želis ABI kompatibilnost i hoćeš da praviš VM.

A ovako, to zaista nije toliko bitno. Recimo, sve do skora, do izlaska TR1 dokumenta, ti nisi imao standardizovane pametne pokazivače (izuzmimo auto_ptr, koji i nije previše pametan :), a opet su skoro svi u te svrhe koristili implementacije Boost biblioteke. Isto tako i što se tiče multithreadinga. Prosto, nije toliko bitno da li je nešto zvanično deo Standarda ako je dobro, tj. ako to razvija izvrsna ekipa. Vremenom se to i "uvuče" svima pod kožu (i pokaže se da je kvalitetno i skalabilno) i koriste ga kao da je deo Standarda.

Kažem ti, ako praviš VM, onda da, treba da standardizuješ mnogo više API-ja nego inače (tj. dosta biblioteka treba zvanično da bude deo standarda ako očekuješ da VM može da vrti aplikaciju, što i jeste cilj Jave), a u slučaju C++-a imaš biblioteke koju su kvalitetne, ali ne postoji prevelika potreba da budu deo Standarda (važnije je da one poštuju Standard, kako bi kod bio portabilan)
[ Dragi Tata @ 03.10.2008. 20:33 ] @
Neke od odgovora možeš da nađeš u Stroustrupovom radu: http://www.research.att.com/~bs/hopl-almost-final.pdf

Posebno pogledaj poglavlje 5.5 - What Wasn’t Done
[ Branimir Maksimovic @ 03.10.2008. 21:52 ] @
Citat:

Kad smo već debelo otišli u off topic, zna li se zašto C++ nikako da dobije standardizovanu podršku za GUI, za baze, Web itd., kao što recimo ima Java. Java programeri retko imaju potrebe da izlaze van API-ja. To su uglavnom neke specijalizovane stvari. Sa druge strane C++ programeri malo toga mogu unutar API-ja da urade. Valjda zato što se želi da C++ pude blizak kako mašini, tako i OS-u.


Pa ti iniciras off topic pricu. ;)

[rant on]
Pazi jedno je standardizovati programski jezik, drugo biblioteke.
Recimo bas kad pominjes javu a vec je receno ona je zamisljena kao izolovana platforma
koja se vrti na nekoj drugoj platformi. Zamisli sad koji je problem da dodjes do native
api-ja ;) JNI ne bih pozeleo ni najgorem neprijatelju.
E sad uporedi recimo native gui aplikacije sa onim sto izgleda kad se uradi u javi.
I po performansama i po izgledu i po zauzecu memorije. Nista bolje ne mislim o .netu,
mada je on usko vezan za windows.
Kada imas standardizovan api to povlaci i sablon dizajna gde su ti ruke prakticno
vezane i sve stancujes uvek na isti nacin.
Medjutim, taj dizajn mozda i nije najbolji, mozda hoces da uradis nesto drugacije.
Eto recimo java programeri su bili ograniceni da koriste thread po konekciji sve dok se neko
nije setio da uvrapuje select i to nazove nio.
Ili probaj da napravis svoj dizajn za threading model u javi. Uradis neki jaci poduhvat
i slicno.
Sta ako izadje neka super duper funkcionalnost o kojoj mora da sanjas dok se
to ne inkorpoira u lib. Da ne govorim recimo bas o c++ strimovima.
Moj kolega je rekao da je to jedno veliko zlo ;)
A zna se koju funkcionalnost nudi linux native file suport a koju strimovi ;)
Mislim c++ pruza mogucnost da se strimovi ishakuju ali tad kod nije
portabilan ;)
Mislim, sve to izgleda elegantno, ali na kraju vidis da su ti usta upregnuta u ular
i nema mrdanja.
Cisto sumnjam da bi neki standardizovani gui framework pio vodu, naravno osim
onima koji kvalitet aplikacije podredjuju portabilnosti.
A tek portabilnost u javi. Koji je to tek mit. Java program je portabilan
samo na jedan vm i jedan kompajler. Promeni kompajler ili vm i gle nista ne radi ;)
[rant off]

Bas sam se ispucao ;)

Pozdrav!
[ Nedeljko @ 04.10.2008. 18:58 ] @
Citat:
Goran Arandjelovic: ...tako je sada, iako nekom komplikovan, C++ možda najbolje dizajniran jezik


Sa ovim se debelo ne bih složio. Toliko je dobro koncipiran da je nemoguće napisati kompajler, koji bi za svaki ulazni fajl stao u konačnom broju koraka sa jednim od dva izlaza:

1. Porukom o grešci ako u ulaznom fajlu nije ispravan C++ program.
2. Komapjliranim programom ako je u ulaznom fajlu ispravan C++ program.

Postojeći C++ kompajleri su samo aproksimacija ovoga.

Branimire,

Da, ja sam krivac za off topic, ne sporim. No, ako se tebi ne sviđa QDateTime klasa, niko ti ne brani da napišeš svoju i koristiš je u Qt programu. Isto važi i za Javu i sve ostalo. Java ima JNI ako zatreba, preko koga možeš da implementiraš takve stvari (izlaskom iz Jave) umesto da čekaš. Razlika je u tome da li imaš bar neko isporučeno rešenje, pa onda pravi svoje ako baš moraš, ili nikakvo isporučeno rešenje, gde si prinuđen da praviš svoje.
[ kiklop74 @ 05.10.2008. 02:26 ] @
Citat:
Branimir Maksimovic: Pa ti iniciras off topic pricu. ;)

[rant on]
Pazi jedno je standardizovati programski jezik, drugo biblioteke.
Recimo bas kad pominjes javu a vec je receno ona je zamisljena kao izolovana platforma
koja se vrti na nekoj drugoj platformi. Zamisli sad koji je problem da dodjes do native
api-ja ;) JNI ne bih pozeleo ni najgorem neprijatelju.


Postoji nešto gore od samog JNI-a. A to je pisanje maven skripte za kompajliranje JNI-a na windows-u i linux-u...

Svojevremeno sam radio na projektu koji je zahtevao da se napiše par JNI-a za pristup nekim sistemskim servisima dok bi GUI bio Java+Swing ili samo Java i sve je to trebalo ubudžiti u maven. Glavni arhitekta u toj firmi se kleo u maven kao nešto najbolje izmišljeno posle točka i pored toga što "olakšava" kompajliranje java projekata navodno je lečio i sidu...

U teoriji maven zvuči super ali u stvarnosti to je nedovršena alatka sa očajnom dokumentacijom i pluginovima prepunim bagova.

Problemi se posebno javljaju kada se poželi uraditi nešto van standardno predviđenih akcija.... kako sam samo zamrzeo celo to zamešateljstvo....

[ Nedeljko @ 06.10.2008. 09:59 ] @
Hajmo malo u 3ayebanciju, kad smo već debelo off topic.

1. Nemoguće je napisati C++ kompajler (matematički dokazano).
2. Na osnovu 1. ne postoji ni jedan C++ kompajler.

Od čega žive C++ programeri?
[ Branimir Maksimovic @ 06.10.2008. 21:10 ] @
Hehe, pazi prvi c++98 full compliant compiler je uradjen
2002 ;)

1. tesko je uraditi potpun C++ compiler

2. na osnovu 1. postoje nepotpuni c++ kompajleri ;)

Pozzzzzzzzzzz!

[ deerbeer @ 07.10.2008. 23:48 ] @
Citat:
Goran Arandjelovic
Imaš dosta biblioteka koje pokrivaju sve ovo što si nabrojao..ali evo, uzmimo za primer GUI:
Ako hoćeš recimo neke (L)GPL biblioteke (na Linuxu je najinteresantniji GTK i Qt, zar ne?:)
Qt - i Windows i Linux
gtkmm (GTK C++ API) - i Windows i Linux

I jos :
http://en.wikipedia.org/wiki/Cairo_(software)
http://www.cairographics.org/
GTK+ od svoje 2.8 koristi cairo engine za svoje widgete
Pogledaj listu backend OS-ova, cak su je i AmigaOS developeri koristili za cuveni workbench :)


[ Nedeljko @ 08.10.2008. 10:37 ] @
Sve je to lepo. I KDE se oslanja na Qt,, pa kada je Trolltech odlucio da promeni arhitekturu u verziji 4, onda se KDE tim cesao i tamo gde ga ne svrbi, ne bi li izbacio novi KDE zasnovan na Qt-u 4.