Milyen széles a wchar_t ?

Fórumok

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?

Hozzászólások

Szerkesztve: 2021. 05. 02., v – 21:13

Aix32: 16bit
Aix64: 32bit
Windows: 16bit

Azt hiszem, ahol 16bit van, ott utf-16 értendő (surrogate pairs).

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.

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 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 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é?

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.

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.

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.

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 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