Skip to content

Commit b138ff8

Browse files
committed
ordered and unordered C++ maps are converted to R lists - replaces r-lib#437
1 parent c9def8e commit b138ff8

File tree

9 files changed

+1446
-1451
lines changed

9 files changed

+1446
-1451
lines changed

cpp11test/R/cpp11.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ cpp11_insert_ <- function(num_sxp) {
8888
.Call(`_cpp11test_cpp11_insert_`, num_sxp)
8989
}
9090

91+
ordered_map_to_list_ <- function(x) {
92+
.Call(`_cpp11test_ordered_map_to_list_`, x)
93+
}
94+
95+
unordered_map_to_list_ <- function(x) {
96+
.Call(`_cpp11test_unordered_map_to_list_`, x)
97+
}
98+
9199
gibbs_cpp <- function(N, thin) {
92100
.Call(`_cpp11test_gibbs_cpp`, N, thin)
93101
}

cpp11test/src/cpp11.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,20 @@ extern "C" SEXP _cpp11test_cpp11_insert_(SEXP num_sxp) {
173173
return cpp11::as_sexp(cpp11_insert_(cpp11::as_cpp<cpp11::decay_t<SEXP>>(num_sxp)));
174174
END_CPP11
175175
}
176+
// map.cpp
177+
SEXP ordered_map_to_list_(cpp11::doubles x);
178+
extern "C" SEXP _cpp11test_ordered_map_to_list_(SEXP x) {
179+
BEGIN_CPP11
180+
return cpp11::as_sexp(ordered_map_to_list_(cpp11::as_cpp<cpp11::decay_t<cpp11::doubles>>(x)));
181+
END_CPP11
182+
}
183+
// map.cpp
184+
SEXP unordered_map_to_list_(cpp11::doubles x);
185+
extern "C" SEXP _cpp11test_unordered_map_to_list_(SEXP x) {
186+
BEGIN_CPP11
187+
return cpp11::as_sexp(unordered_map_to_list_(cpp11::as_cpp<cpp11::decay_t<cpp11::doubles>>(x)));
188+
END_CPP11
189+
}
176190
// matrix.cpp
177191
SEXP gibbs_cpp(int N, int thin);
178192
extern "C" SEXP _cpp11test_gibbs_cpp(SEXP N, SEXP thin) {
@@ -500,6 +514,7 @@ static const R_CallMethodDef CallEntries[] = {
500514
{"_cpp11test_my_warning_n1", (DL_FUNC) &_cpp11test_my_warning_n1, 1},
501515
{"_cpp11test_my_warning_n1fmt", (DL_FUNC) &_cpp11test_my_warning_n1fmt, 1},
502516
{"_cpp11test_my_warning_n2fmt", (DL_FUNC) &_cpp11test_my_warning_n2fmt, 2},
517+
{"_cpp11test_ordered_map_to_list_", (DL_FUNC) &_cpp11test_ordered_map_to_list_, 1},
503518
{"_cpp11test_protect_many_", (DL_FUNC) &_cpp11test_protect_many_, 1},
504519
{"_cpp11test_protect_many_cpp11_", (DL_FUNC) &_cpp11test_protect_many_cpp11_, 1},
505520
{"_cpp11test_protect_many_preserve_", (DL_FUNC) &_cpp11test_protect_many_preserve_, 1},
@@ -533,6 +548,7 @@ static const R_CallMethodDef CallEntries[] = {
533548
{"_cpp11test_sum_int_foreach_", (DL_FUNC) &_cpp11test_sum_int_foreach_, 1},
534549
{"_cpp11test_test_destruction_inner", (DL_FUNC) &_cpp11test_test_destruction_inner, 0},
535550
{"_cpp11test_test_destruction_outer", (DL_FUNC) &_cpp11test_test_destruction_outer, 0},
551+
{"_cpp11test_unordered_map_to_list_", (DL_FUNC) &_cpp11test_unordered_map_to_list_, 1},
536552
{"_cpp11test_upper_bound", (DL_FUNC) &_cpp11test_upper_bound, 2},
537553
{"run_testthat_tests", (DL_FUNC) &run_testthat_tests, 1},
538554
{NULL, NULL, 0}

cpp11test/src/map.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include "cpp11/as.hpp"
2+
#include "cpp11/doubles.hpp"
3+
4+
[[cpp11::register]] SEXP ordered_map_to_list_(cpp11::doubles x) {
5+
std::map<double, int> counts;
6+
int n = x.size();
7+
for (int i = 0; i < n; i++) {
8+
counts[x[i]]++;
9+
}
10+
return cpp11::as_sexp(counts);
11+
}
12+
13+
[[cpp11::register]] SEXP unordered_map_to_list_(cpp11::doubles x) {
14+
std::unordered_map<double, int> counts;
15+
int n = x.size();
16+
for (int i = 0; i < n; i++) {
17+
counts[x[i]]++;
18+
}
19+
return cpp11::as_sexp(counts);
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
test_that("ordered and unordered C++ maps are converted to R lists", {
2+
set.seed(42L)
3+
x <- rnorm(10L)
4+
xprime <- c(x, x[1])
5+
6+
om <- ordered_map_to_list_(x)
7+
expect_type(om, "list")
8+
9+
om_doubles <- as.double(names(om))
10+
expect_equal(om_doubles, sort(om_doubles))
11+
12+
omprime <- ordered_map_to_list_(xprime)
13+
expect_equal(unlist(unique(omprime)), 1:2)
14+
15+
um <- unordered_map_to_list_(xprime)
16+
expect_type(um, "list")
17+
expect_equal(unlist(unique(um)), 1:2)
18+
})

inst/include/cpp11/as.hpp

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
#include <cmath> // for modf
44
#include <initializer_list> // for initializer_list
5+
#include <map> // for std::map
56
#include <memory> // for std::shared_ptr, std::weak_ptr, std::unique_ptr
67
#include <stdexcept>
7-
#include <string> // for string, basic_string
8-
#include <type_traits> // for decay, enable_if, is_same, is_convertible
8+
#include <string> // for string, basic_string
9+
#include <type_traits> // for decay, enable_if, is_same, is_convertible
10+
#include <unordered_map> // for std::unordered_map
11+
#include <vector> // for std::vector
912

1013
#include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_xlength, R_xlen_t
1114
#include "cpp11/protect.hpp" // for stop, protect, safe, protect::function
@@ -243,7 +246,7 @@ enable_if_integral<T, SEXP> as_sexp(const Container& from) {
243246
}
244247

245248
inline SEXP as_sexp(std::initializer_list<int> from) {
246-
return as_sexp<std::initializer_list<int>>(from);
249+
return as_sexp(std::vector<int>(from));
247250
}
248251

249252
template <typename Container, typename T = typename Container::value_type,
@@ -261,7 +264,7 @@ enable_if_floating_point<T, SEXP> as_sexp(const Container& from) {
261264
}
262265

263266
inline SEXP as_sexp(std::initializer_list<double> from) {
264-
return as_sexp<std::initializer_list<double>>(from);
267+
return as_sexp(std::vector<double>(from));
265268
}
266269

267270
template <typename Container, typename T = typename Container::value_type,
@@ -279,7 +282,7 @@ enable_if_bool<T, SEXP> as_sexp(const Container& from) {
279282
}
280283

281284
inline SEXP as_sexp(std::initializer_list<bool> from) {
282-
return as_sexp<std::initializer_list<bool>>(from);
285+
return as_sexp(std::vector<bool>(from));
283286
}
284287

285288
namespace detail {
@@ -325,12 +328,81 @@ enable_if_c_string<T, SEXP> as_sexp(const Container& from) {
325328
}
326329

327330
inline SEXP as_sexp(std::initializer_list<const char*> from) {
328-
return as_sexp<std::initializer_list<const char*>>(from);
331+
return as_sexp(std::vector<const char*>(from));
329332
}
330333

331334
template <typename T, typename = disable_if_r_string<T>>
332335
enable_if_convertible_to_sexp<T, SEXP> as_sexp(const T& from) {
333336
return from;
334337
}
335338

339+
// Pacha: Specialization for std::map
340+
// NOTE: I did not use templates to avoid clashes with doubles/function/etc.
341+
inline SEXP as_sexp(const std::map<std::string, SEXP>& map) {
342+
R_xlen_t size = map.size();
343+
SEXP result = PROTECT(Rf_allocVector(VECSXP, size));
344+
SEXP names = PROTECT(Rf_allocVector(STRSXP, size));
345+
346+
auto it = map.begin();
347+
for (R_xlen_t i = 0; i < size; ++i, ++it) {
348+
SET_VECTOR_ELT(result, i, it->second);
349+
SET_STRING_ELT(names, i, Rf_mkCharCE(it->first.c_str(), CE_UTF8));
350+
}
351+
352+
Rf_setAttrib(result, R_NamesSymbol, names);
353+
UNPROTECT(2);
354+
return result;
355+
}
356+
357+
// Specialization for std::map<double, int>
358+
inline SEXP as_sexp(const std::map<double, int>& map) {
359+
R_xlen_t size = map.size();
360+
SEXP result = PROTECT(Rf_allocVector(VECSXP, size));
361+
SEXP names = PROTECT(Rf_allocVector(REALSXP, size));
362+
363+
auto it = map.begin();
364+
for (R_xlen_t i = 0; i < size; ++i, ++it) {
365+
SET_VECTOR_ELT(result, i, Rf_ScalarInteger(it->second));
366+
REAL(names)[i] = it->first;
367+
}
368+
369+
Rf_setAttrib(result, R_NamesSymbol, names);
370+
UNPROTECT(2);
371+
return result;
372+
}
373+
374+
// Pacha: Specialization for std::unordered_map
375+
inline SEXP as_sexp(const std::unordered_map<std::string, SEXP>& map) {
376+
R_xlen_t size = map.size();
377+
SEXP result = PROTECT(Rf_allocVector(VECSXP, size));
378+
SEXP names = PROTECT(Rf_allocVector(STRSXP, size));
379+
380+
auto it = map.begin();
381+
for (R_xlen_t i = 0; i < size; ++i, ++it) {
382+
SET_VECTOR_ELT(result, i, it->second);
383+
SET_STRING_ELT(names, i, Rf_mkCharCE(it->first.c_str(), CE_UTF8));
384+
}
385+
386+
Rf_setAttrib(result, R_NamesSymbol, names);
387+
UNPROTECT(2);
388+
return result;
389+
}
390+
391+
// Specialization for std::unordered_map<double, int>
392+
inline SEXP as_sexp(const std::unordered_map<double, int>& map) {
393+
R_xlen_t size = map.size();
394+
SEXP result = PROTECT(Rf_allocVector(VECSXP, size));
395+
SEXP names = PROTECT(Rf_allocVector(REALSXP, size));
396+
397+
auto it = map.begin();
398+
for (R_xlen_t i = 0; i < size; ++i, ++it) {
399+
SET_VECTOR_ELT(result, i, Rf_ScalarInteger(it->second));
400+
REAL(names)[i] = it->first;
401+
}
402+
403+
Rf_setAttrib(result, R_NamesSymbol, names);
404+
UNPROTECT(2);
405+
return result;
406+
}
407+
336408
} // namespace cpp11

0 commit comments

Comments
 (0)