Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what's the implication of void**?

Tags:

c++

c

c#

com

When I develop in COM, I always see (void**) type conversion as below.

QueryInterface(/* [in] */ REFIID riid,/* [out] */ void** ppInterface)

What's exact meaning of it?

IMHO, it tells the compiler not to enforce type validation, since the type which is pointed by the ppInterface is not known to the client code at compile time.

Thanks~~~

Update 1

I understand it this way:

void* p implies AnyType* p

void ** pp implies pointer to AnyType*

Update 2

If void**pp means "pointer to void*", then what checks does the compiler do when it sees it?

like image 545
smwikipedia Avatar asked Aug 17 '10 06:08

smwikipedia


4 Answers

A void ** is a pointer to a void *. This can be used to pass the address of a void * variable that will be used as an output parameter - eg:

void alloc_two(int n, void **a, void **b)
{
    *a = malloc(n * 100);
    *b = malloc(n * 200);
}

/* ... */

void *x;
void *y;

alloc_two(10, &x, &y);
like image 180
caf Avatar answered Oct 17 '22 09:10

caf


The reason why COM uses void** with QueryInterface are somewhat special. (See below.)

Generally, void** simply means a pointer to void*, and it can be used for out parameters, ie. parameters that indicate a place where a function can return a value to. Your comment /* [out] */ indicates that the location pointed to by ppvInterface will be written to.

"Why can parameters with a pointer type be used as out parameters?", you ask? Remember that you can change two things with a pointer variable:

  1. You can change the pointer itself, such that it points to another object. (ptr = ...)
  2. You can modify the pointed-to object. (*ptr = ...)

Pointers are passed to a function by value, ie. the function gets its own local copy of the original pointer that was passed to it. This means you can change the pointer parameter inside the function (1) without affecting the original pointer, since only the local copy is modified. However, you can change the pointed-to object (2) and this will be visible outside of the function, because the copy has the same value as the original pointer and thus references the same object.

Now, about COM specifically:

  • A pointer to an interface (specified by riid) will be returned in the variable referenced by ppvInterface. QueryInterface achieves this via mechanism (2) mentioned above.

  • With void**, one * is required to allow mechanism (2); the other * reflects the fact that QueryInterface does not return a newly created object (IUnknown), but an already existing one: In order to avoid duplication of that object, a pointer to that object (IUnknown*) is returned.

  • If you're asking why ppvInterface has type void** and not IUnknown**, which would seem more reasonable type-safety-wise (since all interfaces must derive from IUnknown), then read the following argument taken from the book Essential COM by Don Box, p. 60 (chapter Type Coercion and IUnknown):


One additional subtlety related to QueryInterface concerns its second parameter, which is of type void **. It is very ironic that QueryInterface, the underpinning of the COM type system, has a fairly type-unsafe prototype in C++ [...]

 IPug *pPug = 0;
 hr = punk->QueryInterface(IID_IPug, (void**)&pPug);

Unfortunately, the following looks equally correct to the C++ compiler:

 IPug *pPug = 0;
 hr = punk->QueryInterface(IID_ICat, (void**)&pPug);

This more subtle variation also compiles correctly:

 IPug *pPug = 0;
 hr = punk->QueryInterface(IID_ICat, (void**)pPug);

Given that the rules of inheritance do not apply to pointers, this alternative definition of QueryInterface does not alleviate the problem:

 HRESULT QueryInterface(REFIID riid, IUnknown** ppv);

The same limitation applies to references as to pointers as well. The following alternative definition is arguably more convenient for clients to use:

 HRESULT QueryInterface(const IID& riid, void* ppv);

[...] Unfortunately, this solution does not reduce the number of errors [...] and, by eliminating the need for a cast, removes a visual indicator that C++ type safety might be in jeopardy. Given the desired semantics of QueryInterface, the argument types Microsoft chose are reasonable, if not type safe or elegant. [...]

like image 42
stakx - no longer contributing Avatar answered Oct 17 '22 08:10

stakx - no longer contributing


It is just a pointer to void*.

Eg:

Something* foo;
Bar((void**)&foo);

// now foo points to something meaningful

Edit: A possible implementation in C#.

  struct Foo { }

  static Foo foo = new Foo();

  unsafe static void Main(string[] args)
  {
    Foo* foo;

    Bar((void**)&foo);
  }

  static unsafe void Bar(void** v)
  {
    fixed (Foo* f = &foo)
    {
      *v = f;
    }
  }
like image 5
leppie Avatar answered Oct 17 '22 09:10

leppie


Passing by void * also ensures that the pointed to object cannot be deleted or tampered (accidentally).

"This implies that an object cannot be deleted using a pointer of type void* because there are no objects of type void."

like image 1
Chubsdad Avatar answered Oct 17 '22 08:10

Chubsdad