Open romainfrancois opened 4 years ago
My main hesitation is that currently the read only vector classes never allocate on Rs heap, which seems a nice property when you are reasoning about them.
Really we should annotate the constructors with noexcept
to make this more explicit.
I acknowledge it would make this particular case nicer to have them. But I think for the overall consistency it would be better if they were not included.
Perhaps instead this could be a (set of) factory function, similar (in concept to) Vector::create()
that Rcpp
has:
At the moment we can construct a writable::list
of anything that as_cpp<>
knows how to handle, but only when named, i.e with the _nm =
.
cpp11::cpp_function('sexp named_list(){
return cpp11::writable::list({
"a"_nm = 1, "b"_nm = 2.3
});
}', quiet = FALSE)
#> clang++ -mmacosx-version-min=10.13 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I/Users/romainfrancois/.R/library/4.0/cpp11/include -I/usr/local/include -fPIC -Wall -O3 -Wall -Wimplicit-int-float-conversion -c /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a1117ea2a/src/code_0.cpp -o /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a1117ea2a/src/code_0.o
#> clang++ -mmacosx-version-min=10.13 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I/Users/romainfrancois/.R/library/4.0/cpp11/include -I/usr/local/include -fPIC -Wall -O3 -Wall -Wimplicit-int-float-conversion -c /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a1117ea2a/src/cpp11.cpp -o /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a1117ea2a/src/cpp11.o
#> clang++ -mmacosx-version-min=10.13 -std=gnu++11 -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -single_module -multiply_defined suppress -L/Library/Frameworks/R.framework/Resources/lib -L/usr/local/lib -o /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a1117ea2a/src/code_0.so /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a1117ea2a/src/code_0.o /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a1117ea2a/src/cpp11.o -F/Library/Frameworks/R.framework/.. -framework R -Wl,-framework -Wl,CoreFoundation
named_list()
#> $a
#> [1] 1
#>
#> $b
#> [1] 2.3
cpp11::cpp_function('sexp unnamed_list(){
return cpp11::writable::list({
1, 2.3
});
}', quiet = FALSE)
#> clang++ -mmacosx-version-min=10.13 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I/Users/romainfrancois/.R/library/4.0/cpp11/include -I/usr/local/include -fPIC -Wall -O3 -Wall -Wimplicit-int-float-conversion -c /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a49ae4763/src/code_1.cpp -o /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a49ae4763/src/code_1.o
#> /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a49ae4763/src/code_1.cpp:6:10: error: no matching constructor for initialization of 'cpp11::writable::list' (aka 'r_vector<SEXPREC *>')
#> return cpp11::writable::list({
#> ^ ~
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:274:3: note: candidate constructor not viable: cannot convert initializer list argument to 'const SEXP' (aka 'SEXPREC *const')
#> r_vector(const SEXP& data);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:275:3: note: candidate constructor not viable: cannot convert initializer list argument to 'SEXP' (aka 'SEXPREC *')
#> r_vector(SEXP&& data);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:280:3: note: candidate constructor not viable: no known conversion from 'int' to 'const char *' for 1st argument
#> r_vector(std::initializer_list<const char*> il);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:281:3: note: candidate constructor not viable: no known conversion from 'int' to 'std::__1::basic_string<char>' for 1st argument
#> r_vector(std::initializer_list<std::string> il);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:289:3: note: candidate constructor not viable: cannot convert initializer list argument to 'const R_xlen_t' (aka 'const long')
#> r_vector(const R_xlen_t size);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:293:3: note: candidate constructor not viable: cannot convert initializer list argument to 'const cpp11::writable::r_vector<SEXPREC *>'
#> r_vector(const r_vector& rhs);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:294:3: note: candidate constructor not viable: cannot convert initializer list argument to 'cpp11::writable::r_vector<SEXPREC *>'
#> r_vector(r_vector&& rhs);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:296:3: note: candidate constructor not viable: cannot convert initializer list argument to 'const cpp11::r_vector<SEXPREC *>'
#> r_vector(const cpp11::r_vector<T>& rhs);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/list.hpp:74:24: note: candidate constructor not viable: no known conversion from 'int' to 'SEXPREC *' for 1st argument
#> inline r_vector<SEXP>::r_vector(std::initializer_list<SEXP> il)
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/list.hpp:85:24: note: candidate constructor not viable: no known conversion from 'int' to 'cpp11::named_arg' for 1st argument
#> inline r_vector<SEXP>::r_vector(std::initializer_list<named_arg> il)
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:287:3: note: candidate template ignored: couldn't infer template argument 'V'
#> r_vector(const V& obj);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:284:3: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
#> r_vector(Iter first, Iter last);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:273:3: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
#> r_vector() = default;
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:276:3: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
#> r_vector(const SEXP& data, bool is_altrep);
#> ^
#> /Users/romainfrancois/.R/library/4.0/cpp11/include/cpp11/r_vector.hpp:277:3: note: candidate constructor not viable: requires 2 arguments, but 1 was provided
#> r_vector(SEXP&& data, bool is_altrep);
#> ^
#> 1 error generated.
#> make: *** [/private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpMgV4YE/file16e1a49ae4763/src/code_1.o] Error 1
#> Error in dyn.load(shared_lib, local = TRUE, now = TRUE): unable to load shared object '/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T//RtmpMgV4YE/file16e1a49ae4763/src/code_1.so':
#> dlopen(/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T//RtmpMgV4YE/file16e1a49ae4763/src/code_1.so, 6): image not found
unnamed_list()
#> Error in .Call("_code_1_unnamed_list", PACKAGE = "code_1"): "_code_1_unnamed_list" not available for .Call() for package "code_1"
Created on 2020-07-27 by the reprex package (v0.3.0.9001)
Maybe ::c<Args...>
so that we could e.g.
list x = list::c(1, 2.3);
or
auto x = c<SEXP>(1, 2.3)
I've been playing with this as a way to get back on track with variadic templates.
Not sure about the syntax, but I like the feature, e.g. this makes list(a = 1L, "test")
auto vec = c<cpp11::list>("a"_nm = 1, "test");
#include "cpp11.hpp"
template <typename... Args>
struct is_named ;
template <typename T, typename... Args>
struct is_named<T, Args...> : public std::integral_constant<
bool,
std::is_same<typename std::decay<T>::type, cpp11::named_arg>::value || is_named<Args...>::value
>{};
template <>
struct is_named<> : std::false_type {};
template <typename Vector>
struct c {
using value_type = typename Vector::value_type;
using writableVector = cpp11::writable::r_vector<value_type>;
template <typename... Args>
c(Args&&... args) : data(sizeof...(Args)) {
int n = sizeof...(Args);
fill_values(0, std::forward<Args>(args)...);
if (is_named<Args...>::value) {
cpp11::writable::strings names(n);
fill_names(names, 0, std::forward<Args>(args)...);
data.names() = names;
}
}
template <>
c<>() : data((R_xlen_t)0){}
inline operator SEXP() const {
return data;
}
private:
template <typename T>
void set(int pos, T&& value, std::false_type) {
data[pos] = value;
}
template <typename T>
void set(int pos, T&& value, std::true_type) {
data[pos] = cpp11::as_cpp<value_type>(value.value());
}
template <typename T>
void set_name(cpp11::writable::strings& names, int pos, T&& value, std::false_type) {}
template <typename T>
void set_name(cpp11::writable::strings& names, int pos, T&& value, std::true_type) {
names[pos] = value.name();
}
template <typename T, typename... Args>
void fill_names(cpp11::writable::strings& names, int pos, T&& value, Args&&...args) {
set_name(names, pos, value, typename std::is_same<typename std::decay<T>::type, cpp11::named_arg>::type());
fill_names(names, pos + 1, std::forward<Args>(args)...);
}
template <typename T>
void fill_names(cpp11::writable::strings& names, int pos, T&& value) {
set_name(names, pos, value, typename std::is_same<typename std::decay<T>::type, cpp11::named_arg>::type());
}
template <typename T, typename... Args>
void fill_values(int pos, T&& value, Args&&...args) {
set(pos, value, typename std::is_same<typename std::decay<T>::type, cpp11::named_arg>::type());
fill_values(pos + 1, std::forward<Args>(args)...);
}
template <typename T>
void fill_values(int pos, T&& value) {
set(pos, value, typename std::is_same<typename std::decay<T>::type, cpp11::named_arg>::type());
}
writableVector data;
};
template <>
template <typename T>
void c<cpp11::list>::set(int pos, T&& value, std::false_type) {
data[pos] = cpp11::as_sexp(value);
}
template <>
template <typename T>
void c<cpp11::list>::set(int pos, T&& value, std::true_type) {
data[pos] = value.value();
}
template <>
template <typename T>
void c<cpp11::strings>::set(int pos, T&& value, std::false_type) {
data[pos] = cpp11::r_string(value);
}
template <>
template <typename T>
void c<cpp11::strings>::set(int pos, T&& value, std::true_type) {
data[pos] = cpp11::strings(value.value())[0];
}
using namespace cpp11::literals;
[[cpp11::register]]
void test() {
{
Rprintf("unnamed list\n");
auto vec = c<cpp11::list>(1, "test");
Rf_PrintValue(vec);
}
{
Rprintf("partially named list\n");
auto vec = c<cpp11::list>("a"_nm = 1, "test");
Rf_PrintValue(vec);
}
{
Rprintf("empty list\n");
auto vec = c<cpp11::list>();
Rf_PrintValue(vec);
}
{
Rprintf("unnamed integers\n");
auto vec = c<cpp11::integers>(1, 2);
Rf_PrintValue(vec);
}
{
Rprintf("partially named integers\n");
auto vec = c<cpp11::integers>("a"_nm = 1, 2);
Rf_PrintValue(vec);
}
{
Rprintf("empty integers\n");
auto vec = c<cpp11::integers>();
Rf_PrintValue(vec);
}
{
Rprintf("unnamed strings\n");
auto vec = c<cpp11::strings>("one", "two");
Rf_PrintValue(vec);
}
{
Rprintf("partially named strings\n");
auto vec = c<cpp11::strings>("a"_nm = "one", "two");
Rf_PrintValue(vec);
}
{
Rprintf("empty strings\n");
auto vec = c<cpp11::strings>();
Rf_PrintValue(vec);
}
Because of how cpp11
works this could be easily incubated in another vendorable package
$ Rscript /tmp/test.R
clang++ -mmacosx-version-min=10.13 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I/Users/romainfrancois/.R/library/4.0/cpp11/include -I/usr/local/include -fPIC -Wall -O3 -Wall -Wimplicit-int-float-conversion -c /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpLy5ZFe/file17cbe28f12e16/src/test.cpp -o /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpLy5ZFe/file17cbe28f12e16/src/test.o
clang++ -mmacosx-version-min=10.13 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I/Users/romainfrancois/.R/library/4.0/cpp11/include -I/usr/local/include -fPIC -Wall -O3 -Wall -Wimplicit-int-float-conversion -c /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpLy5ZFe/file17cbe28f12e16/src/cpp11.cpp -o /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpLy5ZFe/file17cbe28f12e16/src/cpp11.o
clang++ -mmacosx-version-min=10.13 -std=gnu++11 -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -single_module -multiply_defined suppress -L/Library/Frameworks/R.framework/Resources/lib -L/usr/local/lib -o /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpLy5ZFe/file17cbe28f12e16/src/test.so /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpLy5ZFe/file17cbe28f12e16/src/test.o /private/var/folders/4b/hn4fq98s6810s4ccv2f9hm2h0000gn/T/RtmpLy5ZFe/file17cbe28f12e16/src/cpp11.o -F/Library/Frameworks/R.framework/.. -framework R -Wl,-framework -Wl,CoreFoundation
unnamed list
[[1]]
[1] 1
[[2]]
[1] "test"
partially named list
$a
[1] 1
[[2]]
[1] "test"
empty list
list()
unnamed integers
[1] 1 2
partially named integers
a
1 2
empty integers
integer(0)
unnamed strings
[1] "one" "two"
partially named strings
a
"one" "two"
empty strings
character(0)
partially named lists are esoteric enough in practice that I am not sure it is worth the added complexity.
But I don't have a very strong opinion in either direction.
Yeah I guess, partially named was a bonus, but the ability to create a list from various types is useful though, and that's not something we can do with initializer lists
Would it be useful to have the initializer lists constructors also for the non writable classes, or should they really only be constructed from
SEXP
, e.g. I enjoy:but this would be useful too:
Created on 2020-07-24 by the reprex package (v0.3.0.9001)