Skip to contents

This vignette goes into slightly more detail than the other vignettes about memory representations and C code.

You might be surprised to discover that an ALTREP is actually stored using the same memory layout as an R pairlist, which is the same data structure used to store formal arguments:

paste0 |>
  formals() |>
  mode()
#> [1] "pairlist"

R pairlists hold 3 pointers, (called the CAR, CDR and TAG)1. ALTREPs re-use this structure to point to the three parts of an ALTREP2:

  • CAR -> data1: internal data, as a SEXPREC (normal R object)
  • CDR -> data2: more internal data (also a SEXPREC)
  • TAG -> class: The ALTREP class, as a raw vector with attributes

In addition, ALTREPs are distinguished from other R structures via the ALTREP flag, which is set to 13.

By now you are probably familiar with alt_data1() and alt_data2() for prying into those first two fields. But for the TAG, altrepr actually has another trick up its sleeve: alt_class().

As mentioned, the ALTREP class, not to be confused with the ALTREP class name, is a raw vector. We can look at it, but it’s not particularly interesting:

library(altrepr)
alt_class(1:3)
#>   [1] 50 90 47 87 77 7f 00 00 c0 4c 47 87 77 7f 00 00 e0 49 47 87 77 7f 00 00 b0
#>  [26] 90 47 87 77 7f 00 00 b0 67 47 87 77 7f 00 00 20 4f 47 87 77 7f 00 00 e0 66
#>  [51] 47 87 77 7f 00 00 00 4a 47 87 77 7f 00 00 10 51 47 87 77 7f 00 00 30 4d 47
#>  [76] 87 77 7f 00 00 d0 8d 47 87 77 7f 00 00 10 74 47 87 77 7f 00 00 90 60 47 87
#> [101] 77 7f 00 00 30 4a 47 87 77 7f 00 00 c0 49 47 87 77 7f 00 00 a0 4a 47 87 77
#> [126] 7f 00 00 d0 94 47 87 77 7f 00 00 e0 94 47 87 77 7f 00 00
#> Error in print.default(x): 'getCharCE' must be called on a CHARSXP

One potential utility of this is checking if two objects belong to the same class (although R prefers to compare pointers rather than vector values for this kind of thing)4.

identical(
  alt_class(1:4),
  alt_class(100:90)
)
#> [1] TRUE
identical(
  alt_class(1:4),
  sort(5:1)
)
#> [1] FALSE

Possibly more interesting is the attributes of the class, which is where we get all the juicy details:

1:5 |>
  alt_class() |>
  attributes()
#> [[1]]
#> compact_intseq
#> 
#> [[2]]
#> base
#> 
#> [[3]]
#> [1] 13

Notice that this is the source for many of the fields returned by alt_details:

alt_details(1:5)
#> $class_name
#> [1] "compact_intseq"
#> 
#> $pkg_name
#> [1] "base"
#> 
#> $base_type
#> [1] "integer"
#> 
#> $data1
#> [1] 5 1 1
#> 
#> $data2
#> NULL