Neuer Datentyp: 48-Bit Int
-
camper schrieb:
groovemaster schrieb:
Ich hab dazu ja auch nur ein Beispiel konstruiert. Statische Intialisierung kann auch so aussehen
static foo bar;
Und foo muss hier kein POD sein.
was soll das denn jetzt?
Das Statement war eher in Verbindung mit deiner Aussage bzgl.
camper schrieb:
ein problem mit nicht-PODs ist, dass keine statische initilisierung stattfinden kann
gedacht.
camper schrieb:
das weisst du besser
das static keyword hat doch nichts mit der initialisierung zu schaffen
Doch, das hat es. Aber wenn's dich stört, dann denk es dir einfach weg und static storage duration dazu. (und lass deine Klugscheisserei aussen vor
)
camper schrieb:
nur insofern als static storage voraussetzung für static initilisiation ist
Das ist zwar richtig, aber eher irrelevant, da man von statischer Initialisierung sowieso nur bei nicht-lokalen Objekten spricht. Viel wichtiger ist die Tatsache, dass es sich um statische Initialisierung bei Nullinitialisierung oder Initialisierung mit konstanten Werten handelt.
camper schrieb:
sinnfreies beispiel:
// a.h struct uint48 { /* schönes interface, kein POD ... */ }; extern const uint48 xyz; // a.cpp #include "a.h" const uint48 xyz = 10; // b.cpp #include "a.h" struct X { uint48 x; X() : x( xyz ) { } }; X x;
wird xyz vor oder nach x initialisiert?
Das ist aber nicht spezifisch für die eigentliche Problematik. Du sprichst hier ein generelles Problem beim Initialisieren von globalen Objekten an.
Auch wenn sich ein uint48 wie ein elementarer Typ benutzen lässt, so bleibt es doch ein non-POD mit all den verbundenen Konsequenzen. So wie es bei anderen Klassen auch der Fall ist. Das dürfte dem Op mittlerweile schon klar geworden sein.camper schrieb:
und das steht wo?
Nunja, ich hoffe, dass du noch nicht anfängst zu hyperventilieren.
Ansonsten bau dir ein static assert mit sizeof.
der standard erlaubt ausdrücklich gc.
Mag sein, diesbzgl. sind mir allerdings keine Quellen bekannt. Genauso wenig, dass die Implementation einfach so irgendwelche "unsichtbaren" Daten in die Klasse packen darf (bis auf Padding, um entsprechendes Alignment zu garantieren). Und wenn schon GC, dann werden benötigte Informationen mit Sicherheit an anderer Stelle gespeichert.
-
groovemaster schrieb:
camper schrieb:
groovemaster schrieb:
Ich hab dazu ja auch nur ein Beispiel konstruiert. Statische Intialisierung kann auch so aussehen
static foo bar;
Und foo muss hier kein POD sein.
was soll das denn jetzt?
Das Statement war eher in Verbindung mit deiner Aussage bzgl.
camper schrieb:
ein problem mit nicht-PODs ist, dass keine statische initilisierung stattfinden kann
gedacht.
camper schrieb:
das weisst du besser
das static keyword hat doch nichts mit der initialisierung zu schaffen
Doch, das hat es.
kapitel und vers?
Aber wenn's dich stört, dann denk es dir einfach weg und static storage duration dazu.
das wäre richtig.
camper schrieb:
nur insofern als static storage voraussetzung für static initilisiation ist
Das ist zwar richtig, aber eher irrelevant, da man von statischer Initialisierung sowieso nur bei nicht-lokalen Objekten spricht. Viel wichtiger ist die Tatsache, dass es sich um statische Initialisierung bei Nullinitialisierung oder Initialisierung mit konstanten Werten handelt.
kapitel 3.6.2 heisst zwar initialization of non-local objects, bezieht sich aber tatsächlich auf objekte mit static storage duration und das bezieht auch locale static objekte mit ein, (3.7. 1 vers 3 und 4). und weiter (6.7) wird jedes lokale objekt mit s.s.d. zero-initialisiert, bevor irgendeine andere init. stattfindet. und zero-init. (und init. mit konstanten ausdrücken) IST statische initialisierung (3.6.2). folgerung: statische initialisierung ist nicht auf nicht-lokale objekte beschränkt.
und ja, ich weiss, was statische initialisierung ist, das war nie die frage. und non-PODs werden eben NICHT statisch initialisiert; der speicher, den sie belegen wird zero-initialisiert, aber das ist wie gesagt, völlig bedeutungslos. auf ein nicht-POD, dessen konstruktor noch nicht ausgeführt wurde, zuzugreifen, ist undefiniertes verhalten.camper schrieb:
sinnfreies beispiel:
wird xyz vor oder nach x initialisiert?
Das ist aber nicht spezifisch für die eigentliche Problematik. Du sprichst hier ein generelles Problem beim Initialisieren von globalen Objekten an.
Auch wenn sich ein uint48 wie ein elementarer Typ benutzen lässt, so bleibt es doch ein non-POD mit all den verbundenen Konsequenzen. So wie es bei anderen Klassen auch der Fall ist. Das dürfte dem Op mittlerweile schon klar geworden sein.genau davon rede ich die ganze zeit. mit PODs hat man dieses problem nicht, sofern sie durch einen konstanten ausdruck initialisiert werden.
camper schrieb:
und das steht wo?
Nunja, ich hoffe, dass du noch nicht anfängst zu hyperventilieren.
Ansonsten bau dir ein static assert mit sizeof.
es war nur eine einfache frage. wenn es dich nervt, solltest du dir überlegen, ob du überhaupt antworten solltest. wenn es eben eine solche garantie gäbe, wäre das schlicht nützlich zu wissen.
Und wenn schon GC, dann werden benötigte Informationen mit Sicherheit an anderer Stelle gespeichert.
'mit sicherheit'.
-
camper schrieb:
kapitel und vers?
Nur mal als Beispiel
void foo() { int a; } void bar() { static int a; }
Beide Funktionen machen exakt das gleiche, bis auf den Unterschied static. Der sorgt dafür, dass aus automatischem statischer Speicher wird, und damit Auswirkungen auf die Initialisierung hat. Mehr dazu findest du zB unter 3.7.1.
camper schrieb:
kapitel 3.6.2 heisst zwar initialization of non-local objects, bezieht sich aber tatsächlich auf objekte mit static storage duration und das bezieht auch locale static objekte mit ein, (3.7. 1 vers 3 und 4). und weiter (6.7) wird jedes lokale objekt mit s.s.d. zero-initialisiert, bevor irgendeine andere init. stattfindet. und zero-init. (und init. mit konstanten ausdrücken) IST statische initialisierung (3.6.2). folgerung: statische initialisierung ist nicht auf nicht-lokale objekte beschränkt.
Nein, das hast du falsch verstanden. Mit nicht-lokal sprach ich nicht vom Scope, sondern vom Speicher selbst. Das betrifft ja nicht nur static Variablen in Funktionen, sondern auch static Member in Klassen.
camper schrieb:
und non-PODs werden eben NICHT statisch initialisiert; der speicher, den sie belegen wird zero-initialisiert, aber das ist wie gesagt, völlig bedeutungslos
Mag sein, dass zero-initialization bei Nicht-PODs nicht als statische Initialisierung bezeichnet wird. Auch wenn du mir noch keine Quellen diesbzgl. genannt hast. Letztendlich ist es nur eine relativ unbedeutende Begriffsdefinition, an die du dich klammerst. Wichtig ist nur, dass zero-initialisiert wird. Deshalb versteh ich immer noch nicht deinen Besserwisser-Einwand gegen die initializer-list, denn die ist das Einzige, was du bei einem Nicht-POD nicht verwenden kannst.
camper schrieb:
auf ein nicht-POD, dessen konstruktor noch nicht ausgeführt wurde, zuzugreifen, ist undefiniertes verhalten
Das ist richtig. Steht aber im Widerspruch wozu?
camper schrieb:
es war nur eine einfache frage.
Und eine einfache Antwort. Ich kenne zumindest keine Textstelle, die explizit erlaubt, irgendwelche zusätzlichen, dem Anwender nicht sichtbaren, Daten in eine Klasse zu packen. Ausnahmen hierbei sind virtuelle Basisklassen, virtuelle Funktionen und Padding (aufgrund Alignment). Deshalb bin ICH mir sicher, dass nur die Daten in die Klasse gepackt werden, die ich explizit angebe. Wenn DU dir nicht sicher bist, dann mach dir, wie bereits erwähnt, ein static assert.
'mit sicherheit'
So ist das. Siehe oben.
-
hier die vorläufige offizielle endversion meiner int48-klasse
int48.h
#ifndef INT48_H #define INT48_H class int48 { unsigned int lo32; // intel format signed short hi16; public: int48 () : lo32(0), hi16(0) {} int48 (__int64 x) : lo32(x), hi16(x>>32) {} operator __int64 () const { return (__int64)lo32 | ((__int64)hi16)<<32; } }; class uint48 { unsigned int lo32; // intel format unsigned short hi16; public: uint48 () : lo32(0), hi16(0) {} uint48 (unsigned __int64 x) : lo32(x), hi16(x>>32) {} operator unsigned __int64 () const { return (unsigned __int64)lo32 | ((unsigned __int64)hi16)<<32; } }; #endif // INT48_H
kompakter gehts nicht mehr.
hier noch der iostream-ausgabe-operator (keine ahnung ob der so jetzt standard-konform ist):
iostream_int64.h
#ifndef IOSTREAM_INT64_H #define IOSTREAM_INT64_H #include<iostream> using namespace std; ostream &operator<< (ostream &os, unsigned __int64 x); ostream &operator<< (ostream &os, __int64 x); #endif // IOSTREAM_INT64_H
iostream_int64.cpp
#include<iostream> #include<cstring> #include "iostream_int64.h" using namespace std; static char *u64toa (unsigned __int64 x, int b, bool uc) { static char buf[21]; char *cp; cp=buf+22; *--cp='\0'; do { *--cp=(uc ? "0123456789ABCDEF" : "0123456789abcdef")[x%b]; x=x/b; } while(x); return cp; } static void write_uint64 (ostream &os, unsigned __int64 x, bool sign) { int base,w; char *sb,*cp; ios::fmtflags f=os.flags(); if((f & (ios::left|ios::right|ios::internal))==0) f|=ios::left; if(f & ios::oct) { base= 8; sb="0"; } else if(f & ios::hex) { base=16; sb="0x"; } else { base=10; sb=""; } cp=u64toa(x,base,(f & ios::uppercase)!=0); w=os.width()-strlen(cp)-strlen(sb); if(sign || (f & ios::showpos)) --w; if(w>0 && (f & ios::left)) while(w--) os.put(os.fill()); if(sign || (f & ios::showpos)) os.put(sign ? '-' : '+'); if(w>0 && (f & ios::internal)) while(w--) os.put(os.fill()); while(*sb) os.put(static_cast<unsigned char>(*sb++)); while(*cp) os.put(static_cast<unsigned char>(*cp++)); if(w>0 && (f & ios::right)) while(w--) os.put(os.fill()); os.width(0); } ostream &operator<< (ostream &os, unsigned __int64 x) { write_uint64(os,x,false); return os; } ostream &operator<< (ostream &os, __int64 x) { write_uint64(os,(x<0 ? -x : x),x<0); return os; }
funktioniert mit VC++6 einwandfrei. für gcc muß man __int64 natürlich durch long long ersetzen.
euere diskussion zum thema kompatibilität halte ich für überflüssig. zumindest der groovemaster-ansatz, über 64-bit-integers zu arbeiten, ist sowieso nicht portabel, da 64-bit-integers nicht standardisiert sind.
es fehlen allerdings immer noch die iostream-eingabe-operatoren für 64-bit-integers. falls sie nicht schon in der standardbibliothek des compilers vorhanden sind. bei VC++6 sind sie es jedenfalls nicht.
so und jetzt könnt ihr meinen code nach allen regeln der kunst verreißen
-
kompatibilität ist nie gänzlich überflüssig. und bis zu diesem zeitpunkt bist du ja auf die zielplattform nicht eingegangen (oder hab ich das übersehen?).
eine kleine anmerkung bzgl. int48:int48 (__int64 x) : lo32(x), hi16(x>>32) {}
das macht möglicherweise nicht das, was du willst, falls du mit überläufen zu tun hast (keine modulo 2^n arithmetik). auf x86 prozessoren ist modulo immer vorzeichen-bewahrend und bietet sich daher an:
int48 (__int64 x) : lo32(x), hi16((x%(1LL<<47))>>32) {}
-
natürlich ist kompatibilität nicht überflüssig. aber erstens ist sie, wenn 64-bit-integers verwendet werden, sowieso nicht gegeben, und zweitens ist die int48-klasse so klein, daß man sich mit der anpassung auch keinen zacken aus der krone bricht.
das macht möglicherweise nicht das, was du willst
es macht sogar genau das, was ich will. und das sogar platform-unabhängig. die höchstwertigen 16 bit werden abgeschnitten. das vorzeichen ist nur beim erweitern aber nicht beim abschneiden ein problem. aber deswegen ist hi16 in der int48-klasse auch signed short und nicht unsigned short. damit klappt dann bei operator __int64 auch die vorzeichenbehaftete erweiterung.
-
stimmt, denkfehler von mir.
-
kein problem
im übrigen ist es ja auch nur eine stilfrage. ich benutze gerne casts während du lieber mit mathematischen operationen arbeitest, die heutzutage jeder halbwegs brauchbare compiler in genauso effizienten code übersetzt.
-
Hmmm....
Mal ne frage.
Wenn die Ganzzahl zu gross ist fuer long int, wieso nicht einfach long long int nehmen? Und sich die sach mit 48bits sparen?static
In meinem Buch steht geschrieben, man sollte static vermeiden,
wenn man mit namespace arbeiten kann.Ghost
-
Green_Ghost schrieb:
static
In meinem Buch steht geschrieben, man sollte static vermeiden,
wenn man mit namespace arbeiten kann.Ghost
static hat zwei bedeutungen:
in funktionen und klassen bewirkt static, dass nur eine einzige variable existiert - und nicht wie im normalfall eins für jedes objekt, bzw. für jeden neuen funktionsaufruf. in diesem falle ändert sich nichts an der sichtbarkeit für den linker, statische klassenmember haben die selbe sichtbarkeit wie die klasse selbst, statische variablen in funktionen sind nach aussen nicht sichtbar (ausser im falle von inline funktionen - vague oder weak linkage).in verbindung mit globalen objekten oder funktionen bewirkt static dagegen, dass die betreffende instanz für den linker unsichtbar ist (internal linkage) und damit in jeder ÜE neu definiert werden darf - sie existiert dann aber eben auch mehrfach. und diese zweite form sollte nicht mehr verwendet werden - statt dessen gibt es unbenannte namespaces.