开发者

what's the implication of void**?

开发者 https://www.devze.com 2023-01-11 17:03 出处:网络
When I develop in COM, I alw开发者_C百科ays see (void**) type conversion as below. QueryInterface(/* [in] */ REFIID riid,/* [out] */ void** ppInterface)

When I develop in COM, I alw开发者_C百科ays 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?


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);


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


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;
    }
  }


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


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


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.


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.


Pointer to pointer of unknown interface that can be provided.


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();
}
0

精彩评论

暂无评论...
验证码 换一张
取 消