wchar_t и Cocoa

написал Владимир Пузанов - August 6, 2008 – 13:07

Пару дней назад мне пришлось интеграчить C++ код в проект на Cocoa. Помимо всех прелестей плюсов, целевая либа еще и использовала wchar_t для хранения строк. По идее, при sizeof(wchar_t)==4 там должен быть UTF32 (NSString внутри оперирует UTF16 через unichar). Конечно в Cocoa есть методы для конвертации кодировок, но, почему то, они не захотели работать ожидаемым образом.

Для начала разберемся с выводом wchar_t* из NSString. Для этого мы будем использовать следующую категорию:

@interface NSString (WideCharOps)
	- (wchar_t *)getWideChars;
@end

Всю основную работу выполнит getCString:maxLength:encoding:, кроме того момента, что в конце не обнулены три байта:

- (wchar_t *)getWideChars
{
	unsigned long len = [self length];
	wchar_t *buffer = new wchar_t[len+1];
	assert([self getCString:(char*)buffer maxLength:(len+1)*sizeof(wchar_t) encoding:NSUTF32StringEncoding]);
	buffer[len] = L'\0';
	return buffer;
}

их приходится явно нуллить через buffer[len] = L’\0′.
Как видите, конверсия в эту сторону работает крайне просто и без проблем, при этом имея всего один подводный камень.
К сожалению ввод UTF32 строки в NSString мне сходу не удался. Перепробовав все мыслимые и немыслимые методы (и даже убедив GCиспользовать короткий wchar_t через -fshort-wchar), я пришел к выводу, что придется разбирать строку самому. К счастью вам не придется лезть в дебри кодировок, так как на сайте уникода мною был найден замечательный исходник: ConvertUTF.c. Забираем его и парный хедер из http://www.unicode.org/Public/PROGRAMS/CVTUTF/ и добавляем в проект. Теперь Можно расширить нашу категорию методом

+ (NSString *)stringWithWideChars:(const wchar_t *)src;

который реализовывается следующим образом:

+ (NSString *)stringWithWideChars:(const wchar_t *)src
{
	size_t len = wcslen(src);
	unichar *dst = (unichar*)malloc(len*sizeof(unichar)+1);
	const char *src_end = (char*) (src+len+1);
	unichar *dst_end = dst+len+1;
	unichar *dst_start = dst;
 
	ConversionResult res;
	ConversionFlags flags=strictConversion;
	res = ConvertUTF32toUTF16((const UTF32**)&src, (const UTF32*)src_end, (UTF16**)&dst, (UTF16*)dst_end, flags);
	assert(res == conversionOK);
	NSString *n = [NSString stringWithCharacters:dst_start length:len];
	free(dst_start);
	return n;
}

Все заморочки связаны с выделением временного буфера dst, в котором мы производим конверсию UTF32 -> UTF16. А UTF16 это уже валидная строка для stringWithCharacters:length:.
Вот таким нехитрым образом можно производить интеграцию кода на wchar_t* в проекты Cocoa.

Написать комментарий