5

In the C++ source file below, in function glfw_get_monitors how to set the class attribute of each element of the object monitor_ptrs?

The line monitor_ptrs[i].attr("class") = "GLFWmonitor"; throws a compilation error:

‘Rcpp::Vector<19>::Proxy’ {aka ‘class Rcpp::internal::generic_proxy<19>’} has no member named ‘attr’

glfw_types.h

#ifndef RCPP_GLFW_TYPES_H
#define RCPP_GLFW_TYPES_H

#include <Rcpp.h>
#include <GLFW/glfw3.h>

void glfw_destroy_monitor(GLFWmonitor*);

// https://stackoverflow.com/questions/41210595/s4-object-with-a-pointer-to-a-c-struct

typedef Rcpp::XPtr<GLFWwindow, Rcpp::PreserveStorage, glfwDestroyWindow> GLFWwindow_ptr;
typedef Rcpp::XPtr<GLFWmonitor, Rcpp::PreserveStorage, glfw_destroy_monitor> GLFWmonitor_ptr;


#endif

C++ source file

#include "glfw_types.h"
using namespace Rcpp;

// [[Rcpp::export]]
GLFWmonitor_ptr glfw_get_primary_monitor() {

  GLFWmonitor_ptr new_monitor = GLFWmonitor_ptr(glfwGetPrimaryMonitor(), true);

  new_monitor.attr("class") = "GLFWmonitor";

  return new_monitor;
}

// [[Rcpp::export]]
Rcpp::List glfw_get_monitors() {

  int nr_monitors;
  GLFWmonitor** monitors = glfwGetMonitors(&nr_monitors);
  Rcpp::List monitor_ptrs(nr_monitors);

  for(int i = 0; i < nr_monitors; i++) {
    monitor_ptrs[i] = GLFWmonitor_ptr((GLFWmonitor*)monitors[i], true);
    monitor_ptrs[i].attr("class") = "GLFWmonitor";
  }

  return monitor_ptrs;
}

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Ramiro Magno
  • 3,085
  • 15
  • 30
  • 1
    See the Rcpp Gallery, one of the very first posts. You just assign to `.attr()`. But you can only assign known and implemented R classes. – Dirk Eddelbuettel Oct 10 '19 at 01:23
  • When you say to assign to `.attr()`, isn't that what I am doing here `monitor_ptrs[i].attr("class") = "GLFWmonitor";`? And what do you mean by *assign known and implemented R classes*? An S3 class isn't it just an object whose class attribute has been set? I know it's very informal but that's R, is it not? In the function `glfw_get_primary_monitor` I do just that and it seems to work just fine. I'm getting an external pointer with the class attribute set as `"GLFWmonitor"`... With `monitor_ptrs[i].attr("class") = "GLFWmonitor";` is not working but I'm guessing it might be the syntax... – Ramiro Magno Oct 10 '19 at 01:34
  • Do you have an R class `GLFWmonitor`? – Dirk Eddelbuettel Oct 10 '19 at 01:45
  • No. It's really just the class attribute. Then, other functions (on the R side) that use these external pointers check for the class attribute. It's just that. – Ramiro Magno Oct 10 '19 at 01:51
  • Just to clarify, in the C++ side, `GLFWmonitor` is a struct defined externally by the C library GLFW that I access via `#include `. The same applies to `GLFWwindow`. So I'm trying just to create external pointers with the class attribute of the corresponding C struct. In this last example where the compilation error happens, I'm trying to return a list to the R side with a bunch of these external pointers, each having the class attribute set to `"GLFWmonitor"`... – Ramiro Magno Oct 10 '19 at 01:53

1 Answers1

5

The problem here is when you're trying to assign the class. Consider the following more minimal example:

#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::List foo() {
    // Setup the list
    Rcpp::List result(1);
    // Setup the object that will go in the list
    Rcpp::IntegerVector x = Rcpp::seq(1, 10);
    // Your approach was to add it to the list, THEN set the class attribute
    result[0] = x;
    result[0].attr("class") = "bar";
    return result;
}

enter image description here

You can't directly add the class to the element access syntax like that. However, you can class an object, then add it to the list:

#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::List foo() {
    // Setup the list
    Rcpp::List result(1);
    // Setup the object that will go in the list
    Rcpp::IntegerVector x = Rcpp::seq(1, 10);
    // Your approach was to add it to the list, THEN set the class attribute
    // result[0] = x;
    // result[0].attr("class") = "bar";
    // What we need to do is set the class of that object
    x.attr("class") = "bar";
    // BEFORE adding it to the list
    result[0] = x;
    return result;
}

/*** R
foo()
*/
foo()
[[1]]
 [1]  1  2  3  4  5  6  7  8  9 10
attr(,"class")
[1] "bar"

Update

Take note that this is an issue in many other contexts with Rcpp::Lists as well. Consider the following:

#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::List baz() {
    Rcpp::List result(1);
    Rcpp::IntegerVector x = Rcpp::seq(1, 10);
    result[0] = x;
    Rcpp::Rcout << result[0][1] << std::endl;
    result[0][2] += 1;
    return result;
}

enter image description here

Compare this with using an std::vector of Rcpp::IntegerVectors:

#include <Rcpp.h>

// [[Rcpp::export]]
void qux() {
    std::vector<Rcpp::IntegerVector> result(1);
    Rcpp::IntegerVector x = Rcpp::seq(1, 10);
    result[0] = x;
    Rcpp::Rcout << result[0][0] << std::endl;
    result[0][2] += 1;
    Rcpp::Rcout << result[0] << std::endl;
}
qux()
1
1 2 4 4 5 6 7 8 9 10

As discussed in several places (will try to come back later and add some links), you generally have to be more explicit when it comes to an Rcpp::List because its elements could be almost anything.

Ramiro Magno
  • 3,085
  • 15
  • 30
duckmayr
  • 16,303
  • 3
  • 35
  • 53
  • Thank you. But do you know why `result[0]` is not showing referential transparency? Is it because of Rcpp syntactic sugar? – Ramiro Magno Oct 10 '19 at 11:55
  • 1
    @rmagno Yeah, so `Rcpp::List`s are difficult in this regard.... **Anything** basically could be in a `List` element, so you can't interact with them directly. Will update the answer to demonstrate this in some other contexts as well. – duckmayr Oct 10 '19 at 12:00
  • 1
    @rmagno Update added – duckmayr Oct 10 '19 at 12:14