tags:

views:

649

answers:

9
+5  A: 

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;
    }
  }
leppie
The language will not allow you to pas a `Something **` where a `void **` is expected without an explicit cast. Moreover, the whole approach is invalid. The trick used in COM is based on the implementation details, which are generally not portable.
AndreyT
@AndreyT: In C++, but C will be quite happy. IIRC C# in unsafe mode will also be happy.
leppie
No, not even in C. `Anything *` is implicitly convertable to and from `void *`, but the same is **not** true of `Anything **` and `void **`. A `void **` should only be dereferenced if it points at an actual `void *` object (or a `char *`, due to a special exception).
caf
I was incorrect. C# requires a cast to `void**`.
leppie
Thanks for your replies. I updated my post.
smwikipedia
@leppie: C might be "happy" only because many C compilers limit themselves to mere "warnings" in cases like that. Still, this is a constraint violation (i.e. an "error") even in C.
AndreyT
@AndreyT: While I do believe what you are saying is true, I still dont really understand why. Maybe I will try formulate a question on my exact 'issue' :) Thanks
leppie
@leppie: The basic reason is that, in general, pointers need not all have the same size or representation (there are some special cases in which this *is* guaranteed, though).
caf
@caf: So you mean sizeof(void**) != sizeof(something**) for some cases? Perhaps I am still sleeping, but I cant think of a single example :) (I do understand sizeof(void*) != sizeof(something*) in some cases)
leppie
@leppie: It's the `sizeof(void *) != sizeof(something *)` case that I was referring to. If you dereference a `void **`, you're accessing the value it points at as a `void *` - so it had better actually *be* pointing at a `void *`. Eg if you cast a `double **` to a `void **`, then dereference it, you'll be accessing a `double *` as if it was a `void *`, which isn't allowed / guaranteed to work.
caf
@leppie: http://c-faq.com/ptrs/genericpp.html
jamesdlin
@jamesdlin: Thanks, although I dont entirely agree with their 'portable' example. Assigning `dp` to `vp` has no purpose as `vp` will be overwritten anyways after the call to `f`.
leppie
@caf: Actually, I got that wrong, when does `sizeof(void*) != sizeof(something*)` ?
leppie
@leppie: The assignment from `dp` to `vp` is not pointless. You can think of it as doing a widening conversion (and then doing a narrowing one afterward). I don't know of any real-world systems where this matters, but you perhaps could imagine a hypothetical system where most objects are not byte-addressable, and therefore `char*` (and `void*`) require larger types.
jamesdlin
@leppie: Potentially, that inequality can be true when the `something` isn't qualified or unqualified `char`.
caf
@caf: Are you saying `sizeof(void*) != sizeof(char*)` ? Also in context with my example, `void**` gets dereferenced to `void*`, hence it will always valid IMO. Dereferencing further will obviously cause problems.
leppie
@leppie: No, I'm saying the opposite. `char *` and `void *` are explicitly guaranteed to have the same size, alignment requirements and representation; but the same guarantee is *not* made for other pointer types (there are some other guarantees, like that all pointers to `struct` types have the same size, alignment and representation as each other).
caf
A: 

It's a pointer to the interface pointer you request using this call. Obviously you can request all sorts of interfaces, so it has to be a void pointer. If the interface doesn't exist, the pointer is set to NULL.

edit: Detailed information to be found here: http://msdn.microsoft.com/en-us/library/ms682521(VS.85).aspx

Mononofu
A: 

It allows the API to specify that a pointer may be used as an [in-out] parameter in future, but for now, the pointer is unused. (NULL is usually the required value.)

When returning one of many possible types, with no common supertype (such as with QueryInterface), returning a void* is really the only option, and as this needs to be passed as an [out] parameter a pointer to that type (void**) is needed.

mdma
A: 

not to enforce type validation

Indeed, void* or void** are there to allow the use of different types of pointers, that can be downcasted to void* to fit in the function parameters type.

Cedric H.
A: 

Pointer to pointer of unknown interface that can be provided.

Learner
+1  A: 

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."

Chubsdad
+17  A: 

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);
caf
Nice example :) +1
leppie
+17  A: 

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. [...]

stakx
+1 for the definitive book on COM.
RBerteig
Arguably, it should actually be `void *pv; hr = punk->QueryInterface(IID_ICAT, pPug = pv;` (referring to the quoted passage from the book).
caf
A: 

Instead of using pointers to pointers, try using a reference to a pointer. It's a bit more C++ than using **.

e.g.

void Initialise(MyType &*pType)
{
    pType = new MyType();
}
Mark Ingram