35

I've read various things about how one should not allocate heap memory in one DLL and deallocate it from outside that DLL. But what about throwing an exception object that is a just a temporary (as most exception objects are)? E.g.:

throw my_exception( args ); // temporary: no heap allocation

When the exception object is caught outside the DLL, the destructor for that object will eventually be executed and the non-heap memory for the object will be reclaimed. Is that OK since it's not heap memory?

Paul J. Lucas
  • 6,895
  • 6
  • 44
  • 88
  • The exception you throw is not the exception you catch; a copy is made along the way. That said, I do not know where the copy resides or how its destroyed. – Mark Ransom Feb 24 '11 at 17:10
  • @Mark: Is that still true when the exception is caught by reference? Do you have a source? – Ben Voigt Feb 24 '11 at 17:18
  • @Mark: AFAIK, C++ requires that an exception have a publicly accessibly copy constructor, but it's up to an implementation as to whether to actually use it to make copies. It may very well be the same object that's caught. – Paul J. Lucas Feb 24 '11 at 17:26
  • 1
    @Ben: From N3225, `15.1 Throwing an exception`, `3: A throw-expression initializes a temporary object...`, `5: The memory for the exception object is allocated in an unspecified way...` @Paul: `5: When the thrown object is a class object, the copy/move constructor and the destructor shall be accessible, even if the copy/move operation is elided` – Eugen Constantin Dinca Feb 24 '11 at 17:50
  • 1
    @Ben: From N3225, `15.3 Handling an exception` `16: The object declared in an exception-declaration or, if the exception-declaration does not specify a name, a temporary (12.2) is copy-initialized (8.5) from the exception object...` `17: ...When the handler declares a reference to a non-constant object, any changes to the referenced object are changes to the temporary object initialized when the throw-expression was executed and will have effect should that object be rethrown.` – Eugen Constantin Dinca Feb 24 '11 at 17:54
  • @Paul, theoretically it might be possible to use the same copy, but practically the object will cease to exist as the stack is unwound. – Mark Ransom Feb 24 '11 at 18:07
  • @Eugen: Thanks, that is exactly what I was wondering. – Ben Voigt Feb 24 '11 at 18:31

2 Answers2

33

Throwing C++ exceptions across DLL boundaries is only possible when all modules use the same C++ runtime, in which case they share a heap as well. But this can be a maintenance burden, especially when libraries from multiple vendors are involved, so it is discouraged.

If you want error-handling which is portable across multiple compilers/compiler versions/compiler settings, either use return codes or OS-provided exceptions (e.g. SEH on Windows)/

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • You wrote, "... in which case they share a heap as well." I specifically asked about the case where no heap memory is involved. – Paul J. Lucas Feb 24 '11 at 17:34
  • @Paul: What I'm saying is that if you have a reason (and there are good ones that appear a lot) not to share heap memory across DLLs, that same reason will prevent you from sharing exceptions across DLLs. – Ben Voigt Feb 24 '11 at 17:36
  • So is it also not OK to share a simple POD struct across DLLs? What if a function in a DLL returns a struct by value? That temporary will get destroyed eventually. Same problem? If not, why not? – Paul J. Lucas Feb 24 '11 at 17:38
  • 13
    @Paul: It's not the exception struct that's the problem, it's the compiler's internal data used to find the matching catch block and perform stack unwinding, all of which are highly compiler-specific and may use global variables, may even use the heap. If all modules aren't sharing a runtime you have problems. – Ben Voigt Feb 24 '11 at 17:56
1

It depends how that memory was allocated and whether the mechanism to do so ("the runtime" or "memory manager") is shared between the specific DLL and the other parts of the application. E.g. a throw new my_exception( args ); could also be in order depending on the details.

You could make your exception reference counted, so that it comes with the intrinsic knowledge of how to destroy its own instance (and owned memory).

Using IMalloc (see MSDN) for instance allocation and placement new would be another way (call OleInitialize before) ...

Indeed, the memory allocation is an issue depending on what is being used. For example mixing statically linked CRT and dynamically linked CRT in different parts of an application will lead to issues the same way the mixing debug and release code would. The problem here is that the code that is supposed to free the memory uses a different "memory manager". But if the thrown object knows about its own destruction, it should be fine, since the dtor code would reside in the same compilation unit as the one allocating it.

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
  • Why would IMalloc be needed? If one were to throw dynamically allocated, reference-counted exception objects via new as you suggest, then as long as the new/delete pair were done within the DLL code, why wouldn't that be sufficient to work without issue? – Paul J. Lucas Feb 24 '11 at 18:35
  • @Paul J. Lucas: That's why it says "would be another way" in the sentence where I mention IMalloc. Sorry if that was unclear. English is not my native language. I'll move it to a separate paragraph ... – 0xC0000022L Feb 24 '11 at 20:40