From 0f7c92f01b182db5e09b91deebf3c4cdf22e834d Mon Sep 17 00:00:00 2001 From: Edzer Pebesma Date: Wed, 10 Jan 2018 16:27:32 +0100 Subject: [PATCH] first shot at interfacing udunits2 directly --- DESCRIPTION | 4 +- NAMESPACE | 3 +- R/RcppExports.R | 15 ++++++ R/conversion.R | 6 --- R/init.R | 9 ++++ src/Makevars.in | 2 + src/Makevars.win | 16 +++++++ src/RcppExports.cpp | 55 ++++++++++++++++++++++ src/io.c | 35 ++++++++++++++ src/io.h | 2 + src/udunits.cpp | 112 ++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 251 insertions(+), 8 deletions(-) create mode 100644 R/RcppExports.R create mode 100644 R/init.R create mode 100644 src/Makevars.in create mode 100644 src/Makevars.win create mode 100644 src/RcppExports.cpp create mode 100644 src/io.c create mode 100644 src/io.h create mode 100644 src/udunits.cpp diff --git a/DESCRIPTION b/DESCRIPTION index 92245225..1bcc34ec 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -7,7 +7,9 @@ Authors@R: c(person("Edzer", "Pebesma", role = c("aut", "cre"), email = "edzer.p Depends: R (>= 3.0.0) Imports: - udunits2 (>= 0.13) + Rcpp +LinkingTo: + Rcpp Suggests: NISTunits, measurements, diff --git a/NAMESPACE b/NAMESPACE index 0b7ccabf..cc851f7c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -64,5 +64,6 @@ export(units_options) export(valid_udunits) export(valid_udunits_prefixes) import(stats) -import(udunits2) import(utils) +importFrom(Rcpp,evalCpp) +useDynLib(units) diff --git a/R/RcppExports.R b/R/RcppExports.R new file mode 100644 index 00000000..a8d7c0aa --- /dev/null +++ b/R/RcppExports.R @@ -0,0 +1,15 @@ +# Generated by using Rcpp::compileAttributes() -> do not edit by hand +# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +udunits_init <- function(path, warn_on_failure = FALSE) { + .Call('_units_udunits_init', PACKAGE = 'units', path, warn_on_failure) +} + +R_ut_parse <- function(name) { + .Call('_units_R_ut_parse', PACKAGE = 'units', name) +} + +R_ut_format <- function(p, names = FALSE, definition = FALSE) { + .Call('_units_R_ut_format', PACKAGE = 'units', p, names, definition) +} + diff --git a/R/conversion.R b/R/conversion.R index b14086f4..c9d1ef16 100644 --- a/R/conversion.R +++ b/R/conversion.R @@ -1,9 +1,3 @@ -NULL -#' @import utils -#' @import stats -#' @import udunits2 -NULL - # Helper functions for testing if we can convert and how using either # user-defined conversion functions or udunits. are_convertible <- function(from, to) { diff --git a/R/init.R b/R/init.R new file mode 100644 index 00000000..3e2f6b34 --- /dev/null +++ b/R/init.R @@ -0,0 +1,9 @@ +#' @import utils +#' @import stats +#' @importFrom Rcpp evalCpp +#' @useDynLib units +NULL + +.onLoad = function(libname, pkgname) { + udunits_init(character(0), TRUE) +} diff --git a/src/Makevars.in b/src/Makevars.in new file mode 100644 index 00000000..e2f78a3b --- /dev/null +++ b/src/Makevars.in @@ -0,0 +1,2 @@ +PKG_CPPFLAGS = @UD_CPPFLAGS@ +PKG_LIBS = @LIBS@ diff --git a/src/Makevars.win b/src/Makevars.win new file mode 100644 index 00000000..fca44df1 --- /dev/null +++ b/src/Makevars.win @@ -0,0 +1,16 @@ +PKG_CPPFLAGS = -I../windows/udunits-2.2.20/include +PKG_LIBS = -L../windows/udunits-2.2.20/lib${R_ARCH} \ + -ludunits2 -lexpat + +all: clean winlibs + +winlibs: + "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" "../tools/winlibs.R" + mkdir -p ../inst + rm -Rf ../inst/share + cp -r ../windows/udunits-2.2.20/share ../inst/ + +clean: + rm -Rf $(SHLIB) $(OBJECTS) + +.PHONY: all winlibs clean diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp new file mode 100644 index 00000000..d32215be --- /dev/null +++ b/src/RcppExports.cpp @@ -0,0 +1,55 @@ +// Generated by using Rcpp::compileAttributes() -> do not edit by hand +// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 + +#include + +using namespace Rcpp; + +// udunits_init +LogicalVector udunits_init(CharacterVector path, bool warn_on_failure); +RcppExport SEXP _units_udunits_init(SEXP pathSEXP, SEXP warn_on_failureSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< CharacterVector >::type path(pathSEXP); + Rcpp::traits::input_parameter< bool >::type warn_on_failure(warn_on_failureSEXP); + rcpp_result_gen = Rcpp::wrap(udunits_init(path, warn_on_failure)); + return rcpp_result_gen; +END_RCPP +} +// R_ut_parse +XPtr< std::vector > R_ut_parse(CharacterVector name); +RcppExport SEXP _units_R_ut_parse(SEXP nameSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< CharacterVector >::type name(nameSEXP); + rcpp_result_gen = Rcpp::wrap(R_ut_parse(name)); + return rcpp_result_gen; +END_RCPP +} +// R_ut_format +CharacterVector R_ut_format(SEXP p, bool names, bool definition); +RcppExport SEXP _units_R_ut_format(SEXP pSEXP, SEXP namesSEXP, SEXP definitionSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< SEXP >::type p(pSEXP); + Rcpp::traits::input_parameter< bool >::type names(namesSEXP); + Rcpp::traits::input_parameter< bool >::type definition(definitionSEXP); + rcpp_result_gen = Rcpp::wrap(R_ut_format(p, names, definition)); + return rcpp_result_gen; +END_RCPP +} + +static const R_CallMethodDef CallEntries[] = { + {"_units_udunits_init", (DL_FUNC) &_units_udunits_init, 2}, + {"_units_R_ut_parse", (DL_FUNC) &_units_R_ut_parse, 1}, + {"_units_R_ut_format", (DL_FUNC) &_units_R_ut_format, 3}, + {NULL, NULL, 0} +}; + +RcppExport void R_init_units(DllInfo *dll) { + R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); + R_useDynamicSymbols(dll, FALSE); +} diff --git a/src/io.c b/src/io.c new file mode 100644 index 00000000..01090a06 --- /dev/null +++ b/src/io.c @@ -0,0 +1,35 @@ +#include +#include +#include /* FILENAME_MAX */ + +#include "io.h" + +/* From the enum comments in udunits2.h */ +const char * ut_status_strings[] = { + "Success", + "An argument violates the function's contract", + "Unit, prefix, or identifier already exists", + "No such unit exists", + "Operating-system error. See \"errno\".", + "The units belong to different unit-systems", + "The operation on the unit(s) is meaningless", + "The unit-system doesn't have a unit named \"second\"", + "An error occurred while visiting a unit", + "A unit can't be formatted in the desired manner", + "string unit representation contains syntax error", + "string unit representation contains unknown word", + "Can't open argument-specified unit database", + "Can't open environment-specified unit database", + "Can't open installed, default, unit database", + "Error parsing unit specification" +}; + +void handle_error(const char *calling_function) { + ut_status stat; + stat = ut_get_status(); + error("Error in function %s: %s", calling_function, ut_status_strings[stat]); +} + +void r_error_fn(const char* fmt, va_list args) { + Rvprintf(fmt, args); +} diff --git a/src/io.h b/src/io.h new file mode 100644 index 00000000..8dee3562 --- /dev/null +++ b/src/io.h @@ -0,0 +1,2 @@ +void r_error_fn(const char* fmt, va_list args); +void handle_error(const char *calling_function); diff --git a/src/udunits.cpp b/src/udunits.cpp new file mode 100644 index 00000000..56ad7e7f --- /dev/null +++ b/src/udunits.cpp @@ -0,0 +1,112 @@ +/* + modified from: https://github.com/pacificclimate/Rudunits2 + + (c) James Hiebert + Pacific Climate Impacts Consortium + August, 16, 2010 + + Functions to support the R interface to the udunits (API version 2) library +*/ + +#include "Rcpp.h" +using namespace Rcpp; + +#include +#include +#include /* FILENAME_MAX */ + +ut_system *sys = NULL; +static ut_encoding enc; + +extern "C" { +#include "io.h" +} + +// [[Rcpp::export]] +LogicalVector udunits_init(CharacterVector path, bool warn_on_failure = false) { + + LogicalVector ret(1); + ut_set_error_message_handler((ut_error_message_handler) r_error_fn); + if (sys != NULL) + ut_free_system(sys); + + sys = NULL; + ut_set_error_message_handler(ut_ignore); + for (int i = 0; i < path.size(); i++) { + sys = ut_read_xml(path[i]); + if (sys != NULL) + break; + } + if (sys == NULL) + sys = ut_read_xml(NULL); + ut_set_error_message_handler((ut_error_message_handler) r_error_fn); + if (sys == NULL) { + if (warn_on_failure) + handle_error("udunit_init"); + ret[0] = false; + } else + ret[0] = true; + enc = UT_UTF8; + return ret; +} + +LogicalVector R_ut_has_system(List foo) { + LogicalVector ret(1); + if (sys != NULL) + ret(0) = true; + else + ret(0) = false; + return ret; +} + +typedef std::vector ut_vec; + +void finalize_ut(ut_vec *v) { + for (int i = 0; i < v->size(); i++) + ut_free((ut_unit *) ((*v)[i])); +} + +// typedef XPtr XPtrUT; +typedef XPtr XPtrUT; + +// [[Rcpp::export]] +XPtr< std::vector > R_ut_parse(CharacterVector name) { + ut_vec *v = new ut_vec; + Rcout << name[0] << std::endl; + ut_unit *u = ut_parse(sys, name[0], enc); + if (u == NULL) { + switch (ut_get_status()) { + case UT_BAD_ARG: + case UT_SYNTAX: + case UT_UNKNOWN: + case UT_OS: + handle_error("R_ut_parse"); + } + } + // error checking ... + v->push_back(u); + XPtrUT p(v); + return(p); +} + +// [[Rcpp::export]] +CharacterVector R_ut_format(SEXP p, bool names = false, bool definition = false) { + XPtr< std::vector > ptr(p); + int opt = enc; + if (names) + opt = opt | UT_NAMES; + if (definition) + opt = opt | UT_DEFINITION; + char buf[256]; + int i = ut_format((ut_unit *) (*ptr)[0], buf, 256, opt); + if (i == -1) { + switch (ut_get_status()) { + UT_BAD_ARG: + UT_CANT_FORMAT: + handle_error("R_ut_format"); + break; + default:; + } + } + return CharacterVector::create(buf); +}