0

I'm trying to add fuzzy search to an iOS App I have built using an existing C++ implementation of double_metaphone by Maurice Aubrey http://aspell.net/metaphone/. I've renamed his main class double_metaphone.mm, set it to File Type C++ Source and got the project to build.

However I am getting a EXC_BAD_ACCESS error after I call the DoubleMetaphone method. Most of my App uses ARC and I see a number of memory management things in the double_metaphone.mm class. That said I am most suspicious of how I'm trying to get my answer back from the method. Here is the relevant part of double_metaphone.h (I enclose the method declaration in extern "C" as per Calling C++ method from Objective C).

void
DoubleMetaphone(const char *str,
            char **codes);

Here are relavant parts of the method implementation in double_metaphone.mm

void
DoubleMetaphone(const char *str, char **codes)
{
int        length;
metastring *original;
metastring *primary;
metastring *secondary;
int        current;
int        last;

current = 0;
/* we need the real length and last prior to padding */
length  = strlen(str); 
last    = length - 1; 
original = NewMetaString(str);
/* Pad original so we can index beyond end */
MetaphAdd(original, "     ");

primary = NewMetaString("");
secondary = NewMetaString("");
primary->free_string_on_destroy = 0;
secondary->free_string_on_destroy = 0;

MakeUpper(original);

/* skip these when at start of word */
if (StringAt(original, 0, 2, "GN", "KN", "PN", "WR", "PS", ""))
current += 1;

/* Initial 'X' is pronounced 'Z' e.g. 'Xavier' */
if (GetAt(original, 0) == 'X')
  {
  MetaphAdd(primary, "S");  /* 'Z' maps to 'S' */
  MetaphAdd(secondary, "S");
  current += 1;
  }

/* main loop */
while ((primary->length < 4) || (secondary->length < 4))  
  {
  if (current >= length)
      break;

  switch (GetAt(original, current))

.....

a whole bunch of cases for each possible characters with if statements to pick out the different fuzzy codings and adding letters to primary and secondary as they are identified.

.....

if (primary->length > 4)
SetAt(primary, 4, '\0');

if (secondary->length > 4)
SetAt(secondary, 4, '\0');

*codes = primary->str;
*++codes = secondary->str;

DestroyMetaString(original);
DestroyMetaString(primary);
DestroyMetaString(secondary);
}

Finally here is the snippet of my code that calls this method. I import the double_metaphone.h and then in a method do the following:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Filtered Word";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
    Word *word = [self.fetchedResultsController objectAtIndexPath:indexPath];

    char *codes;
    NSString *wordForMetaphone = [NSString stringWithString:word.spelling];
    DoubleMetaphone([wordForMetaphone UTF8String], &codes);
    NSLog(@"doubleMetaphone code = %s", codes);

    cell.textLabel.text = word.spelling;

return cell;
}

The log messages seems to execute with no issue, but the cell.textLable.text = word.spelling triggers the EXC_BAD_ACCESS error. When I step through the code I can't po word.spelling either - I'm thinking that the C++ code is blowing up my pointers/memory. But I really don't know where to start to fix it.

Notes: Initially I just passed in [word.spelling UTF*String] but that encountered the same memory issue. word.spelling == @"a" the first time through the code. When I comment out my 4 lines of code the app runs fine.

I'm also not confident I'm setting up char *codes and referencing it correctly so I'm thinking that might be contributing to the issue - shouldn't that be an array of primary and secondary?

I did change the definition of the DoubleMetaphone method from

 DoubleMetaphone(char *str, char **codes) 

because of a "deprecated conversion from string constant to 'char*' " warning see C++ deprecated conversion from string constant to 'char*'. There are a couple of other methods in the C++ class that I also had to switch to const char* I'm hoping that isn't part of the problem?

Please help!

Community
  • 1
  • 1
Alison K
  • 145
  • 1
  • 8
  • What is "word"? Where did you get "cell"? – Hot Licks May 14 '13 at 21:17
  • Word is an object from my model - to test the double_metaphone implementation I took my table of words (its a dictionary app) and thought to log the metaphone code for each one as the cell is drawn. The code snippet is from tableView:cellForRowAtIndexPath: it gets the right 'word' with its spelling from the fetchedResultsController associated with the table. – Alison K May 14 '13 at 21:20
  • That doesn't tell me anything. – Hot Licks May 14 '13 at 21:22
  • I'm sorry, word.spelling was just a way to get a string to send to double_metaphone.... I'll try some hard coded strings to see if they behave differently. – Alison K May 14 '13 at 21:24
  • So I just tested char *codes; NSString *wordForMetaphone = @"A"; DoubleMetaphone([wordForMetaphone UTF8String], &codes); and as soon as the code moves on - trying to access the pre-existing object 'word' I get the EXC_BAD_ACCESS – Alison K May 14 '13 at 21:27
  • So what is "word", how is it defined, and how do you create it? – Hot Licks May 14 '13 at 21:33
  • `*++codes = secondary->str;` is a definite no-no; you're only passing the address of a pointer, not the first element of an array, so it's invalid. – molbdnilo May 14 '13 at 21:37
  • to Hot Licks => I have a core data model for my dictionary that I set up when the app installs. I've included more of my code, I'm hoping that helps. – Alison K May 14 '13 at 21:41
  • @molbdnilo oh dear, that line is part of the C++ or C implementation that I found on the web.... As it seems there is only primary and secondary I'll try to extending the C method to pass two variable into it rather than one... other suggestions welcome. – Alison K May 14 '13 at 21:43
  • Awesome - I just did as you suggested creating two strings to pass in to extract the results and now it is working :-) Thumbs up to @molbdnilo.... – Alison K May 14 '13 at 21:51
  • @molbdnilo create me an answer so I can accept it :-) that was the problem line of code, even though I worked around it rather than figure out how to get the object C side to see the first member of an array as a char *.... you could educate me on what the C method would have liked for codes, I'd love to know. – Alison K May 14 '13 at 22:11

2 Answers2

0

Your problem is

    *++codes = secondary->str;

You're passing a pointer to one char* to the function, so ++codes is an invalid pointer (you're probably overwriting something crucial that happens to be stored next to codes in the calling function).

If you want to return two things, you can either pass two char** parameters - one for "primary" and one for "secondary" - or you can pass a pointer to an array, like this:

char* outputs[2] = {0, 0};
DoubleMetaphone([wordForMetaphone UTF8String], outputs);
NSLog(@"doubleMetaphone primary = %s, secondary = %s", outputs[0], outputs[1]);

...
// In DoubleMetaphone:
codes[0] = primary->str;
codes[1] = secondary->str;
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
0

You could also use my standard C++11 version: double_metaphone. This should #include straight into an .mm (Objective-C++) file without any changes, and you can either convert from std::string to NSString or compose the NSString yourself using the callback version.

Glen Low
  • 4,379
  • 1
  • 30
  • 36