Converting a C++ GUID structure to NSString* in Objective-C++ - c++

I am trying to implement the following method in Objective-C:
- (NSString*) getGuidWithCid: (int) cid;
This method must retrieve a GUID from a database using a C++ API and then convert this GUID into a string representation of the GUID. The structure GUID returned by the C++ API has the following definition:
typedef struct _GUID {
ul_u_long Data1;
ul_u_short Data2;
ul_u_short Data3;
ul_byte Data4[ 8 ];
} GUID;
Here is my attempt to implement this method:
- (NSString*) getGuidWithCid: (int) cid
{
GUID ulguid;
((ULResultSet *) ulresultset)->GetGuid([cname UTF8String], &ulguid);
NSString* data4 = [[[NSString alloc] initWithBytes:ulguid.Data4
length:8
encoding: NSUTF8StringEncoding] autorelease];
return [NSString stringWithFormat:#"%u-%uh-%uh-%#",
ulguid.Data1,
ulguid.Data2,
ulguid.Data3,
data4];
}
The first two lines of this method are working fine, so the variable ulguid contains a valid guid. The problem I am having is with the conversion of the byte array portion of the GUID (ie. Data4 in the GUID structure). What I am getting back from this method currently is three numeric values separated by hyphens, so Data1, Data2 and Data3 seem to be represented correctly, but NULL is being shown for Data4. What am I doing wrong here?

You are calling release on data4 immedaitely after you create it. The contents of the string, therefore, are invalid at the point you try to use it. Consider calling autorelease instead.

It's possible that the bytes in ulguid.Data4 don't form a valid UTF8 string, so you get nil back when you try to create a string with those bytes.

I have successfully implemented this method as follows:
- (NSString*) getGuidWithCid: (int) cid
{
GUID ulguid;
((ULResultSet *) ulresultset)->GetGuid(cid, &ulguid);
char buffer[37];
sprintf(buffer, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
ulguid.Data1, ulguid.Data2, ulguid.Data3,
ulguid.Data4[0], ulguid.Data4[1],
ulguid.Data4[2], ulguid.Data4[3],
ulguid.Data4[4], ulguid.Data4[5],
ulguid.Data4[6], ulguid.Data4[7]);
return [[[NSString alloc] initWithBytes:buffer
length:sizeof(buffer)
encoding:NSASCIIStringEncoding] autorelease];
}
Thanks goes to Ferdinand Beyer (http://stackoverflow.com/users/34855/ferdinand-beyer) for helping me get to this solution.

Related

Using C++ protobuf formatted structure in leveldb. set/get operations

I'd like to make a POC of using leveldb in order to store key-value table of different data types in protobuf format.
So far I was able to open the database file, and I also saw the get function with the following signature :
virtual Status Get(const ReadOptions& options, const Slice& key, std::string* value)=0
I understand that the value is actually refers to a binary string like vector and not regular alphanumeric string, so I guess it can fit for multi type primitives like string, uint, enum) but how can it support struct/class that represent protobuf layout in c++ ?
So this is my proto file that I'd like to store in the leveldb:
message agentStatus {
string ip = 1;
uint32 port = 2;
string url = 3;
google.protobuf.Timestamp last_seen = 4;
google.protobuf.Timestamp last_keepalive = 5;
bool status = 6;
}
and this is my current POC code. How can I use the get method to access any of the variables from the table above ?
#include <leveldb/db.h>
void main () {
std::string db_file_path = "/tmp/data.db";
leveldb::DB* db;
leveldb::Status status;
leveldb::Options options;
options.create_if_missing = false;
status_ = leveldb::DB::Open(options, db_file_path, &db);
if (!status_.ok()) {
throw std::logic_error("unable to open db");
}
Thanks !
You need to serialize the protobuf message into a binary string, i.e. SerilaizeToString, and use the Put method to write the binary string to LevelDB with a key.
Then you can use the Get method to retrieve the binary value with the given key, and parse the binary string to a protobuf message, i.e. ParseFromString.
Finally, you can get fields of the message.

iOS How to Store C++ Object into a NSArray used as a tableview's datasource

Recently, I encountered a problem: in my tableview, I retrieve data from web request, after received response from the network, I need to decode the data and store the data in a C++ object.
However, the tableview datasource, NSArray cannot store C++ object.
I have tried some solutions, like using CFMutableArrayRef or save the C++ object pointer in a NSValue and then put the NSValue into the NSArray. But when I called [tableview reloadData],the data in the CFMutableArray or the NSValue don't have the right data.
Could anybody help me with this problem? Thanks!
Here are some code:
std::list<ImageTextContent> msgs = body->getMsgs();
std::list<ImageTextContent>::iterator itmsgs0;
for(itmsgs0 = msgs.begin();itmsgs0 != msgs.end();itmsgs0++)
{
ImageTextContent *tmpmsgs = &(*itmsgs0);
CFArrayAppendValue(_dataSourceRef, tmpmsgs);
}
and
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ImageTextContent *itc = (ImageTextContent *)CFArrayGetValueAtIndex(_dataSourceRef, 0);
NSString *title = [NSString stringWithCString:(itc->getSubmitTime()).c_str() encoding:NSUTF8StringEncoding];
cell.contentLabel.text = title;
....
}
in cellForRowAtIndexPath method I can't get the right data, why?
The pointers in your _dataSourceRef array become invalid as soon as the lifetime of msgs ends.
Dereferencing any of them is undefined at that point and anything can happen.
(Very common is that the data has been overwritten and you just find some random stuff there.)
Either make getMsgs return a list of pointers, or dynamically allocate a copy the data when you populate the array, like this:
for(itmsgs0 = msgs.begin();itmsgs0 != msgs.end();itmsgs0++)
{
CFArrayAppendValue(_dataSourceRef, new ImageTextContent(*itmsgs0));
}

stringValue of NSDictionary objectForKey runtime error

So I have the following code that causes a runtime error in XCode
NSString* temp = [[param objectforkey#"firstParam"] stringValue];
int tempNum = [[param objectforkey#"secondParam"] intValue];
param loads from a plist. firstParam is as string, secondParam is a number
First line crashes the program.
Now what's interesting is it works if I do a hard caste i.e:
NSString* temp = (NSString*)[param objectforkey#"firstParam"];
int tempNum = [[param objectforkey#"secondParam"] intValue];
Just wondering why the id would have inconsistent implementation in that I have to use intValue to cast to int, but have to do hard cast to get NSString? Why not stringValue?
Your first call to objectForKey returns an object which is an NSString. NSString doesn't have a stringValue method which is why a runtime error is generated. It doesn't make sense to get the stringValue of something that is already a string.
The second call to objectForKey returns an NSNumber. NSNumber does have an intValue method so it doesn't cause an error.
In the second case you are changing the type of the returned value from NSNumber to int. In the first case the object returned is already an NSString and there is no point trying to get the stringValue of a string.
The call [param objectforkey#"firstParam"] DOES return a NSString, so there's no need to call stringValue. Furthermore, NSString does not have a method called "stringValue", so that's the source of your problem.
Just call this: NSString *temp = (NSString *)[param objectForKey:#"firstParam"];
Also note that the selector is objectForKey:, not objectforkey: (capitalization matters).
And, answering your question, NSNumber is a class, so any object is not an "int", but a NSNumber pointer. intValue returns an actual "int", as floatValue returns a "float" (NSNumber can represent any type of number). In the case of the NSString that's not necessary, because there is only one type of NSString (there is, though, a method for returning a const char *, and that would be used like char *string = [[param objectForKey:#"firstParam"] cString];... not very useful for Objective-C apps anyway).
Lets say you call a web service that returns JSON
Then you process data
NSDictionary *json_response = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
now you try something like this
NSString *name = [[object objectForKey:#"Name"] stringValue];
then you get the error....
As mttrb mentions you get the error because of stringValue, objectForKey default return value is String so a potential fix in this case would be:
NSString *name = [object objectForKey:#"Name"];
Hope this hints some one!
If you can not be sure that the returned value is a NSString you should do this:
id value = [param objectForKey#"firstParam"];
if ([value isKindOfClass:[NSString class]]) {
NSString *temp = (NSString *)id;
//Handle string
}
else {
//Handle other case
}
Completing the answer of #Torge, I add my solution to get always an NSString from objectForKey method:
NSObject * value = [param objectForKey#"firstParam"];
NSString *stringValue;
if ([value isKindOfClass:[NSString class]]) {
//Handle string
stringValue = (NSString *)value;
}
else if([value isKindOfClass:[NSNumber class]]){
//Handle NSNumber
stringValue = [((NSNumber *) value) stringValue];
}
else {
//Handle other case
}

InternetCrackUrl not returning lpszHostName properly

I am developing a plugin for NSIS (Unicode) and I am trying to use InternetCrackUrl() to get the hostname of a URL (ie: http://www.google.com/test.html -> www.google.com) but instead of lpszHostName just returning "www.google.com", it returns "www.google.com/test.html".
Here is my code:
void __declspec(dllexport) Example(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra) {
g_hwndParent=hwndParent;
EXDLL_INIT();
LPWSTR szURI = new WCHAR[string_size];
URL_COMPONENTS urlComp;
// Sets szURI to "http://www.xyz.com/test.html"
popstring(szURI);
wstring strUri = szURI;
ZeroMemory(&urlComp, sizeof(urlComp));
urlComp.dwStructSize = sizeof(urlComp);
// Set required component lengths to non-zero so that they are cracked.
urlComp.dwHostNameLength = static_cast<DWORD>(-1);
urlComp.dwSchemeLength = static_cast<DWORD>(-1);
urlComp.dwUrlPathLength = static_cast<DWORD>(-1);
urlComp.dwExtraInfoLength = static_cast<DWORD>(-1);
if (!InternetCrackUrlW(strUri.c_str(), strUri.length(), 0, &urlComp)) {
return _T("InternetCrackUrl failed");
}
// urlComp.lpszHostName = www.xyz.com/test.html
}
Any ideas?
If you don't provide your own buffer InternetCrackUrl will return pointers to characters in the original string you pass as input. It doesn't copy the string.
So, lpszHostName will point to the first character, and dwHostNameLength will give you the number of chars that make the host name.
That is the expected behavior.
Because when you say www.google.com it translates to http://www.google.com/test.html.
The URL is really www.google.com/test.html which is what is returned.
To get what you need, you will need to do some string manipulation.
You could use the strrchr function or the find_first_of method of the std::string class.

Storing NSArray in UIPasteboard

I have several text files which I want to transfer between 2 Apps. (ie. free and paid versions of the same App).
I'm using UIPasteboard to do this. The contents of the files are held in memory as NSArrays, and so I want to copy these NSArrays to the pasteboard (lite version), and read them from the pasteboard (full version).
For some reason the data cannot be read back from the pasteboard. The data is being returned as a NSData object, rather than NSArray, which I think means that it is not in the required format for the pasteboard type I am using, which is "public.utf8-plain-text".
When I read/write NSStrings with this pasteboard type, it works fine.
I searched through Apple docs, etc, to see if there is a different type I should be using for NSArrays, (or other property list objects), but drew a blank.
Writing to the pasteboard: (In the following pDataOutput is an array of strings, file contents) :
NSMutableArray *lArrayCopy = [gGlobalData.cPasteBoard.items mutableCopy];
[lArrayCopy replaceObjectAtIndex:pDataFileIdx
withObject:[NSDictionary dictionaryWithObject:pDataOutput
forKey:#"public.utf8-plain-text"]];
gGlobalData.cPasteBoard.items = lArrayCopy;
[lArrayCopy release];
Reading from the pasteboard:
NSArray *lPBItems = [pPasteBoard valuesForPasteboardType:#"public.utf8-plain-text"
inItemSet:nil];
NSLog(#"PB Items = NSArray of count %d", lPBItems.count);
The above returns:
PB Items = NSArray of count 0
As mentioned above, it returns the data correctly as NSStrings if written as NSStrings.
Any help would be very much appreciated.
Thanks
Stephen C
I ran into the same issue and I think the valueForPasteboardType family of methods are broken and always return NSData.
Here is my solution:
NSArray * lArrayFromPasteBoard = [pPasteBoard valueForPasteboardType:#"com.my.custom.type"];
if ([lArrayFromPasteBoard isKindOf:[NSData class]])
{
lArrayFromPasteBoard = [[NSPropertyListSerialization propertyListWithData:(NSData*)lArrayFromPasteBoard options:0 format:0 error:0];
}
hopefully this will make it so the code in the if won't get called anymore once apple fixes their bug
As of iOS 8.3, UIPasteboard still has this bug. I wrote an extension for UIPasteboard to handle this:
extension UIPasteboard {
func arrayForPasteboardType(pasteboardType: String) -> NSArray? {
switch valueForPasteboardType(pasteboardType) {
case let array as NSArray:
return array
case let data as NSData:
if let array = NSPropertyListSerialization.propertyListWithData(data, options: 0, format: nil, error: nil) as? NSArray {
return array
}
default:
break
}
return nil
}
}