Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which COM smart pointer classes to use?

I'm confused by the choices for COM smart pointers classes for C++ programming:

There's three four I'm aware of:

  • CCOMPtr from ATL
  • _com_ptr_t from the MS Com Support Classes
  • TComInterface (because I'm using C++Builder 2009)
  • CCOMQIPtr, (which I'd previously forgotten)

I've read about the error vs. exception handling differences of the first two, but TComInterface seems totally undocumented. Both the first two seem to have gotchas or 'unexpected' behaviour, from what I can find.

Ideally, I'd like something that's clean and modern C++, but boost::com doesn't exist as far as I know...

I need to control an application from another vendor. They provide a COM interface via a TLB file.

like image 793
Roddy Avatar asked Apr 12 '11 12:04

Roddy


People also ask

When should I use smart pointer?

Smart pointers should be preferred over raw pointers. If you feel you need to use pointers (first consider if you really do), you would normally want to use a smart pointer as this can alleviate many of the problems with raw pointers, mainly forgetting to delete the object and leaking memory.

Should I always use a smart pointer?

So, a smart pointer is only needed, when you use new or other means of dynamic memory allocation. In my opinion, you should prefer to allocate variables on the stack, so when refactoring code (to C++11), you should always ask yourself, if this new is needed, or could be replaced with an object on the stack.

Why Auto_ptr is deprecated?

Why is auto_ptr deprecated? It takes ownership of the pointer in a way that no two pointers should contain the same object. Assignment transfers ownership and resets the rvalue auto pointer to a null pointer. Thus, they can't be used within STL containers due to the aforementioned inability to be copied.


2 Answers

If you are using the type-library import stuff, it will generate code based upon _com_ptr_t and the related COM Support Classes. Go ahead and use those if you are just using stuff from those libraries.

If you are writing ATL-based code, then use CCOMPtr and the other ATL-based classes.

Things may get ugly if you need to mix-and-match both types, but you should probably just get comfortable with both and use whichever makes the most sense for whatever you are doing at the time.

The nice thing is that you have the source for all of these things, so you don't have to rely only on the documentation (which has not been getting much care since .NET appeared).

like image 103
Kristopher Johnson Avatar answered Oct 20 '22 22:10

Kristopher Johnson


Since you are asking for "clean and modern" C++ style, and giving boost as an example, I'll throw in two more: std/boost::shared_ptr and boost::intrusive_ptr. The intrusive_ptr is obviously the more natural choice, since COM objects have an intrusive reference counting mechanism. shared_ptr works just as well, you only need to use a custom deleter that calls IUnknown::Release(), and a small object generator function that does the IUnknown::AddRef() and returns the smart pointer.

I usually go with the intrusive_ptr, so I'm going to explain that in more detail. First, of course, intrusive_ptr_add_ref and intrusive_ptr_release have to be implemented for all IUnknowns. The intrusive_ptr constructor already has a handy feature to skip adding another reference, since many COM functions will do the AddRef() for you.

Now there's one problem with this approach: the intrusive_ptr does not expose its underlying bare pointer like some of the other COM pointers. This is a problem since a common way to create COM objects is by passing a pointer to a pointer to some creation function in another object. Since you cannot pass the intrusive_ptr into these functions, you end up with clumsy temporary bare-pointers that are used to initialize the intrusive_ptr. This is not very elegant, let alone exception-safe (if you need this at all with COM code, which naturally doesn't throw exceptions. I translate COM errors into exceptions though.)

So what I do here is use another tool function that turns a function that takes any com function and returns a callable where any parameter that was a pointer-to-pointer-to-T can either be that or a reference to an intrusive_ptr. Anything else is just like the "input function". These functions then do all the converting between T** and intrusive_ptr& for me. For example, HRESULT CreateBuffer(IBuffer** bufferOut, int size) becomes a callable with the signature HRESULT CreateBuffer(instrusive_ptr<IBuffer>& bufferOut, int size). They are a bit tedious to write for "all" arities, unless you have a code generator, lots of patience, or I presume, variadic templates. But once you have them, it actually makes working with COM very nice.

like image 28
ltjax Avatar answered Oct 20 '22 22:10

ltjax