2

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
Kugel
  • 19,354
  • 16
  • 71
  • 103
  • 1
    Is `StringUtils` compiled by you or is it a third-party library? The implementation of std::string can vary depending on the compiler. – JonM Feb 26 '10 at 16:54
  • 1
    Then either it has a bug or it's compiled with compiler settings different from yours. (I presume you do use the same compiler as what the lib was compiled with.) – sbi Feb 26 '10 at 16:57
  • @sbi: More likely is StringUtils interface is broken. It should not be using string or as function parameters. – John Dibling Feb 26 '10 at 17:07
  • @sbi: John is absolutely right, I'll explain why in a comment to his answer. – Ben Voigt Feb 26 '10 at 17:27
  • this should be tagged "onedefinitionrule" – Ben Voigt Feb 26 '10 at 17:30
  • Why does one definition rule apply here? – Kugel Feb 26 '10 at 17:44
  • Because std::strng is defined one way in the calling code and defined another way in the library code. It might be a stretch to call this ODR, but it does help to illustrate the problem. – John Dibling Feb 26 '10 at 17:50
  • @Ben: I read your comment and still disagree. – sbi Feb 26 '10 at 18:22

4 Answers4

4
  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
  • 476,176
  • 80
  • 629
  • 1,111
2

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.

Community
  • 1
  • 1
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • 1
    By your reasoning, any C++ API would be a poor API. I disagree with that sentiment. – sbi Feb 26 '10 at 17:09
  • 1
    @sbi: Then you didn't understand my reasoning. std::string is blown out inline. The implementation varies from compiler to compiler and from library to library. If you write a library using the intel compiler and I try to consume it using MSVC, std::string will look different to you & me. – John Dibling Feb 26 '10 at 17:14
  • I'm not arguing that all C++ interfaces are poor. How did you make this logical jump? – John Dibling Feb 26 '10 at 17:16
  • 3
    @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 Feb 26 '10 at 17:28
  • @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 Feb 26 '10 at 18:15
  • @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 Feb 26 '10 at 18:21
  • @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 Feb 26 '10 at 18:49
  • @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 Feb 27 '10 at 10:15
  • @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 Mar 01 '10 at 14:40
  • @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 Mar 01 '10 at 15:52
  • @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 Mar 01 '10 at 16:13
  • @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 Mar 02 '10 at 10:38
1

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
  • 219,715
  • 46
  • 258
  • 445
0

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
  • 161,384
  • 21
  • 275
  • 467