views:

272

answers:

4

I have the following code:

std::string F()
{
  WideString ws = GetMyWideString();

  std::string ret;
  StringUtils::ConvertWideStringToUTF8(ws, ret);
  return ret;
}

WideString is a third-party class, so are StringUtils. They are a blackbox to me. Second parameter is passed by reference.

When I step through the debugger the line return ret throws a nasty popup (Visual C++) saying that heap may be corrupted. Upon closer examination copy of the string that gets returned is OK, but the deletion of ret fails. ret contains correct value before return.

What could the converting function possibly do to cause this? Any ideas to fix?

Update:

  • Project itself is a dll
  • StringUtils is a lib
  • Project is compiled against Multithreaded CRT (not debug, not dll)
  • Program seems to run fine when run outside of Visual Studio
+1  A: 

From what little code you show, I suppose StringUtils::ConvertWideStringToUTF8() takes a std::string& as a second parameter. Given that, I don't see how your code can cause a heap corruption.

Note, however, that linking of C++ libraries in general only works when alls the code was compiled using the same compiler and the same compiler settings.

sbi
+4  A: 
  1. If StringUtils was compiled separately (e.g., with a different compiler version), you may have a conflict in the object layout.
  2. If StringUtils is in a DLL, you have to ensure that both it and the main program are compiled to use the standard library in a DLL. Otherwise, each module (executable and DLL) will have its own heap. When StringUtils tries to play with data in the string that was allocated from a different heap, bad things happen.
Jerry Coffin
John Dibling
@John:Good point.
Jerry Coffin
+2  A: 

The designer of StringUtils designed a very poor API. None of the templated Standard library types should be used in the API's public interface. std::string is blown out inline. So if the compiler & libraries you are using is not the exact same compiler & libraries used by the implementor of StringUtils, the types can and likely will be different. Fundamentally, the implementor of StringUtils failed to separate the interface from the implementation.

An illustration of the problem. Suppose you are using MSVC 9.0 SP1 and I am using MSVC 8.0. On my compiler, the implementation of std::string might look like this:

class string
{
// : :  stuff
private:
  int someInt_;
  char* someBuf_;
};

...but on your compiler it might look different:

class string
{
// : :  stuff
private: 

  void* impl_;
};

If I write a library function:

void DoSomethingWithAString(std::string& str);

... and you call it, the sizeof(string) in your code will be different than the sizeof(string) in my code. The types are NOT the same.

You really only have 2 solutions to your problem:

1) [preferred] Get the implementor of StringUtils to fix his broken code.

2) Replace the library used by your compiler to match the library used by StringUtil's implementor. You might be able to accomplish this by using the same compiler at the same patch level as the implementor used, assuming he didn't replace the implementation of the standard library.

EDIT: 3) A third option would be to stop using StringUtils. Honestly this is probably what I'd do.

John Dibling
By your reasoning, any C++ API would be a poor API. I disagree with that sentiment.
sbi
John Dibling
I'm not arguing that all C++ interfaces are poor. How did you make this logical jump?
John Dibling
@sbi: John is absolutely right, using C++ objects across DLL boundaries is very fragile. Only use pure virtual interfaces or C-compatible types. Of course, you can provide C-compatible internal API and then provide a wrapper in a header file, then std::string can be used without issues because it's compiled in the caller's context.
Ben Voigt
@John: You cannot link object files containing classes when they were compiled with different std lib implementations/compilers/compiler versions/compiler settings. This is true no matter whether the objects files contain inline code or not. If you say using `std::string` in an API is dumb, then so is using every other class, no matter whether it contains inlined code or not. (And which class doesn't?)
sbi
@Ben: "Using C++ objects across DLL boundaries is very fragile". This is the very point I saw in John's argument which I disagreed with. (Note: I have been doing cross-platform MLoC C++ projects for a decade. I would never even think of mixing something that's compiled with one compiler version with something that's compiled with another. I understand that this is different for those who only ever used VC and use 3rd-party libs. For them mixing is an option when they adhere to certain rules. But this only works within that closed community. In general, those rules help nothing.)
sbi
@sbi: "I would never even think of mixing something that's compiled with one compiler version with something that's compiled with another." Seem to me you do agree, then.
John Dibling
@John: I agree that passing C++ objects across library boundaries requires that all parts of the program, whether linked statically or dynamically, need to be compiled using (essentially) the same compiler/std lib/compiler setting combination. I do not agree that passing C++ objects (or C++ objects that have inlined code) across library boundaries therefor is generally wrong. And to me that's the essence of your first paragraph.
sbi
@sbi: Then you must think it is OK to require that your customers must use the same version of the STL to consume your library that you used to create the library?
John Dibling
@John: Of course, when you have a C++ API, this is a _requirement_. I already wrote that C++ APIs require the same compiler/compiler setting/std lib combination for all object files linked together. The absence of a standardized ABI is a down-side of C** APIs, but there are advantages, too. As always, you have to balance one against the other.
sbi
@sbi: If you properly separate your interface from your implementation, using a C++ API does not require you to use the same compiler/lib/settings as the API's author. If you allocate in one and deallocate in the other, yes you have to use the same CRT. If you use classes with inlined code, yes both sides have to use the same inline code. I would and did argue that APIs that do either of these are poor APIs, and you might disagree with that, and that's fine. But to say that it is a requirement of all C++ APIs to use the same compiler/libs etc is flat wrong.
John Dibling
@John: Yes, the more restrictive you are in your interface, the less restrictive you have to be with compiler settings and the like. But no matter how careful you are with the former, you still have to be careful with the latter. Even if you don't use any std lib facilities and inline functions, always return all resources to where they were acquired etc., you still can break things by simply messing up calling convention or alignment settings between the app and a lib.
sbi
A: 

Your use of StringUtils and WideString makes it look like you're using C++ Builder. Are you trying to mix a C++ Builder module and a Visual C++ module? If so, then you'd definitely see the problems you've described.

You can't pass a Visual C++ std::string to a C++ Builder function because the C++ Builder code will assume that the parameter uses C++ Builder's std::string definition. The classes might have different fields, and the fields they have in common might be in a different order.

Even if the classes have the same definitions, the modules will still use different memory managers. The called function will allocate memory for the new string contents using its memory manager, and the caller will use its own memory manager to attempt to free the string's contents later.

Rob Kennedy
so if I reserve capacity beforehand, I should be OK?
Kugel
I have no what compiled the lib that contains StringUtils.
Kugel
Unlikely, Kugel. The function probably doesn't use already-allocated space in the input string. Besides, you're forgetting about my second paragraph. Where did this library come from, anyway?
Rob Kennedy
Its a part of Adobe InDesign CS3 SDK.
Kugel