File: rinside_sample17.cpp

package info (click to toggle)
r-cran-rinside 0.2.19-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 668 kB
  • sloc: cpp: 3,310; ansic: 117; xml: 57; ruby: 34; makefile: 2
file content (169 lines) | stat: -rw-r--r-- 5,084 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*-
//
// More elaborate examples for exposing functions using C++11
//
// Copyright (C) 2014 Christian Authmann

#include <iostream>

#include <RcppCommon.h>

#include <memory>

/*
 * We have a simple data type with two values.
 *
 * Just to make it less simple (and more educational), this class is not copyable,
 * preventing it from being used as a function parameter or return type.
 */
class Foo {
    public:
        Foo(int a, int b) : a(a), b(b) {
        }
        ~Foo() {
        }

    private:
        Foo(const Foo &f) : a(f.a), b(f.b) {
            throw "Cannot copy construct Foo";
        }

        Foo &operator=(const Foo &f) {
            throw "Cannot copy assign Foo";
        }

    public:
        int a, b;
};


/*
 * We define converters between Foo and R objects, see
 * http://cran.r-project.org/web/packages/Rcpp/vignettes/Rcpp-extending.pdf
 *
 * These template declarations must be after RcppCommon.h and before Rcpp.h
 * The implementation can follow later, when all of Rcpp/Rinside is available.
 *
 * Since Foo is not copyable, we need a workaround. Instead of passing Foo
 * directly, we pass C++11's std::unique_ptr<Foo> - which is movable.
 * Note that the older std::auto_ptr does not work.
 */
namespace Rcpp {
    template<> SEXP wrap(const Foo &f);
    template<> SEXP wrap(const std::unique_ptr<Foo> &f);
    template<> std::unique_ptr<Foo> as(SEXP sexp);
}

#include <Rcpp.h>
#include <RInside.h>


/*
 * After including Rcpp/Rinside, we can implement the converters.
 */

// An implementation for unique_ptr
template<> SEXP Rcpp::wrap(const std::unique_ptr<Foo> &f) {
    return Rcpp::wrap(*f);
}

// And an implementation for a non-wrapped object
template<> SEXP Rcpp::wrap(const Foo &f) {
    Rcpp::List list;

    list["a"] = f.a;
    list["b"] = f.b;

    return Rcpp::wrap(list);
}

// Converting the R object back to a C++ object will always return a unique_ptr
template<> std::unique_ptr<Foo> Rcpp::as(SEXP sexp) {
    Rcpp::List list = Rcpp::as<Rcpp::List>(sexp);
    int a = list["a"];
    int b = list["b"];

    // With c++14, we'd use std::make_unique<Foo>(a, b) here
    return std::unique_ptr<Foo>(new Foo(a, b));
}


// C++ functions we wish to expose to R
std::unique_ptr<Foo> swapFoo(std::unique_ptr<Foo> input) {
    return std::unique_ptr<Foo>(new Foo(input->b, input->a));
}

std::unique_ptr<Foo> addFoo(std::unique_ptr<Foo> foo1, std::unique_ptr<Foo> foo2) {
    return std::unique_ptr<Foo>(new Foo(foo1->a + foo2->a, foo1->b + foo2->b));
}

/*
 * Let's also assume that we have some kind of data source. We want R scripts to be able
 * to query the database without actually exposing the database class.
 */
class FooDatabase {
    public:
        FooDatabase(int database_id) : database_id(database_id) {
        }
        // R scripts will want to call this..
        std::unique_ptr<Foo> queryFoo(int id) {
            return std::unique_ptr<Foo>(new Foo(database_id, id));
        }
        // ..but really should not be allowed call this.
        void destroyDatabase() {
            throw "boom!";
        }
    private:
        int database_id;
};


int main(int argc, char *argv[]) {
    // create an embedded R instance
    RInside R(argc, argv);

    // expose the "swapFoo" and "addFoo" functions in the global environment
    R["swapFoo"] = Rcpp::InternalFunction( &swapFoo );
    R["addFoo"] = Rcpp::InternalFunction( &addFoo );

    // We can also expose C++11's std::function, for example to grant access to these three "databases"
    FooDatabase db1(1), db2(2), db3(3);

    // All data from DB1 can be queried
    std::function< std::unique_ptr<Foo>(int) > queryDB1 = std::bind(&FooDatabase::queryFoo, std::ref(db1), std::placeholders::_1);
    R["queryDB1"] = Rcpp::InternalFunction( queryDB1 );

    // DB2 shall only be queried with id=42
    std::function< std::unique_ptr<Foo>() > queryDB2 = std::bind(&FooDatabase::queryFoo, std::ref(db2), 42);
    R["queryDB2"] = Rcpp::InternalFunction( queryDB2 );

    // For DB3, let's do some more complicated permission checks. That's a good excuse to use a lambda.
    std::function< std::unique_ptr<Foo>(int) > queryDB3 =
        [&db3] (int id) -> std::unique_ptr<Foo> {
            if (id < 0 || id > 20)
                throw "id out of allowed range";
            return db3.queryFoo(id);
        };
    R["queryDB3"] = Rcpp::InternalFunction( queryDB3 );


    std::unique_ptr<Foo> result = R.parseEvalNT(
        "foo1 = queryDB1(20);"
        //"print(foo1);" // a=1, b=20
        "foo2 = queryDB2();"
        //"print(foo2);" // a=2, b=42
        "foo3 = queryDB3(10);"
        //"print(foo3);" // a=3, b=10

        "foo1 = swapFoo(foo1);"
        //"print(foo1);" // a=20, b=1
        "foo = addFoo(foo1, addFoo(foo2, foo3));"
        //"print(foo);"  // a=25, b=53

        "foo;" // return the object
    );

    std::cout << "    Got result a=" << result->a << ", b=" << result->b << std::endl;
    std::cout << "    Expected   a=25, b=53" << std::endl;
}