How to identify the object type in variant holding an array?

Tried several variations, but I either get a TypeMismatch exception or don’t find a logical way…

I have a variable of type variant (can’t avoid variants in my case).
This variable may hold anything, including a single-dimensional array. I have to write its content to a BinaryStream.

My current code works fine as long as the variant doesn’t hold an array of objects:

 dim j As Integer
     
 if bs<>nil then
   j=VarType(Value)
   bs.WriteInt32 j
   Select case j
   case 11 'Boolean
     bs.WriteBoolean Value
   case 9 'Object
     if Value isa Point then
       bs.WriteInt8 16
       bs.WriteDouble Point(Value).X
       bs.WriteDouble Point(Value).Y
     elseif Value isa Picture then
       bs.WriteInt8 21
       bs.WritePicture Value
     Else
       bs.WriteInt8 0
       dim ex As new RuntimeException("Object type not yet supported.",-50)
       Raise ex
     end if
   case 5 'Double
     bs.WriteDouble Value

(and so on for non-array values. Next, for arrays:)

       case 4106 'Array of booleans
         dim ac,ai As Integer
         dim av() As Boolean
         
         av=Value
         ac=av.LastIndex
         bs.WriteInt32 ac
         for ai=0 to ac
           bs.WriteBoolean av(ai)
         next
         bs.WriteInt8 39 'Marker
       case 4098 'Array of integers
         dim ac,ai,av() As Integer
         
         av=Value
         ac=av.LastIndex
         bs.WriteInt32 ac
         for ai=0 to ac
           bs.WriteInt32 av(ai)
         next
         bs.WriteInt8 39

But, for an array of objects, I have to know which class of objects it holds so I can save each object accordingly.
VarType doesn’t know about my custom classes, so it’s out. I don’t think introspection is good for that either. “IsA” doesn’t work for an array, since it would return the fact it’s an array. I haven’t found another function.

My current code looks like this (but doesn’t work):

case 4105 'Value contains an array of objects
  dim vl() as Variant
  
  vl=Value
  if UBound(vl)=-1 then 'Empty. Write “9” to the stream (when read back, “9” will have this meaning)
    bs.WriteInt8 9
  Else
    for each v as Variant in vl
      bs.WriteInt8 1 //One more object to write
      WriteVariantToStream bs,v //Recursion with only a single object
    next
    bs.WriteInt8 0 //No more objects to save
  end if

This compiles fine, but I get a TypeMismatch exception at “vl=Value”, because vl is an array of variants and Value is an array of MyCustomClass.

I could have code like this:

dim vp() as Point
dim vm() as MyClass
dim vi() as Picture

if UBound(Value)=-1 then
'Empty
else
dim v as Variant=Value(0) 'Now I know the type hold in the array

select case true
case v isa Point
vp=Value
//Save each point
case v isa MyClass
vm=Value
//Save each object
case v isa Picture
ci=Value
//Save each picture
…

But this looks clunky.

I’m missing something obvious or it just must be clunky like that?

In addition, the code which I thought would work (the last block in my previous post) doesn’t even compile.
The line “if UBound(Value)=-1 then” is being reported as “Parameters are not compatible with this function”/“This is not an array but you are using it as one”.

So this is even trickier than I originally thought. Assigning the first value of the array to a variable of type variant will fail if the array is empty (OutOfBounds exception) but I can’t know whether the array is empty either. So I see no way to know the type of objects being in the array at all (other than trying with a variable and handling an exception when the array is empty, but that’s just bad).

Perhaps a known limitation, but I don’t know how to go further.

Please check variant helper functions in MBS Plugin:

Variant Helper functions in MBS Xojo Plugins

GetVariantArrayUboundMBS and GetVariantArrayValueMBS may help you a lot.

1 Like

Again, a great thank to you and your plugin!
Yes, this works perfectly and is accurately replacing the missing call in Xojo.

I’m a bit late, but did you know that code like this works?

var dArr() as Dictionary
dArr.AddRow new Dictionary

var a as auto = dArr
var oArr() as object = a
1 Like

I’d not say I didn’t know, but I’ve not thought of this. Converting the variant to an array of objects (i.e. of type “object”) and then the variable is truly an array, providing all the available methods on it.

Smart way, indeed. Since the “object” class is the base of any object (obvious), the assignment works (we already know it’s an array of objects using the VarType).

Thank you; great piece of code.

BTW, we should be able to do this directly from a Variant, but that results in a TypeMismatchException. I filed a bug report.

<https://xojo.com/issue/62616>

We have a couple of older cases for that.
But as Xojo hasn’t done anything in that regard for years, I got a couple of nice plugin functions to work around this.

e.g. 12213: Assigning a Variant containing an Array to anything other than the array’s EXACT type generates a TypeMismatchException

Thank you.

Currently, my impression is that Xojo is overwhelmed by these left-open cases, which are growing; even some of the new ones that are duplicate are left open.
I’m wondering how many duplicates (not closed) are in Feedback as of now.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.