Microsoft really locked down what you can express in the Windows Runtime ABI.
How do you access implementation specific stuff in a public ref class in C++/CX, assuming you know the underlying implementation is yours?
Here a ficticious example:
public interface class IFoo
{
IAsyncAction^ SomethingAsync();
void SomethingElse(IVector<int>^ y);
};
public ref class Foo sealed : IFoo
{
public:
Foo(int x);
IAsyncAction^ SomethingAsync();
void SomethingElse(IVector<int>^ y);
};
Here, what you see is what you get. There is a class Foo which implements IFoo. That's a great "outer face", but not a good "inner face".
Let's assume I have a larger system which deals with IFoo objects. How do I take an IFoo^ reference and turn it into something where I can access internals and implementation details?
I could of course do dynamic_cast<Foo^>(someIFoo). This would either get me a valid Foo^, or nullptr. Great. But not very scalable. I'd have to treat every concrete implementation separately. It gets ugly fast.
Sadly there is no way to let this public ref class have a base class of any sort in the current Windows Runtime environment. Published/public ref classes must be sealed, and standard C++ classes can't be bases of ref classes at all. I can stuff all kinds of internal and private code in Foo, but there's no obvious way to access it once the object has been cast to IFoo^ and been across the ABI border.
What I really want is a private interface, but there is no support for that. Frustrating! Then I came up with a soluation, of sorts... by staring at the interface declaration and trying to think like a burglar.
I made a class called AsyncActionWrapper<T>, which implements IAsyncAction and holds two things internally:
- A real IAsyncAction^.
- A hidden "payload".
The wrapper implements all the IAsyncAction methods, and simply passes them on to the inner IAsyncAction. Thus the object appears perfectly normal, but there is an extra method there, if you know about it:
T GetPayload() { return m_payload; }
I make sure that some, or all, implementations of the method SomethingAsync wrap their result in this type of object, and hide a useful payload.
Thus I can have IFoo objects, implemented by me, or implemented by the user, and then when they are passed back in to my API, I call SomethingAsync... and then immediately cast the returned IAsyncAction:
auto asyncAction = someFoo->SomethingAsync();
auto wrapper = dynamic_cast<AsyncActionWrapper<IHiddenInterface^>^>(asyncAction);
if(wrapper)
{
IHiddenInterface^ payload = wrapper->GetPayload();
payload->SecretMethod("Win!");
}
Voila, private interface.
But there are drawbacks naturally. I have to either be sure that SomethingAsync does something very benign, since I'm calling it with ulterior motives (but do have to call it), or that it starts work I'd have wanted to start anyway.
There are countless variations of the trick, and obviously it doesn't have to involve async actions at all. It just happened to fit this occasion. You could just as well wrap an IVector or something else, any interface reference should do just as well.
Inga kommentarer:
Skicka en kommentar