简体   繁体   中英

Get unichar * from a C++ std::string& to create a nonnull NSString in Objective-C++

I have some Objective-C++ that wraps an existing C++ library. The C++ library occasionally gives me std::string& parameters that I want to convert into nonnull NSString s. Almost all of the NSString initializers return nullable NSString s except: -initWithCharactersNoCopy:length:freeWhenDone: and -initWithCharacters:length: . However, both of those require unichar * , but std::string.c_str() returns char * .

How can I get a unichar * from a C++ std::string so that I can create an NSString * _Nonnull ?

Not a duplicate

Other questions simply want to convert from std::string to NSString . I want to know if it's possible to do so without producing a null NSString * , possibly by calling methods on std::string to get unichar * . std::wstring_convert looks promising, but I'm not a C++ developer, so I don't know how to get started with that yet.

Possible solution

After researching further, I learned that std::string is just a std::basic_string<char> , and it seems like you can define your own std::basic_string s. I found a similar example that converts to std::wstring :

// std::string -> std::wstring
std::string s("string");
std::wstring ws;
ws.assign(s.begin(), s.end());

So I adapted it to std::basic_string<unichar> , and it compiles fine:

void Foo(const std::string& bar) {
  std::basic_string<unichar> barUnichar;
  barUnichar.assign(bar.begin(),
                    bar.end());
  NSString * _Nonnull barNSString = 
  [NSString stringWithCharacters:barUnichar.c_str() 
                          length:barUnichar.length()];
  NSLog(@"bar: %@", bar);
}

I don't mind that converting from std::string to std::basic_string<unichar> performs a needless copy, and I imagine I can change the above code to use -[NSString initWithCharactersNoCopy:length:freeWhenDone:] once I learn more about C++ memory ownership rules.

Possible solution is no good

Joe Groff says on twitter :

That's going to do a byte-by-byte mapping. It may work for ASCII but will give you garbage for any Unicode.

Let's try again, see if it helps you. You have edited the question in response to the suggestion it is a duplicate and added:

Other questions simply want to convert from std::string to NSString . I want to know if it's possible to do so without producing a null NSString * ...

The direct answer: No

The reason is straightforward: The Objective-C library cannot assume that any pointer passed to it references a validly encoded C string, even if that pointer is typed std::string .

Now you may be very confident that your C++ code will never pass you an invalidly encoded string – and it can be argued that confidence is reasonable (but then it is C++ after all ;-)) – and so believe the null result will never happen, but that does not change the fact that the Objective-C library cannot assume it won't.

The DIY answer: Yes

Trying to avoid the null, eg by trying to convert the std::string into a unichar * etc. as you have considered, just avoids the issue - some piece of code somewhere has to deal with checking the encoding or risk returning an invalidly encoded NSString .

While it might be possible to do this it will both be more involved, and may leave you not knowing what is returned for invalid encodings, compared to the DIY approach: just deal with the null return yourself at source and replace it by something else. For example:

std::string someCstring;

NSString *convertedString = @(someCstring.c_str()) ?: @"ERROR: C string is invalid UTF8";

Here convertedString will never be nil .

(If your C string is not UTF8 you will need to use another NSString initialiser which takes an encoding.)

HTH

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM