Using shared_ptr in dll interfaces

I have an abstract class in my dll.

class IBase { protected: virtual ~IBase() = 0; public: virtual void f() = 0; }; 

I want to get IBase in my exe file that the dll loads. The first way is to create the following function

 IBase * CreateInterface(); 

and add the virtual function Release() to IBase .

The second way is to create another function

 boost::shared_ptr<IBase> CreateInterface(); 

and the Release() function is not required.

Questions.

1) Is it true that the destructor and memory deallocation are called in the dll (not in the exe file) in the second case?

2) Does the second case work well if the exe file and the dll were compiled with different compilers (or different settings).

+20
c ++ boost abstract-class dll shared-ptr
Oct 22 '09 at 7:56
source share
4 answers

The answer to your first question: A virtual destructor is called in your DLL - information about its location is built into your object (in the vtable). In the case of freeing up memory, it depends on how disciplined your IBase users IBase . If they know that they should call Release() and consider that the exception can bypass the control flow in an amazing direction, the right one will be used.

But if CreateInterface() returns shared_ptr<IBase> , it can bind the correct release function to this smart pointer. Your library might look like this:

 Destroy(IBase* p) { ... // whatever is needed to delete your object in the right way } boost::shared_ptr<IBase> CreateInterface() { IBase *p = new MyConcreteBase(...); ... return shared_ptr<IBase>(p, Destroy); // bind Destroy() to the shared_ptr } // which is called instead of a plain // delete 

Thus, each user of your DLL is easily prevented from leaking resources. They never have to worry about calling Release() or pay attention to exceptions that bypass unexpectedly bypass the control flow.

To answer the second question: The disadvantage of this approach is clearly expressed by the other answer s: your audience should use the same compiler, linker, settings, libraries as you. And if they can be quite a lot, this can be a serious flaw for your library. You must choose: security against a wider audience

But there is a possible loophole: Use shared_ptr<IBase> in your application, i.e.

 { shared_ptr<IBase> p(CreateInterface(), DestroyFromLibrary); ... func(); ... } 

Thus, no specific implementation object is passed across the DLL boundary. However, your pointer is safely hiding behind shared_ptr , which calls DestroyFromLibrary at the right time, even if func() throws an exception or not.

+16
Oct 22 '09 at 8:43
source share

I would advise using shared_ptr in the interface. Even using C ++ in general in the DLL interface (unlike the "extern C routines") is problematic, because name management will not allow you to use the DLL with another compiler. Using shared_ptr especially problematic because, as you have already determined, there is no guarantee that the DLL client will use the same shared_ptr implementation as the caller. (This is because shared_ptr is a template class, and the implementation is fully contained in the header file.)

To answer your specific questions:

  • I'm not quite sure what you are asking here ... I assume that your DLL will contain implementations of classes derived from IBase . The code for their destructors (like the rest of the code) will in both cases be contained in a DLL. However, if the client initiates the destruction of the object (by calling delete in the first case or allowing the last instance of shared_ptr to go out of scope in the second case), then the destructor will call the code from the client.

  • Ignoring it will usually prevent your DLL from being used with another compiler ... but the implementation of shared_ptr can change even in a new version of the same compiler, and this can lead to your trouble. I would shy away from using the second option.

+5
Oct 22 '09 at 8:07
source share
  • Using shared_ptr , make sure that the resource allocation function is called in the DLL.
  • Look at the answers to this question .

The way out of this problem is to create a clean C-interface and a thin fully embedded C ++ shell.

+1
Oct 22 '09 at 8:19
source share

On the first question: I accept a reasonable assumption and do not speak from experience, but it seems to me that in the second case the memory release will be called "in .exe". There are two things that happen when you call delete object; : firstly, destructors are called and secondly, the memory for the object is freed. The first part, calling the destructor, will definitely work as you expect, by naming the correct destructors in your DLL. However, since shared_ptr is a class template, its destructor is generated in your .exe, and therefore it will call the delete () operator in your exe, and not in the DLL. If these two were associated with different versions at runtime (or even statically linked to the same version of runtime), this should lead to terrible undefined behavior (this is the part I'm not quite sure about, but it is logical to assume that the path ) There is an easy way to check if what I said is correct, override the removal of the global operator in your exe, but not your dll, put a breakpoint in it and see what is called in the second case (I would do it myself, but I have there is a lot of time to relax, unfortunately).

Please note that for the first case, there is the same thing that you seem to understand, but just in case). If you do this in exe:

 IBase *p = CreateInterface(); delete p; 

then you are in the same trap - the calling new operator in the dll and the calling delete operator in exe. You will need the corresponding DeleteInterface function (IBase * p) in your DLL or the Release () method in IBase (which does not have to be virtual and not make it inline) for the sole purpose of invoking the correct memory, the release function.

0
Oct 22 '09 at 9:44
source share



All Articles