Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Jim Springfield

Jim Springfield Jim​Springfield

Niner since 2011

  • GoingNative 4: Jim Springfield on ATL, GoingNative Conference - Register Today!

    I can't comment on when I think we'll ship or other numbers.  I'm just saying those do play into our decisions.

    I agree regarding static vs dynamic linking, just noting that people do some crazy things and sometimes it works.

    It isn't always feasible to just use different APIs through LoadLibrary.  Also, some APIs are not available under Metro (e.g. LoadLibrary itself is not available), so we would have to create multiple CRT binaries for different OS targets.

  • GoingNative 4: Jim Springfield on ATL, GoingNative Conference - Register Today!

    There are many things that factor into which OS versions we support in a given release.  It includes some things such as installed base and when the OS goes out of support.  We also consider the complexity of supporting an older OS when there are new APIs that we are trying to use. 

    One example of a technical reason we wanted to not support XP was in order to use the newer locale APIs in Windows.  Windows is deprecating some of the older APIs and there are APIs that are not allowed to be used in Metro.  While it is theoretically possible, we really do not want to have multiple CRT codebases in one release.

    Projecting forward to when VC11 ships and to when ISVs would have apps ready, it is not expected that XP will still have a sizable market.  I can see there are some segments where this may not be true and VS2010 will continue to be supported for quite some time.

    Note that sometimes, even if we don't officially support it, DLLs may load on an older OS or you may be able to statically link the CRT and avoid the new APIs or even implement your own version of the new API.

  • GoingNative 3: The C++/CX Episode with Marian Luparu

    @Dennis

    The problem is that if I am holding an IBaz, I don't necessarily know where it came from.  If it came from WinRT I should call delete when I'm done with the interface.  If it came from a local class, that will destroy the object and I probably don't want to do that.  Although these two interfaces are the same, delete does something fundamentally different.  The ownership rules are just different.

  • GoingNative 3: The C++/CX Episode with Marian Luparu

    @Dennis

    OK, let's go down that path.  Let's say you have the following.  (IBaz could have come from metadata.)

    struct IBaz
    {
        virtual void Baz1() = 0;
    };

    struct Foo : public IBaz
    {
        void Baz1();
    };

    For a class defined internally, this is just ISO C++.  Note that there is no ref-counting machinery anywhere.  I do "new Foo" and I can delete it.  Note that if I want to be able to delete it from IBaz, I need a virtual dtor on IBaz.  However, it is possible to get an IBaz from some other API.  If it is coming from WinRT, I will need to create an object instance that implements IBaz as the "IBaz" I get from the API will be the ABI level IBaz.  I need something like this. (Yes, this would be tool generated.)

    struct IBazImporter : public IBaz
    {
        void Baz() {CheckHR(p->Baz1());}
        _IBaz* p;
        ~IBazImporter() {p->Release();}
    };

    Similarly, when I pass one of my IBaz interfaces to a WinRT interface I will need to create a wrapper the other way. (This could be tool generated.)

    struct IBazExporter : public _IBaz
    {
        HRESULT Baz1() {...}
        IBaz* p;
        unsignedlong refcnt;
        // IInspectable...
    };

    This is exactly what .Net does for COM in creating RCWs/CCWs.  However, there is a more serious problem here because C++ doesn't do garbage collection.  In the original ISO C++ code, we manage objects by using new and delete.  (Yes, you can use shared_ptr, but that is just a layer over new and delete.)  I should either provide a virtual dtor on IBaz or I make its dtor protected to avoid calling delete on it.  However, this is fundamentally different from what has to happen with interfaces that come from outside.  If I get an interface from outside and I wrap it up in another C++ object, I have to tell that wrapper object to go away.  The only way to do that is to call delete on it.  However, that will just release the reference count.  On the locally defined ISO C++ class, calling delete will actually delete the underlying object regardless of whether someone else is holding an interface.

    There is a fundamental impedance mismatch between the C++ object model and COM/WinRT/.Net.  C# is able to support COM and .Net with minimal syntax changes because their object model already supports garbage collection and reference counting is a form of GC.  The C++ object model does not implicitly support GC which causes the problems.  C++/CLI provided a way to access .Net objects that are garbage collected,  C++/CX is almost identical to C++/CLI and provides the same services for WinRT.  This is not a coincidence that the same syntax can work for both.

    In all honesty, I would have felt pretty bad if we did ignore what we did in C++/CLI and did something completely different.

  • GoingNative 3: The C++/CX Episode with Marian Luparu

    Herb pointed out some issues with trying to automatically expose a WinRT wrapper on top of a C++ class.  I want to pile on that with some other observations.  Some of you have been asking what is wrong with the wrapper approach and here is one issue that would need to be addressed.

    Let's say I have an interface IBaz defined in metadata.  It has one method called Baz1.  At the ABI level, it looks like this.

    struct _IBaz : public IInspectable
    {
        virtual HRESULT __stdcall _Baz1() = 0;
    };

    There are a couple of ways the high-level projection could look.  One has the high-level interface inherit from the low-level.  Note that we still need QI, AddRef, and Release in order to implement ref-counting.  This is very similar to what #import does today.

    struct IBaz : public _IBaz
    {
        void Baz1() {CheckAndThrow(_Baz1());}
    };

    Another way is to create a struct that holds onto the underlying interface rather than inheriting from it.  (This has some advantages when you try to implement the "requires" relationship.)

    struct IBaz
    {
        HRESULT QueryInterface() {...}
        long AddRef() {...}
        long Release() {...}
      void Baz1() {CheckAndThrow(p->_Baz1());}
    private:
        _IBaz* p;
    };

    There is a fundamental problem, however, as we have stated that we want to be able to define interfaces locally in "ISO C++" like this.

    struct IBaz
    {
        virtual void Baz1() = 0;
    };

    On one hand, we talk about projecting metadata interfaces into ISO C++ and then we talk about authoring interfaces in ISO C++, but the ISO C++ would be different in each case.  Note that we really need to have AddRef/Release defined on IBaz somehow.  We might be tempted to say that IBaz should inherit from IInspectable like this.

    struct IBaz : public IInspectable
    {
        virtual void Baz1() = 0;
    };

    Now, however, you have a "high-level" ISO C++ interface inheriting from a low-level IInspectable.  That doesn't make sense and it is still different from what #import would do.

    Things will only get more complicated when trying to represent a "requires" relationship or an actual class.

  • GoingNative 3: The C++/CX Episode with Marian Luparu

    @Garfield

    I wouldn't say that the issue is strictly about exposing C++ code to other languagues.  It is really about exposing code through WinRT (which does allow other languages to use it).  The primary authoring scenarios to me are around using DirectUI (XAML).  It has been a long time since Microsoft provided a good way for native developers to do UI.

    @PFYB

    "The questions we are asking are honest: ( a ) could you do the C++/CX in C++, eg, in the way we propose, ( b ) if not, why, ( c ) if yes, why didn't you do it this way."

    I think I have answered these.

    a) Yes
    b) n/a
    c) Because we didn't see this approach as the best solution for our users.  We've tried to cover many of the issues that pushed us to C++/CX and I can understand if you disagree with some of them.

    "If you think the approach with wrappers is worse than the one you took with C++/CX, we would appreciate your thoughts as to why."

    I think we have been trying to explain this.  I often see one of two responses (not just from you PFYB): "that issue isn't important to me" or "you can get close to that with a wrapper".  I can't really argue against what anyone else thinks is important, but we have tried to state our assumptions and the goals we were trying to achieve.  The fundamental issue seems to boil down to how good of a solution could be delivered with some wrapper mechanism vs that of language extensions.  Based on responses, I believe some of you would be willing to accept a more convoluted solution in exchange for it not being based on language extensions. (Note: I will admit that it is not a given that this solution would necessarily be more convoluted, although it is my belief that it would be.)

    Specifics on optimization

    What you suggest with parameters is in fact what usually happens today in COM.  Interface pointers are passed as bare pointers to functions, but are held onto using a smart pointer.  However, I have seen a lot of code where people pass smart pointers by value.  Even passing a const& is sub-optimal as you are really passing a pointer to the pointer.  This type of system can work, but we really wanted something simpler.  We actually spent a lot of time trying to decide whether we should expose raw, but high-level, interface pointers and eventually decided against it in order to keep the model as simple as possible.

    Regarding modules, we do NOT go through WinRT machinery when accessing a locally defined (within a binary) ref class.  So, we can inline code and take advantage of other compiler optimizations.

    @ISO

    I will take a look at what you say CORBA is doing.  Sounds interesting.

    Regarding partial classes, I explained that there is often more than just putting additional methods and data into a class.  Often, you need to modify the set of bases for the class.  Sometimes, you also need to inject code into constructors/destructors or other functions, although usually that can be worked around.  Also, doing a #include inside of a class would make it really hard on the browsing tools within the IDE.  You can take a look at some of my posts on Intellisense/browsing for more background.

    Regarding CreateInstance and WRL.  WRL doesn't use ^ and creates objects similarly to how ATL did it.  "Ref new" returns a ^, not a *.  If you get rid of hats (or similarly marked up pointers), then you don't need it.

    @Warren

    Yes, "new T^[3]" should also work.

    There will be large applications converted to run in WinRT.  I can't provide more details than that, sorry.

    Regarding C++/CLI, I totally get your point.  We were never a first class .Net language.  Winforms support wasn't great, and WPF never happened.  I do believe this is different, however.  This is going to be the ONE way to access Windows moving forward and everyone is committed to having native languages be a part of that.  We aren't an afterthought.  We are part of it.  Also, when Windows creates new APIs, they commit to them for a long time.

    @pierremf

    It is hard to make general characterizations about what percentage of an app is boundary or not.  There are many large applications that run across different operating systems where a very large percentage of the app is written in portable C++ and the UI is written to target a particular OS.  (Note: some people use Qt or other frameworks to make this even easier.)  There are also some small apps that do this.  Usually, partitioning an app like this is done for portability.

    @jalf

    Creating a smart pointer and a CreateInstance<> template would be pretty easy, but it would be harder to make them work with the existing machinery.  You would still need to use ^ for methods.  You could create a typedef to use for parameters (e.g. IFooParam).  I don't see us providing this, however.

    There is lots of code that is WinRT at the boundary.  Windows itself is doing this.  Because C++/CX wasn't available at the start of the project, most Windows code is implemented using WRL, but some stuff is being done in C++/CX and we expect more to come.

    @KMNY

    "In my opinion language extensions have the right to exist iff there isn't an easy or possible way to achieve goals with already existing features. As we proved here this isn't the case with CX. Everything what CX does can be done as easily (and in most cases even easier) with C++. No need for extensions in this case."

    This really gets to the heart of this discussion.  As you say, this is your opinion (and of some others here).  I agree that if there isn't an easy way, language extensions may not be called for.  However, I still don't believe there is an easy way.

    @Dennis

    We believe C++/CX provides value and we are committed to it, but customers can always choose not to use it.  We want to make it the best we can, but at some point the marketplace is the true decider.

    Summary

    We are committed to C++/CX and I still firmly believe it is the right approach.  Even though we have not released Beta yet, there is no chance that we will fundamentally change directions at this time.  (Someone asked this, but I can't put my finger on the name.)  However, we are still very interested in feedback on bugs and issues that people are finding with C++/CX.  Places where there are impedance mismatch issues with standard C++ are also good to know.  We have tried to reduce these compared with C++/CLI, but there are obviously more.

  • GoingNative 3: The C++/CX Episode with Marian Luparu

    Thanks to everyone who has been commenting on this and also on the blog post I did.  It is clear to me that everyone is passionate about this and wants to see good things happen.

    I am a library guy at heart and I would love to see more of the C++11 features.  Stephan (STL) really wanted to see variadic templates.  It would have made his life easier.

    One thing that occurred to me is that we are in a somewhat difficult position here.  You guys aren't really asking us to justify why we did C++/CX, rather you are asking us to justify why we didn't do something else that didn't require language extensions.  You have even somewhat outlined what a solution could look like, which is to have a tool that can generate C++ code from metadata both for consumption and for what I would call pre-authoring.  The "pre-authoring" code might generate C++ base classes and other code you would use while authoring.  Then you need to generate metadata, which could come from the compiler itself or some form of post-processing tool.  The post-processing tool might also generate C++ code that gets built into the binary.

    I do agree that this approach is possible to do, although I believe that there are negatives to this approach.  We did consider several different possibilities during design and discarded them due to issues we ran into.  Herb and I have covered many of these.  Some of these issues were due to original design constraints (which some of you may disagree with as well) that we placed on ourselves such as wanting to avoid HRESULTs and lower level ABI types in the user-written code.  Some designs created too much burden on the developer in having to do the right thing and know too many internal details without clear support from the compiler.  Others would have caused undue performance overhead (such as creating something akin to CCWs and RCWs that .Net uses).  Many times, decisions came down to judgment, although we did prototype many aspects of C++/CX as we went along.  Sometimes, our design team was split down the middle and Herb would break the ties.  We also got feedback from others within the C++ team, other internal teams such as Windows, and some external partners.  We did get questions about our decisions and we were asked to reconsider certain decisions.  We took that feedback and would often spend a lot of time considering it.

    I don't claim that there is no design that could have solved all of these issues and done it without modifying C++.  But even if there truly isn't a good way to do this, to prove a negative thesis such as this would be near impossible.  We did look at approaches like this and based on our experience with MIDL, #import, and other tools, we simply didn't see a path down this road that we liked.  We have tried to condense a lot of discussion here and it is always hard to summarize some of this.  We have tried to show that the design of C++/CX does address what our goals were, and we have tried to point out issues with doing it other ways, but I don't see a way that I can prove to your satisfaction what you are asking.

    Here are some responses to specific questions/issues.  I've tried to read through everything, although I'm sure I'm missing some stuff.

    Interaction between standard C++ and C++/CX

    T^ arr[3];

    This should work.  We've opened a bug on it. 

    There was discussion about the fact that some standard C++ and C++/CX features don't play well together and you get errors when you try.  Often this is due to fundamental difference in the object models between C++ and WinRT.  The fact that I get compiler errors when I do things that are incompatible is better to me than silently compiling and getting some strange runtime behavior.  Where possible, we have tried to make C++/CX have fewer compatibility problems than C++/CLI, such as by allowing you to take the address of members and embedding standard C++ types into C++/CX ref classes.  That said, I want to make as much work as possible and suggestions or bugs in this area are very welcome. 

    Partial Classes

    There are other ways to do this and we've done them before, but there are some issues with those.  Emitting code directly into the user's code can be done, but it is usually non-trivial.  There is also emission of base classes/interfaces and data members that need to be constructed.  These require emitting code that is more intertwined with user code than just a simple region.

    File extension

    I like the idea of a different file extension that would imply the /ZW flag.  Calling it .cx seems pretty neat.  It does encourage the idea of separating the boundary/UI code from the core of the application, which I agree many people want to keep in standard C++ for portability.

    Optimizations

    There are two main places we are able to optimize.  The first is for arguments to functions.  In C++/CX, many parameters are hats (^).  A ^ implies that we are doing automatic reference counting.  For instance, if you have a ^ that is a member of struct and you assign to it, an AddRef will automatically be performed.  In some ways, this is similar to a shared_ptr.  However, when a ^ is passed as an argument to a function, we will not do an automatic AddRef/Release around it.  To accomplish something similar with a  shared_ptr, you would have the function take a "const shared_ptr<>&".  However, this is fundamentally different in that it means at the ABI that a pointer to the variable is actually passed. 

    We will also do this for String^ parameters.  String^ are different from other types in that there is no vtable for them and they may take one of two forms.  There is what is called a "fast-pass" mechanism where a "normal" C/C++ string can be passed to an API that takes a String^ (HSTRING at ABI) without copying the string.  We will automatically do this for string literals, wchar*, and all of the string classes we ship.  Other string classes can easily support this as well.  If the callee decides to hold onto the string it will be automatically copied if the string was fast-pass, but if it was not fast-pass then the reference count of the string can just be bumped.  So, both the caller and callee can do the optimal thing.

    The second opportunity for optimization occurs when calling methods of a class.  Keep in mind that methods of a class may actually be on different interfaces at the ABI.  If you have a WinRT class Foo, when you declare a Foo^, you are holding onto the default interface of Foo.  If you call a method of Foo that is on the non-default interface, the compiler will generate a QueryInterface call to get the interface and then call that method.  The optimization that is possible is when you make multiple calls to a non-default interface of a class (through a local variable that the compiler can prove can't change).  Someone programming in COM today or using the low-level WinRT ABI would likely see this and cache the QI call themselves, but the compiler can do this automatically for ^.

    ref new vs CreateInstance<T> vs new

    There were several things we didn't like about using "new T" where T is a ref class.  One is that the type of the expression is T^, but it is T* for non ref classes.  The second is that we wanted to leave the door open to the possibility of using "ref new" on a non-WinRT class and have that automatically create a reference-counted object.  You can override operator new, but "new T" of a ref class won't use it.

    Regarding something like CreateInstance<T>, you can definitely do this, but if you don't have "ref new", how do you implement CreateInstance<>?  Is CreateInstance<> magically understood by the compiler?  Does it do "new" under the covers?  If so, does that mean I can do "new T"?  What does that mean vs CreateInstance?