Rembed.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include "RInside.h"
  15. #include "eclrtl.hpp"
  16. #include "jexcept.hpp"
  17. #include "jthread.hpp"
  18. #include "hqlplugins.hpp"
  19. #ifdef _WIN32
  20. #define EXPORT __declspec(dllexport)
  21. #else
  22. #define EXPORT
  23. #endif
  24. static const char * compatibleVersions[] =
  25. { "R Embed Helper 1.0.0", NULL };
  26. static const char *version = "R Embed Helper 1.0.0";
  27. static const char * EclDefinition =
  28. "EXPORT Language := SERVICE\n"
  29. " boolean getEmbedContext():cpp,pure,namespace='Rembed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';\n"
  30. " boolean syntaxCheck(const varstring src):cpp,pure,namespace='Rembed',entrypoint='syntaxCheck';\n"
  31. " unload():cpp,pure,namespace='Rembed',entrypoint='unload';\n"
  32. "END;"
  33. "EXPORT getEmbedContext := Language.getEmbedContext;"
  34. "EXPORT syntaxCheck := Language.syntaxCheck;"
  35. "EXPORT boolean supportsImport := false;"
  36. "EXPORT boolean supportsScript := true;";
  37. extern "C" EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  38. {
  39. if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
  40. {
  41. ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
  42. pbx->compatibleVersions = compatibleVersions;
  43. }
  44. else if (pb->size != sizeof(ECLPluginDefinitionBlock))
  45. return false;
  46. pb->magicVersion = PLUGIN_VERSION;
  47. pb->version = version;
  48. pb->moduleName = "R";
  49. pb->ECL = EclDefinition;
  50. pb->flags = PLUGIN_DLL_MODULE | PLUGIN_MULTIPLE_VERSIONS;
  51. pb->description = "R Embed Helper";
  52. return true;
  53. }
  54. namespace Rembed
  55. {
  56. // Use a global object to ensure that the R instance is initialized only once
  57. static class RGlobalState
  58. {
  59. public:
  60. RGlobalState()
  61. {
  62. const char *args[] = {"R", "--slave" };
  63. R = new RInside(2, args, true, false, false);
  64. }
  65. ~RGlobalState()
  66. {
  67. delete R;
  68. }
  69. RInside *R;
  70. }* globalState = NULL;
  71. static CriticalSection RCrit; // R is single threaded - need to own this before making any call to R
  72. static RGlobalState *queryGlobalState()
  73. {
  74. CriticalBlock b(RCrit);
  75. if (!globalState)
  76. globalState = new RGlobalState;
  77. return globalState;
  78. }
  79. extern void unload()
  80. {
  81. CriticalBlock b(RCrit);
  82. if (globalState)
  83. delete globalState;
  84. globalState = NULL;
  85. }
  86. MODULE_INIT(INIT_PRIORITY_STANDARD)
  87. {
  88. return true;
  89. }
  90. MODULE_EXIT()
  91. {
  92. unload();
  93. }
  94. // Each call to a R function will use a new REmbedFunctionContext object
  95. // This takes care of ensuring that the critsec is locked while we are executing R code,
  96. // and released when we are not
  97. class REmbedFunctionContext: public CInterfaceOf<IEmbedFunctionContext>
  98. {
  99. public:
  100. REmbedFunctionContext(RInside &_R, const char *options)
  101. : R(_R), block(RCrit), result(R_NilValue)
  102. {
  103. }
  104. ~REmbedFunctionContext()
  105. {
  106. }
  107. virtual bool getBooleanResult()
  108. {
  109. return ::Rcpp::as<bool>(result);
  110. }
  111. virtual void getDataResult(size32_t &__len, void * &__result)
  112. {
  113. std::vector<byte> vval = ::Rcpp::as<std::vector<byte> >(result);;
  114. rtlStrToDataX(__len, __result, vval.size(), vval.data());
  115. }
  116. virtual double getRealResult()
  117. {
  118. return ::Rcpp::as<double>(result);
  119. }
  120. virtual __int64 getSignedResult()
  121. {
  122. return ::Rcpp::as<long int>(result); // Should really be long long, but RInside does not support that
  123. }
  124. virtual unsigned __int64 getUnsignedResult()
  125. {
  126. return ::Rcpp::as<unsigned long int>(result); // Should really be long long, but RInside does not support that
  127. }
  128. virtual void getStringResult(size32_t &__len, char * &__result)
  129. {
  130. std::string str = ::Rcpp::as<std::string>(result);
  131. rtlStrToStrX(__len, __result, str.length(), str.data());
  132. }
  133. virtual void getUTF8Result(size32_t &chars, char * &result)
  134. {
  135. throw MakeStringException(MSGAUD_user, 0, "Rembed: %s: Unicode/UTF8 results not supported", func.c_str());
  136. }
  137. virtual void getUnicodeResult(size32_t &chars, UChar * &result)
  138. {
  139. throw MakeStringException(MSGAUD_user, 0, "Rembed: %s: Unicode/UTF8 results not supported", func.c_str());
  140. }
  141. virtual void bindBooleanParam(const char *name, bool val)
  142. {
  143. R[name] = val;
  144. }
  145. virtual void bindDataParam(const char *name, size32_t len, const void *val)
  146. {
  147. std::vector<byte> vval;
  148. const byte *cval = (const byte *) val;
  149. vval.assign(cval, cval+len);
  150. R[name] = vval;
  151. }
  152. virtual void bindRealParam(const char *name, double val)
  153. {
  154. R[name] = val;
  155. }
  156. virtual void bindSignedParam(const char *name, __int64 val)
  157. {
  158. R[name] = (long int) val;
  159. }
  160. virtual void bindUnsignedParam(const char *name, unsigned __int64 val)
  161. {
  162. R[name] = (unsigned long int) val;
  163. }
  164. virtual void bindStringParam(const char *name, size32_t len, const char *val)
  165. {
  166. std::string s(val, len);
  167. R[name] = s;
  168. }
  169. virtual void bindVStringParam(const char *name, const char *val)
  170. {
  171. R[name] = val;
  172. }
  173. virtual void bindUTF8Param(const char *name, size32_t chars, const char *val)
  174. {
  175. UNIMPLEMENTED;
  176. }
  177. virtual void bindUnicodeParam(const char *name, size32_t chars, const UChar *val)
  178. {
  179. UNIMPLEMENTED;
  180. }
  181. virtual void importFunction(size32_t lenChars, const char *utf)
  182. {
  183. throwUnexpected();
  184. }
  185. virtual void compileEmbeddedScript(size32_t lenChars, const char *utf)
  186. {
  187. func.assign(utf, rtlUtf8Size(lenChars, utf));
  188. }
  189. virtual void callFunction()
  190. {
  191. result = R.parseEval(func);
  192. }
  193. private:
  194. RInside &R;
  195. RInside::Proxy result;
  196. std::string func;
  197. CriticalBlock block;
  198. };
  199. class REmbedContext: public CInterfaceOf<IEmbedContext>
  200. {
  201. public:
  202. virtual IEmbedFunctionContext *createFunctionContext(bool isImport, const char *options)
  203. {
  204. return new REmbedFunctionContext(*queryGlobalState()->R, options);
  205. }
  206. };
  207. extern IEmbedContext* getEmbedContext()
  208. {
  209. return new REmbedContext;
  210. }
  211. extern bool syntaxCheck(const char *script)
  212. {
  213. return true; // MORE
  214. }
  215. } // namespace