C++/CLI confusion about % vs. ^

Just a little confusion I am hoping someone can help clear up...

I have a method passing in a handle to a Point structure to which I make changes to X and Y values. On return to the caller I find that the Point values are no different. The declareation is Point ^pPt
Now if I instead use Point% pPt for passing a reference then any change to pPt.X or pPt.Y works.

I thought I could treat Point ^pPt similar to a pointer where changes to pPt->X, pPt->Y would be seen by the calling code

Thanx


Answer this question

C++/CLI confusion about % vs. ^

  • tchris38

    Thanx I think I see what you are referring to ... just calling f1 inherently boxes the Point ^p variable which results in a pass by value not reference. Other than % to pass by reference is there a way to pass as a pointer to Moot point since passing by reference works for my needs -- I was just curious.

     Jonathan Caves MSFT wrote:
    You should be able to use '^' similar to a '*' in C++. One question: is your Point class a value-type If it is then things are slightly different: for example:

    using namespace System;

    value struct Point {
       int
    x;
       int
    y;
    };

    void f1(Point^ p)
    {
       p->x += 2;
       p->y += 2;
    }

    void f2(Point% p)
    {
       p.x += 2;
       p.y += 2;
    }

    int main()
    {
       Point p = { 1, 2 };

       Console::WriteLine("p.x = {0}, p.y = {1}", p.x, p.y);

       f1(p);

       Console::WriteLine("p.x = {0}, p.y = {1}", p.x, p.y);

       f2(p);

       Console::WriteLine("p.x = {0}, p.y = {1}", p.x, p.y);
    }

    If compiled and ran this program will produce:

    p.x = 1, p.y = 2
    p.x = 1, p.y = 2
    p.x = 3, p.y = 4


    The issue here is that when you box a value-type (which is what happens when f1 is called in the example above) the runtime creates a copy of your value-type on the GC heap: so any modifications to this copy are not reflected in the orginial instance of the value-type.

    In general if you wish to modify a value-type don't box it instead pass it by reference.


  • LozD

    In my DLL where this code I am referring exists I expose many methods that accepts as a first parameter what I thought was the equal of a pointer to a class...after what you have shown I am questioning my use of ^ and wonder if it more efficient to change use of the handle to all references. For example:

    MethodA(SomeClass ^pClassVar)

    for efficency should change to

    MethodA(SomeClass% pClassVar)

    Only thing I do not like is from C# the code now would need an explicit ref declare in passing pClassVar

    What is the recommended approach

  • Sebbie85

    If you want to pass values-types by reference between C++ and C# then you definitely need to pass then by reference using a '%'. The reason for this is that the C# compiler does not understand the concept of boxed value-types as a first-class type and therefore it would not understand the signature of the method.



  • Willem Termans

    <bxs122@discussions.microsoft.com> wrote in message
    news:1da63330-a8ea-434e-a1d5-ddc03bf61ca5@discussions.microsoft.com
    > I have a method passing in a handle to a Point structure to which I
    > make changes to X and Y values. On return to the caller I find that
    > the Point values are no different. The declareation is Point ^pPt 
    > Now if I instead use Point% pPt for passing a reference then any
    > change to pPt.X or pPt.Y works.
     
    Point is a value class (a struct), not a reference class. When you pass it by handle, it has to be boxed, so what actually gets passed is a boxed copy allocated on managed heap. This is what your function modifies. When you pass by reference, the compiler is smart enough not to box so you modify the original instance on the stack.
     
    Try something like this:
     
    Point pt;
    Point^ pPt = pt; // implicit boxing
    f(pPt); // your function modifies the boxed copy
    pt = *pPt; // unbox and copy back into the value

    --
    With best wishes,
        Igor Tandetnik

  • Atul Sehgal

    You should be able to use '^' similar to a '*' in C++. One question: is your Point class a value-type If it is then things are slightly different: for example:

    using namespace System;

    value struct Point {
       int
    x;
       int
    y;
    };

    void f1(Point^ p)
    {
       p->x += 2;
       p->y += 2;
    }

    void f2(Point% p)
    {
       p.x += 2;
       p.y += 2;
    }

    int main()
    {
       Point p = { 1, 2 };

       Console::WriteLine("p.x = {0}, p.y = {1}", p.x, p.y);

       f1(p);

       Console::WriteLine("p.x = {0}, p.y = {1}", p.x, p.y);

       f2(p);

       Console::WriteLine("p.x = {0}, p.y = {1}", p.x, p.y);
    }

    If compiled and ran this program will produce:

    p.x = 1, p.y = 2
    p.x = 1, p.y = 2
    p.x = 3, p.y = 4


    The issue here is that when you box a value-type (which is what happens when f1 is called in the example above) the runtime creates a copy of your value-type on the GC heap: so any modifications to this copy are not reflected in the orginial instance of the value-type.

    In general if you wish to modify a value-type don't box it instead pass it by reference.



  • Mark Jordan

    You're almost there: but it is not the fact that function call passes the pointer by value (which is almost always the case with pointers) - it is the fact that box operation creates a copy of the object that is the root of your problem.

    In my example above I call function, f1, with a instance of the Point class p: the compiler sees that the function requires a P^ so it emits the instructions to CLR to box p and then it passes the handle to this boxed P to the function. Think of it as follows in Standard C++;

    struct Point {
       int x;
       int y;
    };

    void f(P* pPoint)
    {
       pPoint->x += 2;
       pPoint->y += 2;
    }

    P p = { 1, 2 };
    P tmp = p;
    f(&tmp);


    In this case here f will update the contents of the variable tmp and not of the original instance p.

    The only way to do this using a pointer is to initially create a boxed value-type:

    Point^ p = new Point(1, 2);

    f1(p);

    But I really cannot recommend this solution as it has its own problems: the major one being that C++/CLI is the only language that can manipulated boxed value-types like this.

  • Tom Schulte

    My last question remains ...

    In C++ a most efficient manner of passing a class instance was via a pointer to that class instance. In C++/CLI what is the most efficient manner of passing a class instance around Is it reference or handle

    THanx.

  • C++/CLI confusion about % vs. ^