wchar_t[] in char* casten
-
Hallo!
Folgendes Minimalbeispiel:#include <iostream> struct buf { char* ptr; }; void f(buf* b) { std::cout << b->ptr << std::endl; } int main() { wchar_t x[] = L"Hello, UTF-16!"; buf b; b.ptr = reinterpret_cast<char*>(x); f(&b); std::cin.get(); }
(Dass das std::cout mit wchar_t keinen Sinn ergibt weiß ich, im echten Problem werden die Bytes des Arrays nicht interpretiert, wie es ein Stream tut).
Das Problem: Ich will einer API-Funktion eine best. Struktur übergeben, die einen char-Pointer enthält. Wenn ich aber nun ein wchar_t-Array habe, will ich einfach durch array-to-pointer-decay und reinterpret_cast<> den wchar_t[] auf char* biegen. Leider gibt mir die Funktion nur den allerersten Buchstaben aus, der Debugger zeigt auch kein ganzes Array, sondern nur diesen einen Buchstaben. Wie komme ich auf diese Weise an das ganze Array ran?
-
So geht das einfach nicht, allein schon weil
char
undwchar_t
unterschiedlich groß sind. Du könntest allerdings die Zeichen einzeln casten:char
-Array anlegen, denwchar_t
-String durchgehen und jedes Zeichen mitstatic_cast
unwandeln.
-
Zuerst einmal, wieso deine aktuelle "Lösung" nicht funktioniert:
wchar_t
ist grösser alschar
. Auf Windows sind es zum Beispiel 2 Bytes. Dann ist der erstewchar_t
Wert von deinen String-Literal wahrscheinlicher dieser: 0x0048. Windows läuft als Little-Endian. Wenn wir nun den Zeiger als Verweis aufchar
betrachten, welches bekanntlich jeweils aus einem Byte besteht, dann bekommen wir die folgenden zwei Werte für deinwchar_t
Wert: 0x48 0x00. Das ist ein nullterminierter String mit dem Buchstaben 'H'.Tatsächliche Lösung, falls du die Funktion
f
verändern darfst:
std::wcout << reinterpret_cast<wchar_t*>(buf->ptr) << std::endl;
Falls dies nicht geht, dann musst du den String halt vorher umwandeln:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-168607.html
Es gäbe auch noch plattformspezifische Funktionen. Auf Windows zum BeispielWideCharToMultiByte
.Und noch eine Anmerkung zum Schluss:
L"Hello, UTF-16!"
Das stimmt so ziemlich sicher nicht. Auf Windows wäre es UCS-2, auf Linux ist es, glaub ich, UTF-32 (wchar_t
ist auf Linux 4 Bytes gross). Der aktuelle Standard macht keine genaue Aussage überwchar_t
.Grüssli
-
Windows verwendet fast überall UTF-16, nicht UCS-2.
-
> Tatsächliche Lösung, falls du die Funktion f verändern darfst:
Darf ich eben nicht, da es eine API-Funktion (Winsock 2) ist. Es geht genauer um WSASend(), eine Funktion die eine Struct entgegen nimmt, welche einen char*-Pointer und ein int-Wert mit der Größe des Datenblocks enthält. Dabei werden die Bytes des Feldes, ich wiederhole, NICHT interpretiert. Ob da nun zufällig ein 0-Zeichen drin steckt, ist der API eigentlich Jacke wie Hose, es verschickt eben nur die reinen Bytes. Ich muss nichts weiter tun, als die Anzahl der Zeichen mit sizeof(wchar_t) zu multiplizieren, damit der Block komplett gelesen wird. Nur, macht er das natürlich nicht. Das ist doch nur eine Interpretationssache, ob man nun eben wchar_t-Bytes einzeln als char-Bytes teilt und übergibt. Wieso kann ich von diesem gecasteten Zeiger einfach nicht auf die nächsten Elemente zugreifen?
buf->ptr + 1 * sizeof(wchar_t); buf->ptr + 2 * sizeof(wchar_t); buf->ptr + 3 * sizeof(wchar_t); buf->ptr + 4 * sizeof(wchar_t); ...
Wenn es sowas wie WSASendW() geben würde, hätte ich hier das Problem ja nicht.
-
hustbaer schrieb:
Windows verwendet fast überall UTF-16, nicht UCS-2.
In den API Funktionen ja, aber soweit mir bekannt ist, sind die String-Literals UCS-2. Habe mich da aber auch schon länger nicht mehr dazu informiert, kann mich auch täuschen
@Ad aCTa,
Was du schreibst, macht alles nicht sehr viel Sinn und hat in meinen Augen keinen Zusammenhang. Wenn du denwchar_t
String mit WSASend verschicken willst, geht das so:wchar_t buf[] = L"Hello World"; std::size_t const bufSize = sizeof(buf) / sizeof(wchar_t); WSABUF wsaBuffer; wsaBuffer.len = bufSize; wsaBuffer.ptr = reinterpret_cast<char*>(buf); WSASend(socket, &wsaBuffer, 1, &sentBytes, 0, NULL, NULL);
Wo ist nun das Problem daran?
Wieso dein Debugger bei wsaBuffer.ptr nur den ersten Buchstaben ausgibt, habe ich weiter oben schon erklärt. Was du im Zusammenhang mit WSASend mit diesem Satz meinst "Leider gibt mir die Funktion nur den allerersten Buchstaben aus." ist mir schleierhaft.
Werd mal genauer in der Problem- und Fehlerbeschreibung.Grüssli
-
> Was du im Zusammenhang mit WSASend mit diesem Satz meinst "Leider gibt mir die Funktion nur den allerersten Buchstaben aus." ist mir schleierhaft.
Damit meinte ich f(), nicht WSASend(). Aber WSASend() macht prinzipiell das selbe: es verschickt einfach nur das eine einzige, erste Byte. Was ich nicht verstehe ist:
std::size_t const bufSize = sizeof(buf) / sizeof(wchar_t);
Wenn der String nun 30 Bytes groß ist (2 Bytes pro Charakter) und du ihn durch sizeof(wchar_t) (2) teilst, bleiben da nur noch 15 Bytes (7 Charakter und ein halbes, falsches), du versendest da ja nur den halben String!? So wie du es geschrieben hast, wollte ich es einfach machen. Klappt aber nicht.
-
Dravere schrieb:
hustbaer schrieb:
Windows verwendet fast überall UTF-16, nicht UCS-2.
In den API Funktionen ja, aber soweit mir bekannt ist, sind die String-Literals UCS-2.
Hä?
Was die (Wide-)String-Literals sind ist AFAIK implementation-defined. Mal angenommen der MSVC frisst überhaupt Unicode Source Files (hab ich nie ausprobiert), dann würde es mich sehr wundern, wenn er UCS-2 Literals draus machen würde, wo Windows doch (fast) alles als UTF-16 interpretiert.
-
@Ad aCTa:
WSASend ist es natürlich egal was du verschickst.
Aber wo genau hast du nun ein Problem, verstehe das nicht ganz.Du musst natürlich immer das verschicken, was der Empfänger auch erwartet. Wenn der Empfänger z.B. einen String in Latin-I erwartet, dann musst du auch Latin-I senden. Also einzelne "char"s, nicht "wchar_t"s.
Wenn du also einen std::wstring bzw. ein wchar_t Array hast, dann musst du den String vor dem Verschicken konvertieren. Einfach casten reicht da nicht.Wenn du die Empfangende Seite auch selbst programmierst, dann musst du dem Empfänger natürlich beibringen wchar_t Strings zu verwenden.
-
> Einfach casten reicht da nicht.
Ich will ja auch nur den Zeiger casten, nicht die Nutzdaten. Du muss ich nicht casten, ob das nun wchar_ts, chars oder was weiß ich sind ist erst mal vollkommen egal, WSASend schickt es einfach in den Äther und gut ist. Welcher Charset verwendet wird, wird vorerst auch vom Protokoll geregelt. Für ANSI funktioniert alles, nur nicht für Unicode. Ich mache genau das, was Dravere gepostet hat, und dennoch verschickt WSASend() nur dieses eine einzige Byte und nicht alle. Ich bin wirklich ratlos.
-
WSASend verschickt soviel Bytes wie du sagst.
Warum glaubst du dass nur 1 Byte verschickt wird?BTW: das
sizeof(buffer)/sizeof(wchar_t)
ist natürlich falsch, das wäre die Anzahl der "wchar_t"s, nicht die Anzahl der Bytes. Also einfach nursizeof(buffer)
bzw.wcslen(str) * sizeof(wchar_t)
falls du mit nem Zeiger arbeitest.
-
Das Problem liegt mit ziemlicher Sicherheit nicht an diesem Cast.
Auch dein allererstes Programm funktioniert doch wie erwartet. Wenn du im Debugger
b->ptr
b->ptr+1
b->ptr+2
b->ptr+3eingibst, und dich innerhalb von f() befindes, zeigt er dir doch wohl "H", "", "e", "", usw. ... an, oder?
Wie berechnest du denn die Pufferlänge für die WSABUF-Struktur?
Wieviel Bytes zeigt dir WSASend() als gesendete Bytes an?
Tauchen Fehler auf?
Jedenfalls würde ich mich nicht in diesen Cast verbeißen. Der ist schon OK soweit.
-
Ad aCTa schrieb:
Wenn der String nun 30 Bytes groß ist (2 Bytes pro Charakter) und du ihn durch sizeof(wchar_t) (2) teilst, bleiben da nur noch 15 Bytes (7 Charakter und ein halbes, falsches), du versendest da ja nur den halben String!?
Vergiss das Teilen durch
sizeof(wchar_t)
. War gestern seehr müde, 20 Minuten später war ich bereits im Betthustbaer schrieb:
Was die (Wide-)String-Literals sind ist AFAIK implementation-defined.
Das ist klar, habe ich ja schon selber ca. gesagt.
hustbaer schrieb:
Mal angenommen der MSVC frisst überhaupt Unicode Source Files (hab ich nie ausprobiert), dann würde es mich sehr wundern, wenn er UCS-2 Literals draus machen würde, wo Windows doch (fast) alles als UTF-16 interpretiert.
Ich dachte, dass ich sowas vor vielen Jahren mal gelesen habe, dass die Wide-String-Literals im MSVC UCS-2 sind. War glaub ich noch mit dem VS2003. Wäre aber mal interessant dies für die aktuellen MS Kompiler abzuklären. Geht hier aber ziemlich ins Off-Topic
@Ad Acta
Wie empfängt du die Bytes denn wieder? Ist der Empfänger auch von dir geschrieben? WSASend hat nämlich nichts mit irgendwelchen Zeichen am Hut. Es verwendetchar*
einfach nur als Zeiger auf Bytes.Grüssli