C ++ / CLI: Boxing and Shared Lists
I am trying to create a generic list of object references PointF
. (No, I'm not going to create a generic list of objects PointF
.) However, the following line won't compile:
Generic::List<PointF^> ^pointList; // Generates error C3225
On the other hand, creating an array of links PointF
works without issue as follows:
array<PointF^> ^points = gcnew array<PointF^>;
Here's a sample program:
using namespace System;
using namespace System::Drawing;
namespace Generic = System::Collections::Generic;
int main(array<System::String ^> ^args)
{
array<PointF^> ^points = gcnew array<PointF^>{
nullptr, PointF(0.0f, 0.0f), PointF(1.0f, 0.0f), nullptr
};
Generic::List<PointF^> ^pointList;
Console::WriteLine(L"Hello World");
return 0;
}
How do I create a general list of links PointF
? In other words, how do I create a generic list of PointF
s nested packages ?
source to share
This is a .Net generic limit that only accepts a CLI-complient type such as a value type or reference to a reference type. It does not accept C ++ / CLI types, such as stack semantics for ref types (which compiles to deterministic rework) or, in your case, a reference to a boxed value type.
The array is native to the CLI and does not have this limitation.
source to share
PointF
is not a class, it is a structure. You cannot have structure references without boxing inside the object.
You can either have a list of links Object
, or disable the link PointF
whenever you use it, or a list of a custom class that encapsulates the value PointF
.
With implicit conversions and from a value, PointF
you can make a transparent box and unboxing. I'm not sure how you write it in C ++, but in C # it would look like this:
public class PointFObject {
// encapsulated PointF structure
private PointF _value;
// constructor
public PointFObject(PointF value) {
_value = value;
}
// implicit conversion to a PointF value
public static implicit operator PointF(PointFObject obj) {
return obj._value;
}
// implicit conversion from a PointF value
public static implicit operator PointFObject(PointF value) {
return new PointFObject(value);
}
}
Now you can create a list of PointFObject objects and access them as a list of PointF values:
List<PointFObject> pointList = new List<PointFObject>();
pointList.Add(new PointF(0f, 0f));
PointF p = pointList[0];
source to share
Even though the storage location of the type PointF
and the referenced heap object PointF^
are different types of things, they are both described by the same object Type
. The system usually decides which type it represents based on how the type is used. If the type is PointF
used to describe a storage location, that storage location will be allocated to hold the structure. The C ++ / CLI compiler can resolve variable declaration PointF^
, but the Framework has no idea about such a thing. In C ++ / CLI code, the compiler can use a type Object
store to store a reference to a heap object, PointF
or it can useObject[]
to store a bunch of such links; if such locations are never exposed to the outside world, and the compiler never stores anything other than references PointF
, the compiler can know that the target of any non-null reference can be safely used as PointF
. The compiler cannot expose such storage locations to external code, however, because the type system offers no indication that other code should be limited to storing references PointF
.
source to share
As others have noted, typical types only accept parameters of the CLS type. Since it is PointF^
not CLS compliant, it List<PointF^>
is invalid. array<>
avoids this problem by being a template type rather than a generic type.
However, there is a (rather simple) workaround: create List<Nullable<PointF>>
. Then your sample program will look like this:
using namespace System;
using namespace System::Drawing;
namespace Generic = System::Collections::Generic;
int main(array<System::String ^> ^args)
{
array<Nullable<PointF>> ^points = gcnew array<Nullable<PointF>> {
Nullable<PointF>(), Nullable<PointF>(PointF(0.0f, 0.0f)), Nullable<PointF>(PointF(1.0f, 0.0f)), Nullable<PointF>()
};
Generic::List<Nullable<PointF>> pointList(points);
pointList.Add(PointF(2., 0.));
Console::WriteLine(L"Hello World");
return 0;
}
source to share