Unicode karakterek Rust --> C közötti átadásakor belefutottam egy érdekes hibaüzenetbe:
warning: `extern` block uses type `char`, which is not FFI-safe
--> src/main.rs:2:25
|
2 | fn teszt_c(charvec: *const char, len: usize);
| ^^^^^^^^^^^ not FFI-safe
|
= note: `#[warn(improper_ctypes)]` on by default
= help: consider using `u32` or `libc::wchar_t` instead
= note: the `char` type has no C equivalent
Érdekességképpen a rovásírás is a 17 bites tartományban, konkrétan az U+10C80
- U+10CFF
értékek között található, ahogy több távolkeleti ország karakterkészlete sem fér bele a 16 bites unicode-ba.
És akkor adódik a kérdés. Ha a Rust char típusa (unicode) az u32 széles, akkor a C nyelv wchar_t típusa az milyen széles valójában? Vannak olyan C fordítók, amely wchar_t típusa nem tudja befogadni a unicode értékeket?
Melyik C fordítóknál nem 32 bites a wchar_t?
- 685 megtekintés
Hozzászólások
https://en.cppreference.com/w/cpp/language/types
A notable exception is Windows, where wchar_t is 16 bits and holds UTF-16 code units
- A hozzászóláshoz be kell jelentkezni
Aix32: 16bit
Aix64: 32bit
Windows: 16bit
Azt hiszem, ahol 16bit van, ott utf-16 értendő (surrogate pairs).
- A hozzászóláshoz be kell jelentkezni
Jól meg van bonyolítva a C a rendszerfüggő és össze-vissza használt típusaival. A közelmúltban a hol 16 hol 32 bites int-et veséztük ki.
Az UCS-2 az, amikor 0..FFFF-ig csak a Unicode részhalmaza van a 16 bites változóban, az UTF16-ban ábrázolt betű viszont lehet több szón átnyúló. Ekkor hogyan működik a wcsncpy, wmemcpy és társai?
Egyre inkább úgy érzem, hogy a C hordozható, de csak ha előre felkészülsz, hogy mely rendszerek között akarod hordozni. Ellenkező esetben teli van csapdákkal.
- A hozzászóláshoz be kell jelentkezni
Egyre inkább úgy érzem, hogy a C hordozható, de csak ha előre felkészülsz, hogy mely rendszerek között akarod hordozni. Ellenkező esetben teli van csapdákkal.
Ez nem a "rendszer" függvénye, hanem a fordítóé, meg a környezeté. Az egységesített stdxyz.h
headerek a platformok többségén ugyanúgy használhatóak; pl. az [u]int#_t
típusok mindig olyan szélesek, ami rájuk van írva.
- A hozzászóláshoz be kell jelentkezni
A helyzet még érdekesebb:
$ gcc teszt.c -o teszt1 -fshort-wchar
$ clang teszt.c -o teszt2 -fshort-wchar
A gcc és clang esetén alapból 4 byte-os wchar_t. A fenti opcióval átdefiniálódik 2 byte-osra.
Miért kell tudni átdefiniálni? Lekövetik ezt a változást a wcsncpy() és egyéb wchar_t -s függvények?
Az átdefiniálás után a teljes Unicode karaktertábla szintén ugrott, csak az alsó 16 bit játszik.
- A hozzászóláshoz be kell jelentkezni
A gcc és clang esetén alapból 4 byte-os wchar_t.
Ez egy fordítóspecifikus eset. És én mit mondtam? Hogy a fordító és a környezet függvénye. Ez is erre egy példa.
Miért kell tudni átdefiniálni?
Azért, mert mind a gcc
, mind a clang
crossplatform fordítók és lehet, hogy olyan platformra fognak fordítani, ahol 16-bites a wchar_t
.
Lekövetik ezt a változást a wcsncpy() és egyéb wchar_t -s függvények?
Nem a függvényeknek kell "lekövetni" az átdefiniálást, hanem az átdefiniálás van azért, hogy azzal lehessen igazodni a célplatform függvényeihez.
Az átdefiniálás után a teljes Unicode karaktertábla szintén ugrott, csak az alsó 16 bit játszik.
És? Te mondtad neki, hogy definiálja felül. Ha a célplatformon 16-bites a wchar_t
és ennek megfelelően a wchar_t
-vel dolgozó függvények is 16 biten dolgoznak, az talán a C nyelv "hibája"? Nem inkább a célplatformé?
- A hozzászóláshoz be kell jelentkezni
Vettem egy 32-bites AIX-os programot, fogtam egy utf8-as stringet ("hambuger 🍔 emoji"), és megnéztem, mit mondd rá a mbstowcs. Hát, nem bírkózott meg vele, nem lett belőle 'surrogate pair'
- A hozzászóláshoz be kell jelentkezni
akkor a C nyelv wchar_t típusa az milyen széles valójában?
https://en.wikipedia.org/wiki/Wide_character#Programming_specifics
The width of wchar_t is compiler-specific and can be as small as 8 bits. Consequently, programs that need to be portable across any C or C++ compiler should not use wchar_t for storing Unicode text. The wchar_t type is intended for storing compiler-defined wide characters, which may be Unicode characters in some compilers.
- A hozzászóláshoz be kell jelentkezni
Ezek alapján akkor minek a tárolására alkalmas a wchar_t? Jó ez egyáltalán bármire? (Disclaimer: rohadt régen kellett cvel foglalkoznom)
- A hozzászóláshoz be kell jelentkezni
Az adott compiler és az általa biztosított környezet (pl. C library) által támogatott encodingok használatára jó. Ha ez limitált, akkor a programodban is limitált lesz a támogatás. Ha ez nincs (wchar_t = char), akkor dettó.
- A hozzászóláshoz be kell jelentkezni
nalam 22mm, most mertem le :)
- A hozzászóláshoz be kell jelentkezni
Ez ütött így a mai kígyóbűvölős munkanapom végén. :)
- A hozzászóláshoz be kell jelentkezni
Hat, erdekes ez az egesz wchar_t mizeria az utf-8 vilagaban... valojaban sosem ertettem teljesen hogy ezt minek. Marmint ertem en hogy villanymotor, de... akkoris.
- A hozzászóláshoz be kell jelentkezni
UTF8 és a Unicode nem ugyanaz. Lásd ezt az egyszerű példát.
- A hozzászóláshoz be kell jelentkezni
Jaja, az oke. Csak kerdes hogy hasonlo funkcionalitast nem egyszerubb-e akkor mar direkt utf8-on csapatni kozvetlenul - mint heurisztikus, onmagaval nem kompatibilis tipusokkal. Mivel egy utf8-sztringben a konkret szama megegyezik a (c>>6) != 2 karakterek szamval, ezert valojaban nem egy nagy mutavany ez. Meg ugye mar erre is vannak kesz fv-k, mint pl az mblen() es baratai.
- A hozzászóláshoz be kell jelentkezni
UTF8 előtti móka volt ez!
- A hozzászóláshoz be kell jelentkezni
Na akkor most ismét tanulok, mert a régi módszert ismertem eddig.
C-ben hogyan implementálod a karakterfeldolgozást?
Nem csak a szétcsapatásra, hanem tetszőleges Unicode karakterrel végzett műveletre, karaktercserékre és karakterbeszúrásokra is gondolok.
- A hozzászóláshoz be kell jelentkezni
Hat, ilyen karakterfeldolgozas (pl karakter beszurasa, csere) az igy sima C-ben sincs alapbol, csak libc-fuggvenyek amik ezt megkonnyitik ;) De azokat faek egyszeruen tudod implementalni barmire (utf-8 es sima 8-bit char-ra egyarant) es a fordito mar gondoskodik rola hogy a leheto leghatekonyabb legyen - beleertve azt hogy kicsereli memcpy meg hasonlo hivasokra ahol lehet es/vagy erdemes.
- A hozzászóláshoz be kell jelentkezni
A unicode-témát nem mindig tudod elkerülni. Itt egy mókás példa:
use std::io::{self, Write};
use std::{char, thread, time};
fn main() {
let delay = time::Duration::from_millis(500);
loop {
for x in 0..24 {
let unicodebase = 0x1F550 + 0x0C * (x % 2);
let ch = char::from_u32(unicodebase + x / 2).unwrap();
print!("\r{}", ch);
io::stdout().flush().unwrap();
thread::sleep(delay);
}
}
}
$ rustc pelda.rs
Nagyítsd fel a terminál betűit (ctrl +) és $ ./pelda
Vicces, mi mindent beletettek a unicode-táblába.
Érdekes, hogy nem csak névben van különbség a char és az u32 között. A char (unicode) az u32 értékkészleténél szűkebb értéket vehet fel,
sőt ki van hagyva egy közbenső rész a unicode táblából az UTF16 miatt. Lásd: https://rust.godbolt.org/z/a4dne16zo
- A hozzászóláshoz be kell jelentkezni