Monday, October 19, 2009

CSB Internal Type Safety


3 4



CSB Internal Type Safety



In addition to the various methods that enable STL functionality, the CSB enumerator interface contains a curious section labeled "implementation support." You can inspect its content in the previous section of this chapter. In this section of the enumerator interface, there is an odd set of type definitions and a method that requires a user-defined translation (known as bonding routines) between its client-visible version and the version implemented by the interface proxy, just like the familiar Next method. As explained previously, enumerator instances must cooperate with one another so that their iterators can carry out certain operations on behalf of (possibly remote) iterator adapters. The implementation support section facilitates this cooperation among enumerators.


Because all a remote client can pass to an enumerator interface is a parameter type that can be represented by MIDL and marshaled, an enumerator must obtain raw C++ objects from another enumerator via an interface pointer. This poses an interesting problem, really. How do we obtain artifacts that cannot be represented in IDL from an interface pointer?



The answer lies in the combination of two advanced IDL features: local methods and cpp_quote. Because the actual information that is communicated among enumerators cannot be represented in IDL, the interface method that delivers this information must be called locally—that is, in context. Passing raw C++ iterator objects across process or even context boundaries does not make sense. That's why CSB co-locates all of a collection's enumerator objects with their collection. The method that retrieves a raw iterator from an enumerator therefore can be marked as local. The user-defined glue that translates local method invocation to proxy method invocation can simply return an error because the method cannot and should not work across contexts. The other user-defined bonding routine that translates from stub to local method should then never be invoked and can simply return E_UNEXPECTED. The macro IENUM_CALL_AS_RESOLVER, shown in the previous section, arranges for such code to be generated for your enumerator interface.
15



The second piece of the puzzle is creating the function signature of the local method within IDL in a way that maintains type safety. Type safety is particularly important in generic programming projects because of the complexity of the type compositions involved. A minor error in manual type reproduction can have fatal consequences. Representing iterator types in interfaces is not easy because IDL cannot represent instantiations of C++ templates. I use cpp_quote to produce a type-safe signature for C++ clients of the generated header files and a nondescript void* signature for MIDL. The C++ declaration must be preceded by extern C++ because MIDL-generated headers are placed in an extern C block and template instantiation is not permitted by the compiler in that scope.



All you need to do for your enumerator is paste the appropriate implementation support block into your interface definition. The single method is implemented by CSB, and the IENUM_CALL_AS_RESOLVER macro generates local and call_as glue code for the implementation support method, along with the one required for the COM+ Next and RemoteNext methods. You can place the macro in a .c file and link it to your project. But we can pursue the quest for type safety only to a certain point. A situation could arise, possibly involving container types from third-party libraries, in which declaring type-safe signatures in an IDL file becomes just too difficult or cumbersome. So far this has not happened to me. But if you encounter a situation like this, you might want to introduce a new method, _GetIteratorNoType, into your enumerator and have CSB implement it by using reinterpret_cast to cast its void* argument to the iterator type within the implementing template and then delegate to the existing _GetIterator. Be sure to do this within exception guards, just in case the types don't quite match after all. This strategy will keep all C++ generic types out of your IDL files, albeit at the cost of type safety among enumerator objects that support the same interface.



No comments:

Post a Comment