Does .NET IL support copy by reference or by value? - cil

The only .NET language i know is C#. In C# you can write lhs=rhs and if its a struct it will copy by value, if a class it copies by reference.
Does the .NET CLI support doing either on any type of object? Can i create a struct Pt { int x, y; } and do something like
Pt pt
var pt_ref=&pt
pt_ref.x=99 //pt.x is now 99
var pt_cpy=pt
pt_cpy.x=88 //nothing else has changed

At the IL level references to value types are a possibility. C# disallows them (except in parameters, using the ref keyword), but in C++/CLI you can write your example:
value struct Pt
{
int x;
int y;
};
void f()
{
Pt pt;
Pt% ptRef = pt;
ptRef.x = 99;
Pt ptCpy = pt;
ptCpy.x = 88;
}
However, the other way around is not possible. If you have a reference type, and you create a copy of that, you create a copy of the reference itself, not of the referenced object. If you want to copy the referenced object, you need to write a function to copy it yourself.

Related

How to use global variable in Objective c without struct or enum

var token : String { get and set}
Can any tell how to use the same thing in Objective C. In swift I can use any where in the project to access the token. I want the same thing to be done..
First, you should avoid to use global var with OOP.
If you really need to use global var, make it become an instance var in appdelegate.
If you don't want to put it in appdelegate, create a source file and declare as static var could solve it, remember access it by accessor, Never access global var directly.
GlobalVar.h
void setGlobalVar(int var);
int getGlobalVar(void);
GlobalVar.m
static int s_globalVar = 0;
void setGlobalVar(int var) {
s_globalVar = var;
}
int getGlobalVar(void) {
return s_globalVar;
}

2 dimensional array of managed object c++

I am attempting to create a grid of hexagons on a windows form.
To do this i have created a 'hexagon' class with the header file as follows:
ref class Hexagon
{
public:
Hexagon(int X, int Y, int I, int J);
Hexagon();
private:
array<Point>^ vertices;
Point Centre;
int i, j;
public:
int GetX();
int GetY();
void SetCentre(int X, int Y);
void CalculateVertices();
array<Point>^ GetVertices();
void drawHexagon();
};
i then want to have a 2 dimensional array storing these hexagons. my attempt at doing this is as follows:
array<Hexagon^,2>^ Grid
but i get the 'a variable with static storage duration cannot have a handle or tracking reference type'
how do i create a 2d array to add hexagons to?
A ref class declares a class which is managed by the garbage collector. One strong restriction that the C++/CLI compiler applies to such declarations is that such class cannot contain non-managed objects. That very often turns out poorly when the object is moved when the heap is compacted, invalidating unmanaged pointers to such non-managed objects.
The likely trouble-maker is the Point type, there are no other candidates. A sample declaration for a managed Point type that doesn't have this problem is:
public value struct Point {
int x, y;
};
Or use the baked-in System::Drawing::Point type instead.

C++/CLI: how to overload an operator to accept reference types?

I am trying to create a CLI value class c_Location with overloaded operators, but I think I have an issue with boxing. I have implemented the operator overloading as seen in many manuals, so I'm sure this must be right.
This is my code:
value class c_Location
{
public:
double x, y, z;
c_Location (double i_x, double i_y, double i_z) : x(i_x), y(i_y), z(i_z) {}
c_Location& operator+= (const c_Location& i_locValue)
{
x += i_locValue.x;
y += i_locValue.y;
z += i_locValue.z;
return *this;
}
c_Location operator+ (const c_Location& i_locValue)
{
c_Location locValue(x, y, z);
return locValue += i_locValue;
}
};
int main()
{
array<c_Location,1>^ alocData = gcnew array<c_Location,1>(2);
c_Location locValue, locValue1, locValue2;
locValue = locValue1 + locValue2;
locValue = alocData[0] + alocData[1]; // Error C2679 Binary '+': no operator found which takes a right-hand operand of type 'c_Location'
}
After searching for a longer time, I found that the error comes from the operand being a reference type, as it is an array element of a value type, and the function accepting only value types as it takes an unmanaged reference. I now have 2 possibiblities:
adding a unboxing cast to c_Location and so changing the faulty line in main() to
locValue = alocData[0] + (c_Location)alocData[1];
modifying the operator+ overloading so that it takes the parameter by value instead of by reference:
c_Location operator+ (const c_Location i_locValue)
both options work, but as far as I can see, they both have disadvantages:
opt 1 means that I have to explicitly cast wherever needed.
opt 2 means that the function will create a copy of the parameter on its call and therefore waste performance (not much though).
My questions: Is my failure analysis correct at all or does the failure have another reason?
Is there a better third alternative?
If not: which option, 1 or 2, is the better one? I currently prefer #2.
The rules are rather different from native C++:
the CLI demands that operator overloads are static members of the class
you can use the const keyword in C++/CLI but you get no mileage from it, the CLI does not support enforcing const-ness and there are next to no other .NET languages that support it either.
passing values of a value type ought to be done by value, that's the point of having value types in .NET in the first place. Using a & reference is very troublesome, that's a native pointer at runtime which the garbage collector cannot adjust. You'll get a compile error if you try to use your operator overload on a c_Location that's embedded in a managed class. If you want to avoid value copy semantics then you should declare a ref class instead. The hat^ in your code.
any interop type you create in C++/CLI should be declared public so it is usable from other assemblies and .NET languages. It isn't entirely clear if that's your intention, it is normally the reason you write C++/CLI code.
You could make your value class look like this instead:
public value class c_Location
{
public:
double x, y, z;
c_Location (double i_x, double i_y, double i_z) : x(i_x), y(i_y), z(i_z) {}
static c_Location operator+= (c_Location me, c_Location rhs)
{
me.x += rhs.x;
me.y += rhs.y;
me.z += rhs.z;
return me;
}
static c_Location operator+ (c_Location me, c_Location rhs)
{
return c_Location(me.x + rhs.x, me.y + rhs.y, me.z + rhs.z);
}
};
Untested, ought to be close. You'll now see that your code in main() compiles without trouble.
TL;DR version:
For managed code, use % for a pass by reference parameter, not &
You diagnosis is not completely correct. Boxing has nothing to do with your problem. But reference types do, in a way.
You were really close when you said that "I found that the error comes from the operand being a reference type". Well, the operand is a value type not a reference type. But the error occurs when the operand is stored inside a reference type, because then it's inside the garbage-collected heap (where all instances of reference types are placed). This goes for arrays as well as your own objects which contain a member of value type.
The danger is that when the garbage collector runs, it can move items around on the gc heap. And this breaks native pointers (*) and references (&), because they store the address and expect it to stay the same forever. To handle this problem, C++/CLI provides tracking pointers (^) and tracking references (%) which work together with the garbage collector to do two things:
make sure the enclosing object isn't freed while you're using it
find the new address if the garbage collector moves the enclosing object
For use from C++/CLI, you can make operator+ a non-member, just like normal C++.
value class c_Location
{
public:
double x, y, z;
c_Location (double i_x, double i_y, double i_z) : x(i_x), y(i_y), z(i_z) {}
c_Location% operator+= (const c_Location% i_locValue)
{
x += i_locValue.x;
y += i_locValue.y;
z += i_locValue.z;
return *this;
}
};
c_Location operator+ (c_Location left, const c_Location% right)
{
return left += right;
}
The drawback is that C# won't use non-members, for compatibility with C#, write it like a non-member operator (with two explicit operands) but make it a public static member.
value class c_Location
{
public:
double x, y, z;
c_Location (double i_x, double i_y, double i_z) : x(i_x), y(i_y), z(i_z) {}
c_Location% operator+= (const c_Location% i_locValue)
{
x += i_locValue.x;
y += i_locValue.y;
z += i_locValue.z;
return *this;
}
static c_Location operator+ (c_Location left, const c_Location% right)
{
return left += right;
}
};
There's no reason to worry about this for operator+= since C# doesn't recognize that anyway, it will use operator+ and assign the result back to the original object.
For primitive types like double or int, you may find that you need to use % also, but only if you need a reference to an instance of that primitive type is stored inside a managed object:
double d;
array<double>^ a = gcnew darray<double>(5);
double& native_ref = d; // ok, d is stored on stack and cannot move
double& native_ref2 = a[0]; // error, a[0] is in the managed heap, you MUST coordinate with the garbage collector
double% tracking_ref = d; // ok, tracking references with with variables that don't move, too
double% tracking_ref2 = a[0]; // ok, now you and the garbage collector are working together

c++/cli reference to property

Well, I haven't yet found something that says this is impossible, though I'm starting to think it might be. Can you make this work?
using namespace System;
template <typename T>
void unset(Nullable<T>& var) { var = Nullable<T>(); }
void unset(String^% var) { var=nullptr; }
//this is really a C# class in my situation, so I can't change its types
public ref class Foo
{
public:
property Nullable<Decimal> Dec;
property Nullable<int> Num;
property String^ Str;
};
int main()
{
Foo^ foo = gcnew Foo;
foo->Dec = Decimal(1.2);
foo->Num = 3;
foo->Str = "hi";
unset(foo->Dec);
unset(foo->Num);
unset(foo->Str);
Console::WriteLine(foo->Dec);
Console::WriteLine(foo->Num);
Console::WriteLine(foo->Str);
}
Update: unset is called from a code-generating macro which is called on about 50 params. I'd prefer not to have to go make varieties of the macro for each type.
It isn't possible. Setting a property requires calling the property setter function. There is no way to guess for the called method that it needs to call a function vs can assign the passed variable pointer. If you really want to do this then pass a delegate.
There is actually one .NET language that supports it, VB.NET generates code like this:
T temp = obj->prop;
func(temp)
obj->prop = temp;
There is however a dreadful aliasing problem with that, quite undebuggable. This goes belly up in the (rare) case where func() also uses the property. This is otherwise the way you'd work around the limitation, explicitly in your own code.
Beware that your code is wrong, possibly intentional, you are passing a C++ & reference, not a managed % interior pointer. The compiler is going to bitch about that, you can't create references or pointers to managed objects. They move. Unless the reference is to a variable on the stack. It doesn't otherwise change the answer.
For those who may end up here wondering how I got on with this, I ended up being lucky that the class I was working with was an LLBLGen Entity, so I was able to replace
unset(re->var);
with
{ SD::LLBLGen::Pro::ORMSupportClasses::IEntityField2^ f = re->Fields[#var]; \
if (f->IsNullable) \
f->CurrentValue = nullptr; }

Tracking reference in C++/CLI

Can someone please explain me the following code snippet?
value struct ValueStruct {
int x;
};
void SetValueOne(ValueStruct% ref) {
ref.x = 1;
}
void SetValueTwo(ValueStruct ref) {
ref.x = 2;
}
void SetValueThree(ValueStruct^ ref) {
ref->x = 3;
}
ValueStruct^ first = gcnew ValueStruct;
first->x = 0;
SetValueOne(*first);
ValueStruct second;
second.x = 0;
SetValueTwo(second); // am I creating a copy or what? is this copy Disposable even though value types don't have destructors?
ValueStruct^ third = gcnew ValueStruct;
third->x = 0;
SetValueThree(third); // same as the first ?
And my second question is: is there any reason to have something like that?:
ref struct RefStruct {
int x;
};
RefStruct% ref = *gcnew RefStruct;
// rather than:
// RefStruct^ ref = gcnew RefStruct;
// can I retrieve my handle from ref?
// RefStruct^ myref = ???
What is more: I see no difference between value type and ref type, since both can be pointed by handler ;(
Remember that the primary use of C++/CLI is for developing class libraries for consumption by GUIs / web services built in other .NET languages. So C++/CLI has to support both reference and value types because other .NET languages do.
Furthermore, C# can have ref parameters that are value typed as well, this isn't unique to C++/CLI and it doesn't in any way make value types equivalent to reference types.
To answer the questions in your code comments:
am I creating a copy or what?
Yes, SetValueTwo takes its parameter by value, so a copy is made.
is this copy Disposable even though value types don't have destructors?
Incorrect. Value types can have destructors. Value types cannot have finalizers. Since this particular value type has a trivial destructor, the C++/CLI compiler will not cause it to implement IDisposable. In any case, if a parameter is an IDisposable value type, the C++/CLI compiler will ensure that Dispose is called when the variable goes out of scope, just like stack semantics for local variables. This includes abnormal termination (thrown exception), and allows managed types to be used with RAII.
Both
ValueStruct% ref = *gcnew ValueStruct;
and
ValueStruct^ ref = gcnew ValueStruct;
are allowed, and put a boxed value type instance on the managed heap (which isn't a heap at all, but a FIFO queue, however Microsoft chooses to call it a heap like the native memory area for dynamic allocation).
Unlike C#, C++/CLI can keep typed handles to boxed objects.
If a tracking reference is to a value type instance on the stack or embedded in another object, then the value type content has to be boxed in the process of formed the reference.
Tracking references can also be used with reference types, and the syntax to obtain a handle is the same:
RefClass^ newinst = gcnew RefClass();
RefClass% reftoinst = *newinst;
RefClass^% reftohandle = newinst;
RefClass stacksem;
RefClass^ ssh = %stacksem;
One thing that I can never seem to remember completely is that the syntax isn't 100% consistent compared to native C++.
Declare a reference:
int& ri = i; // native
DateTime% dtr = dt; // managed tracking reference
Declare a pointer:
int* pi; // native
Stream^ sh; // tracking handle
Form a pointer:
int* pi = &ri; // address-of native object
DateTime^ dth = %dtr; // address-of managed object
Note that the unary address-of operator is the same as the reference notation in both standard C++ and C++/CLI. This seems to contradict a tracking reference cannot be used as a unary take-address operator (MSDN) which I'll get back to in a second.
First though, the inconsistency:
Form a reference from a pointer:
int& iref = *pi;
DateTime% dtref = *dth;
Note that the unary dereference operator is always *. It is the same as the pointer notation only in the native world, which is completely opposite of address-of which, as mentioned above, are always the same symbol as the reference notation.
Compilable example:
DateTime^ dth = gcnew DateTime();
DateTime% dtr = *dth;
DateTime dt = DateTime::Now;
DateTime^ dtbox = %dt;
FileInfo fi("temp.txt");
// FileInfo^ fih = &fi; causes error C3072
FileInfo^ fih = %fi;
Now, about unary address-of:
First, the MSDN article is wrong when it says:
The following sample shows that a tracking reference cannot be used as a unary take-address operator.
The correct statement is:
% is the address-of operator for creation of a tracking handle. However its use is limited as follows:
A tracking handle must point to an object on the managed heap. Reference types always exist on the managed heap so there is no problem. However, value types and native types may be on the stack (for local variables) or embedded within another object (member variables of value type). Attempts to form a tracking handle will form a handle to a boxed copy of the variable: the handle is not linked to the original variable. As a consequence of the boxing process, which requires metadata which does not exist for native types, it is never possible to have a tracking handle to an instance of a native type.
Example code:
int i = 5;
// int^ ih = %i; causes error C3071
System::Int32 si = 5;
// System::Int32^ sih = %si; causes error C3071
// error C3071: operator '%' can only be applied to an instance
// of a ref class or a value-type
If System::Int32 isn't a value type then I don't know what is. Let's try System::DateTime which is a non-primitive value type:
DateTime dt = DateTime::Now;
DateTime^ dtbox = %dt;
This works!
As a further unfortunate restriction, primitive types which have dual identity (e.g. native int and managed value type System::Int32) are not handled correctly, the % (form tracking reference) operator cannot perform boxing even when the .NET name for the type is given.