Portál AbcLinuxu, 6. května 2025 14:50
UnicodeString( const WideChar chrFrom, unsigned int count = 1 ); UnicodeString( const AnsiString& strFrom, CodePage::CodePage codepage = CodePage::locale );kdežto v BaseString je konstruktor jen jeden
BaseString( const T chrFrom, unsigned int count = 1 );
./Toolkit/String.h:125: note: candidate 1: Toolkit::UnicodeString::UnicodeString(WideChar, unsigned int) ./Toolkit/String.h:126: note: candidate 2: Toolkit::UnicodeString::UnicodeString(const Toolkit::AnsiString&, Toolkit::CodePage::CodePage)Když tam je jasně v deklaraci i definici
const WideChar
. L'=' je const wchar_t, takže mu dávám wchar_t. I když změním UnicodeString( const WideChar, unsigned int) na UnicodeString( const wchar_t, unsigned int) stejně nadává. A ještě navíc je ten typedef v namespace Toolkit, proč nenadává s Toolkit::WideChar, když k AnsiString to namespace přidal?
char
.
Jen na první pohled. Zkuste si přeložit
Toolkit::AnsiString(L'x');
Překladač to spolkne, protože wchar_t
lze implicitně konvertovat na char
a poslední konstruktor BaseString
umožňuje použít jeden parametr typu char
. Je to sice dost krkolomné oproti té konverzi, kterou jste automaticky předpokládal, ale je to korektní. Ta hláška kompilátoru vás upozorňuje, že přestože jedna varianta je horší, podle ISO normy musí překladač považovat obě za přípustné a vyhodit chybu. Nechce se mi teď hledat, jestli to tam skutečně je, ale předpokládám, že autoři gcc se podívali.
iconv
.
iconv
je pod LGPL, takže při dynamickém linkování není problém.
Další věcí je, že jsem zjistil, že z nějakého mně neznámého důvodu je v linuxu velikost wchar_t rovna 4, kdežto ve windows 2. Jako kdyby těch problému nebylo dost.
V čem je problém? Typ wchar_t
není zamýšlen pro předávání dat mezi různými platformami, je to typ pro interní reprezentaci Unicode řetězců. Konverzi na přenositelná kódování vám vyřeší automaticky např. operátory <<
a >>
pro zápis do widestreamů a čtení z nich.
Pokud bych ale místo wchar_t použil třeba unsigned int, ve Win to proleze, jenže linuxovému g++ zase vadí že L"Bla" je wchar_t*. Takže zatím nechávám wchar_t a v Linuxu bude hold "široký" řetězec zabírat dvojnásobné místo v paměti.
Nechápu, co byste tím chtěl ušetřit. Poslední platforma, na které jsem zažil, že měl int
méně než 32 bitů, bylo Borland C++ 4.5, a to ještě jen při v režimu kompilace 16-bitových aplikací pro DOS nebo Windows 3.1.
short
má 16 bitů, ani že bude na různých platformách stejně velký. Za druhé vám 16-bitový typ nemusí být schopen pojmout všechny potřebné znaky. Že se ve Windows většinou používá UCS-2 a říká se mu UTF-16, to je spíše politováníhodné, rozhodně bych se nesnažil tuto praxi rozšiřovat na další platformy.
uint find_char(const wchar_t* s, uint length, wchar_t uc) { uint i; for (i = 0; i != length; i++) { if (s[i] == uc) return i; } return uint(-1); }který je ovšem platný ve windows 2000 (používá UCS-2), ale ve windows XP už platný není, protože winxp používá UTF16 a funkce neobsahuje surrogate pair testing (a k čemu by nám vlastně byl, když vstup je wchar_t?). Moje řešení znamenalo si napsat svůj string který bude vždy 32 bit. Pokud by autor blogy chtěl, rád pošlu (implementace stringu není hotová za 5 minut a já mám jsem doufám "skoro?" všechny mouchy už chytl). Ke stringu mám hotové i převádění mezi 8 bit formáty a unicode like formáty, vše v licenci BSD. Napsal jsem to pro vlastní toolkit o kterém byla diskuze v minulém blogu.
long long - 64 bitů vždyTo bych netvrdil. Až budou 128-bitové platformy, může být klidně 128 bitů
Svět je mnohem různorodější, než si ho představujete. Stejně jako před pár lety všichni nezodpovědní programátoři brali jako samozřejmost, že long
má vždy a všude 32 bitů a vždy také mít bude, berete vy dnes za nezvratitelnou pravdu, že int
má a vždy bude mít 32
bitů a long long
64 bitů. Tak tomu ovšem být nemusí a ve svých programech byste na to spoléhat neměl. Jinak budou za pár let na vaši hlavu chrlit sprostá slova ti, kdo je po vás budou muset opravovat, stejně jako já dnes chrlím nadávka na adresu těch, kdo psali a píší své programy s představou, že vždy a všude platí a bude platit sizeof(int) == sizeof(long) == sizeof(void*) == 4
.
Pokud nevěříte, podívejte se do Kernighana a Ritchieho, Appendix A, sekce 2.6, kde jsou uvedeny příklady velikostí datových typů. Je tam mimo jiné uveden počítač Honeywell 6000, na němž má typ short
velikost 36 bitů. Jistě, ta kniha už má svá léta, ale na to, že by se nenašla současná platforma s 32-bitovým short
, bych si rozhodně nevsadil. Navíc i ISO C99 definuje konstantu CHAR_BITS
, o jejíž hodnotě říká pouze to, že má být aspoň 8.
Jak píšete datové struktury kde chcete 16 bit integer?
Použiju int16_t
a doufám, že to nikdy nikdo (a zejména ne já) nebude muset portovat na platformu, kde tento typ není definován. On totiž podle specifikace definován být nemusí - na platformách, kde žádný 16-bitový celočíselný typ neexistuje. Naštěstí to zase tak často potřeba není - v podstatě jen tehdy, ukládají-li se data do souboru (v binárním formátu) nebo konstruují-li se hlavičky síťových protokolů.
Na platformě, která nemá 16 bitový typ a kompilér ho neumí prostě máme smůlu a většína 32 a 64 bit platforem chápe short jako 16 bitový typ.
Ne, chápe ho tak většina těch, kterým říkám empiričtí programátoři. Tedy ti, které nezajímá nějaká specifikace jazyka a kteří považují program za funkční, pokud jim to chodí. Takoví lidé by zasloužili naplácat přes ruce.
Pokud budou 128 bit procesory, tak budelong
avoid*
asi 128 bit (i když "zatím" neznám význam tak velké adresy) nebo vznikne nový typ.
Těžko odhadovat, long
asi ano, jestli i void*
, to je otázka. Osobně ale zastávám stanovisko, že je to stejně jedno, protože přetypovávání mezi pointery a celočíselnými typy je čuňárna těžkého kalibru. Uvědomte si, že specifikace vám vůbec negarantuje existenci celočíselného typu o stejné velikosti jako pointer - dokážu si docela dobře představit, že by na x86 byl pointer 48-bitová hodnota (16 bitů selektor, 32 bitů offset) a slušně napsané programy by se s tím vypořádaly.
Ale uznávám, že do struktury je opravdu lepší int16_t, i když nevím jestli si ten typedef někdy člověk nebude muset udělat sám
Proč struktury? Struktury si vám překladač stejně může v paměti srovnat, jak se mu bude hodit. Jak už jsem napsal, jediné situace, kdy skutečně potřebujete znát velikosti datových typů, je jejich předávání mezi platformami, ať už přes soubor nebo po síti, případně ještě přímá komunikace s hardwarem. Znáte-li nějakou jinou, sem s ní.
Ještě jsem zapoměl dodat, že pokud se jedná o kompatibilitu, tak změna typu char by podle mého názoru znamenala zboření 99% programů.
Slušný programátor nebude o typu char
předpokládat nic víc, než že má aspoň 8 bitů, (tj. pojme rozsah klasického ASCII plus "horní polovinu tabulky", že jeho velikost odpovídá nejmenší adresovatelné jednotce paměti a že sizeof()
vrací hodnoty v násobcích velikosti tohoto typu. Ti ostatní si za své problémy mohou sami. To je totéž jako programátoři, kteří si zjistí, že na jejich platformě je char
znaménkový, a na základě toho předpokládají, že je to tak všude.
to je to samé jak když někdo říká že je hřích gotoŽe je
goto
hřích se říkalo v dobách, kdy se přecházelo z Basicu a kdekdo cpal goto
úplně všude. goto
má svůj význam, a např. v linuxovém jádru se vyskytuje celkem hustě.
Asi jste slušný programátor jenom Vy Řekněte mi že jste nikdy nepočítal s tím, že char má 8 bitů a nebudu Vám věřit.Nikdo není dokonalý. Ale spoléhat běžně na to, že něco bude tak či onak, když to není nikde specifikováno (a je to vlastně jen "náhoda", že to tak je), je skutečně cestou do pekel. Od toho existují datové typy s pevnou délkou (
int16_t
apod.), i když jsou bohužel nepřenositelné.
Mimochodem existuje ještě mnohem větší čuňárna, kterou člověk také občas vidí, a to nevhodné nakládání s typy, jako je třeba pthread_t
. Takže už jsem viděl, že někdo porovnával thready stylem t1 == t2
(kde t1
a t2
jsou typu pthread_t
), což je hrubě nekorektní.
pthread_t
vůbec znamená. V současné linuxové implementaci (NPTL, ale myslím že i LT) je to pointer na strukturu pthread
, která se používá uvnitř libpthread
. Ovšem může to být obecně cokoliv, třeba nějaká struktura, index v poli, nějaký hash apod. Pro některé datové typy by porovnání nebylo vůbec možné.
Ale to není to podstatné. Zmíněné porovnávání především není korektní, protože sice bude rovnost hodnot pthread_t
znamenat, že se jedná o totéž vlákno, ale obráceně to rozhodně platit nemusí. Mohou být dva různé identifikátory odkazující na totéž vlákno. Pak nebude pro stejné vlákno platit t1 == t2
.
Proto je jedinou korektní cestou pro porovnání vláken volání funkce pthread_equal()
.
přetypování mezi pointery se dělá docela často a je to někdy nezbytné (to je to samé jak když někdo říká že je hřích goto), abych uvedl příklad už v knihovně C tak se podívejme na funkci sprintf()
Nemluvil jsem o přetypování mezi pointery na různé typy, tomu se v C opravdu nevyhnete. Mluvil jsem o přetypovávání mezi pointery a celočíselnými typy. Tedy když se proměnné typu void*
zneužívají k ukládání hodnot typu int
nebo long
- a nebo naopak. Takové věci jsou bohužel v programech poměrně časté a pro ty, kdo se je snaží portovat na platformy, kde mají tyto typy různou velikost, je to noční můra.
Zarovnávání struktur jde potlačit a někdy to má svůj význam, například když chci šetřit místo
Existují platformy, které vás např. nenechají uložit 32-bitové číslo na adresu, která není dělitelná čtyřmi. A nejde o nic exotického nebo dávno zapomenutého. Ruku do ohně bych za to nedal, ale mám pocit, že příkladem je třeba Sparc.
ad char) Asi jste slušný programátor jenom VyŘekněte mi že jste nikdy nepočítal s tím, že char má 8 bitů a nebudu Vám věřit.
Dříve jsem takové věci skutečně z neznalosti dělával. Dnes už o problematice vím víc, takže bych to použil jen v případě opravdu krajní nouze.
void* a = 0xdeadbeef; printf("%p\n", a);
Ve funkci vprintf, nebo ještě dál se argument stejně přetypuje na long (nebo typ který velikostně odpovídá pointeru) a pracuje se s ním jako s integerem, třeba unsigned long
Já programuji hodně dlouho a problém s velikostí char jsem opravdu ještě neměl. Nevím co je krajní nouze, ale myslím že většina knihoven počítá s tím, že char je 8 bit.
To zarovnání zápisu a čtení na 32 bit o kterém mluvíte opravdu existuje, ale řeší to sám kompilér (ale jestli je to sparc opravdu neřeknu)
No nic, ještě musím projít nové blogy#include <stdio.h> #include <stdarg.h> int args(const char* fmt, ...) { const char* p; va_list ap; va_start(ap, fmt); for (p=fmt; *p; p++) { void* ptr; int num; switch(*p) { case 'p': ptr = va_arg(ap, void*); printf("%p\n", ptr); break; case 'i': num = va_arg(ap, int); printf("%d\n", num); break; default: va_end(ap); return -1; } } va_end(ap); return 0; } int main() { int n = 0; const char* s = "ahoj"; if (args("pipi", s, n, &n, 3) < 0) return 1; return 0; }
void writeU64(uint64 value); void writeU32(uint32 value); void writePtr(const void* value);Pokud mám vlastní funkce pro převod
uint64
a uint32
tak je přece zcela logické že const void*
přetypuju podle platformy buď na int32 nebo na int64.
dělal bych to asi takto:
inline void writePtr(const void* value) { if (sizeof(void*) == sizeof(uint64)) { writeU64( (uint64)value ); } else if (sizeif(void*) == sizeof(uint32) { writeU32( (uint32)value ); } else { ASSERT(1); } }Toto je přetypování ukazatele a šlo mi pouze o to, že nazývat to čurárnou mi přijde ujeté, jinak věřím že jste to myslel dobře.
Ne, pořád nevím, kam tím míříte. Tvrdím, že funkci typu printf()
zvládnete bez problémů implementovat, aniž byste musel přetypovávat pointer na celé číslo nebo naopak.
Já bych to tak nedělal. On tento způsob zápisu binárních hodnot je jednak sám o sobě problematický (kdyby nic jiného, musíte řešit problémy s přenosem mezi big-endian a little-endian platformami), jednak mne nenapadá žádný důvod, proč do streamu ukládat pointer. A i kdybych se k tomu rozhodl, udělám to raději takto:
const std::ostream& operator <<(std::ostream& s, void* const& p) { return s.write(&p, sizeof(p)); }
Jak už jsem upozornil jednou, nemáte absolutně žádnou záruku, že existuje vůbec nějaký celočíselný typ, který má stejnou velikost jako pointer. A stejně tak nemáte žádnou záruku, že existuje celočíselný typ o velikosti právě 32 resp. 64 bitů.
To je sice možné, ale nic to nemění na skutečnosti, že takový přístup je nevhodný, protože výrazně snižuje přenositelnost projektu. Ostatně, výtky na adresu určitého "vytváření si svého vlastního světa" u (některého) GNU software už jsem viděl mnohokrát…
Ne, endianness musíte řešit pokaždé, když do streamu (ať už je to soubor nebo síťová komunikace) ukládáte data v binární podobě. Tedy samozřejmě kromě případů, že stoprocentně víte, že veškeré aplikace, která ta data budou sdílet, budou pouze jednoho typu (LE nebo BE). Ale to jsme zase tam, kde jsme byli - u přenositelnosti.
int32_t a
kde nemusím řešit endiannes nebo pokud pracuji s char a[sizeof(int32_t)]
.
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.