Hi,

(I reposted this because I did not saw it appear on the mailing list and added some new info)

I wanted to make a union to map some type to a byte array and back again so I started off like so;

# example class I want to map to a byte array
class N-GtkTargetEntry is repr('CStruct') is export {
  has Str $.target;
  has guint $.flags;
  has guint $.info;

  submethod BUILD ( Str :$target, Int :$!flags, Int :$!info ) {
    $!target := $target;
  }
}


# mapper class/union
class TargetEntryBytes is repr('CUnion') {
  HAS N-GtkTargetEntry $.target-entry;
  HAS CArray[uint8] $.target-bytes;

  multi submethod BUILD ( N-GtkTargetEntry :$target-entry! ) {
    $!target-entry := $target-entry;
  }

  multi submethod BUILD ( CArray[uint8] :$target-bytes! ) {
    $!target-bytes := $target-bytes;
  }
}


The first build method of class N-TargetEntryBytes is doing well and when setting the $!target-entry I can retrieve the bytes from $!target-bytes.

Now, the second build method gives problems. I get nonsense values for the fields of $!target-entry when I set the $!target-bytes.

Then, the next attempt for the BUILD did not improve matters

  multi submethod BUILD ( CArray[uint8] :$target-bytes! ) {
    $!target-bytes := CArray[uint8].new;
    my Int $array-size = nativesizeof(N-GtkTargetEntry);
    for ^$array-size -> $i {
      $!target-bytes[$i] = $target-bytes[$i];
    }
  }

What I think is happening here is that only an address is stored, pointing to an array elsewhere which does not map to the other union member. In this situation, the creation of the array does not take the 'HAS' (uppercased!) attribute declaration into account.


I get a better result when the line '$!target-bytes := CArray[uint8].new;' is replaced by  '$!target-entry := N-GtkTargetEntry.new( :target(''), :flags(0), :info(0));' making space by setting a dummy $!target-entry first and then overwrite the data by writing in $!target-bytes.

Still not correct, the target value in the N-GtkTargetEntry keeps the string value I set, i.e. the empty string ''. Flags and info are now ok.

Final changes shown below give the proper outcome, i.e. the target string, flags and info  in the N-GtkTargetEntryclassare now set to the correct values! (Change is that the init of N-GtkTargetEntry in the 2nd BUILD is called without the :target argument)


class N-GtkTargetEntry is repr('CStruct') is export {
  has Str $.target;
  has guint $.flags;
  has guint $.info;

  submethod BUILD ( Str :$target?, Int :$!flags = 0, Int :$!info = 0 ) {
    $!target := $target if $target.defined;
  }
}

class TargetEntryBytes is repr('CUnion') {
  HAS N-GtkTargetEntry $.target-entry;
  HAS CArray[uint8] $.target-bytes;

  multi submethod BUILD ( N-GtkTargetEntry :$target-entry! ) {
    $!target-entry := $target-entry;
  }

  multi submethod BUILD ( CArray[uint8] :$target-bytes! ) {
    # need to make dummy to create proper space
    $!target-entry := N-GtkTargetEntry.new;

    my Int $array-size = nativesizeof(N-GtkTargetEntry);

    for ^$array-size -> $i {
      $!target-bytes[$i] = $target-bytes[$i];
    }
  }
}



The question: is there an explanation for this behavior because I do not understand it completely why it should be written like this ? It feels that there are some quirks where you need to program around it and afraid that it might change later without warning?


Regards,
Marcel

Reply via email to