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 ?

+2


source to share


4 answers


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.

+4


source


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];

      

0


source


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

.

0


source


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;
}

      

0


source







All Articles