locked
Hat or not

    Question

  • Hi,

    Below is a  sample code snippet from session 690.

     

    Calculator^ calc = ref new Calculator(); // Calculator is a ref class from a custom WinRT component

    txtResult->Text = calc->Add(99, 1).ToString();

     

    I noticed that ToString() is not called with '->', but with '.'. Looks like an intermediate Platform::Int32 (I guess) w/o a hat was generated from calc->Add(99,1).

    My question is that when there supposed to be a 'hat', and when there is not.

     

    The following code do not work as I tried:

    -----------------------

    Calculator calc;

    txtResult->Text = calc.Add(99, 1).ToString(); // Code compile and build, but this line crash, as calc seems not been initialized.

    ----------------------

    Int32 int1(5); // Compiles and int1 is initialized and works as expected.

    String str("test"); //  Doesn't compile and compiler complains C3149: 'Platform::String' : cannot use this type here without a top-level '^'

    ---------------------

     

    Thanks

     






    • Edited by Ciger Wednesday, October 19, 2011 9:13 AM
    Wednesday, October 19, 2011 8:33 AM

Answers

  • Yes, we're aware of this issue.

     

    For //build/, please work around his by using the ^ syntax, i.e.:

    Test^ test = ref new Test();

    ...

    delete test;

     

    The code-gen for that is (well should be), very close to saying:

    Test test;

     

    Both of those create heap-allocated objects behind the scenes, but the difference is whether you logically have heap semantics vs. stack semantics.

    The main difference is:

    * The ^syntax will fire a destructor when the refcount on the object drops to 0, or if you explicitly call delete. (So if you handed the object out it's not necessarily at the end of your scope)

    * The stack syntax will fire a destructor when the object goes out of scope (or on an exception etc.). This is important when you e.g. handed of the object to another thread or async callback, so there may still be a refcount on it, but you need to get rid of it right away. (E.g. a file handle that needs to be closed otherwise the file is locked).

     

    The main advantage of the second is exception-safe deterministic destruction.

    PS: Because of the nature of refcounting, it's a little bit less important with WinRT to have deterministic destruction than with e.g. the /clr and a garbage collected heap (where the point of destruction is virtually random). However, you will find that with async patterns it is common to get into a situation where you're transfer the ownership of an object from one thread to another (e.g. via a lambda). There is then a race condition between the two threads for releasing the object. This is generally still ok, but if the object represents a file or other exclusive resource it might be critical to perform the destruction at a specific time, rather than relying on the timing between the two threads.

     


    Tuesday, November 01, 2011 7:49 PM

All replies

  • String may be specially handled to not alow stack semantics.

    Regular WinRT classes should support stack semantics. You code above should have worked. If it did not, it's certainly a compiler error (not surprising in a pre-beta dev preview).


    http://blog.voidnish.com
    Wednesday, October 19, 2011 4:49 PM
  • Thanks Nishant for the hints. I might got the idea here: Int32 is a value class, and String or the custom winRT component are all ref class. They are different.

     

    for example: Int32^ int1 = ref new Int32(1); // Doesn't compile.

     

    If I'm understanding correct, then the only problem is that why "Calculator calc;" compiles if it is not supposed be used in stack. Really a compiler problem?

    Didn't touch any managed code semantics before WinRT.


    • Edited by Ciger Thursday, October 20, 2011 2:08 AM
    Thursday, October 20, 2011 1:48 AM
  • Well remember that WinRT is not managed. It's 100% native code.

     

    I am on Windows 7 at the moment. If I use Windows 8 today I'll do a quick test to see if I can repro your error too.


    http://blog.voidnish.com
    Thursday, October 20, 2011 11:50 AM
  • Here's a follow up. Stack semantics is indeed buggy at the moment. The following code that should work will crash:

    ref class Test
    {
    public:
     int Bar(String^ s)
     {
      return s->Length();
     }
    };

    MainPage::MainPage()
    {
        InitializeComponent();

       Test test;
    } // <-- crash here : vccorlib.h : __cli_winrt_ptr_dtor  

     

    I reckon the C++ team is aware of this, so I don't think you need to file this on Connect.


    http://blog.voidnish.com
    Thursday, October 20, 2011 1:38 PM
  • Yes, we're aware of this issue.

     

    For //build/, please work around his by using the ^ syntax, i.e.:

    Test^ test = ref new Test();

    ...

    delete test;

     

    The code-gen for that is (well should be), very close to saying:

    Test test;

     

    Both of those create heap-allocated objects behind the scenes, but the difference is whether you logically have heap semantics vs. stack semantics.

    The main difference is:

    * The ^syntax will fire a destructor when the refcount on the object drops to 0, or if you explicitly call delete. (So if you handed the object out it's not necessarily at the end of your scope)

    * The stack syntax will fire a destructor when the object goes out of scope (or on an exception etc.). This is important when you e.g. handed of the object to another thread or async callback, so there may still be a refcount on it, but you need to get rid of it right away. (E.g. a file handle that needs to be closed otherwise the file is locked).

     

    The main advantage of the second is exception-safe deterministic destruction.

    PS: Because of the nature of refcounting, it's a little bit less important with WinRT to have deterministic destruction than with e.g. the /clr and a garbage collected heap (where the point of destruction is virtually random). However, you will find that with async patterns it is common to get into a situation where you're transfer the ownership of an object from one thread to another (e.g. via a lambda). There is then a race condition between the two threads for releasing the object. This is generally still ok, but if the object represents a file or other exclusive resource it might be critical to perform the destruction at a specific time, rather than relying on the timing between the two threads.

     


    Tuesday, November 01, 2011 7:49 PM