I would like to, within my own compiled C++ code, check to see if a library package is loaded in R (if not, load it), call a function from that library and get the results back to in my C++ code.
Could someone point me in the right direction? There seems to be a plethora of info on R and different ways of calling R from C++ and vis versa, but I have not come across exactly what I am wanting to do.
Thanks.
Dirk's probably right that RInside makes life easier. But for the die-hards... The essence comes from Writing R Extensions sections 8.1 and 8.2, and from the examples distributed with R. The material below covers constructing and evaluating the call; dealing with the return value is a different (and in some sense easier) topic.
Setup
Let's suppose a Linux / Mac platform. The first thing is that R must have been compiled to allow linking, either to a shared or static R library. I work with an svn copy of R's source, in the directory ~/src/R-devel. I switch to some other directory, call it ~/bin/R-devel, and then
~/src/R-devel/configure --enable-R-shlib
make -j
this generates ~/bin/R-devel/lib/libR.so; perhaps whatever distribution you're using already has this? The -j flag runs make in parallel, which greatly speeds the build.
Examples for embedding are in ~/src/R-devel/tests/Embedding, and they can be made with cd ~/bin/R-devel/tests/Embedding && make. Obviously, the source code for these examples is extremely instructive.
Code
To illustrate, create a file embed.cpp. Start by including the header that defines R data structures, and the R embedding interface; these are located in bin/R-devel/include, and serve as the primary documentation. We also have a prototype for the function that will do all the work
#include <Rembedded.h>
#include <Rdefines.h>
static void doSplinesExample();
The work flow is to start R, do the work, and end R:
int
main(int argc, char *argv[])
{
Rf_initEmbeddedR(argc, argv);
doSplinesExample();
Rf_endEmbeddedR(0);
return 0;
}
The examples under Embedding include one that calls library(splines), sets a named option, then runs a function example("ns"). Here's the routine that does this
static void
doSplinesExample()
{
SEXP e, result;
int errorOccurred;
// create and evaluate 'library(splines)'
PROTECT(e = lang2(install("library"), mkString("splines")));
R_tryEval(e, R_GlobalEnv, &errorOccurred);
if (errorOccurred) {
// handle error
}
UNPROTECT(1);
// 'options(FALSE)' ...
PROTECT(e = lang2(install("options"), ScalarLogical(0)));
// ... modified to 'options(example.ask=FALSE)' (this is obscure)
SET_TAG(CDR(e), install("example.ask"));
R_tryEval(e, R_GlobalEnv, NULL);
UNPROTECT(1);
// 'example("ns")'
PROTECT(e = lang2(install("example"), mkString("ns")));
R_tryEval(e, R_GlobalEnv, &errorOccurred);
UNPROTECT(1);
}
Compile and run
We're now ready to put everything together. The compiler needs to know where the headers and libraries are
g++ -I/home/user/bin/R-devel/include -L/home/user/bin/R-devel/lib -lR embed.cpp
The compiled application needs to be run in the correct environment, e.g., with R_HOME set correctly; this can be arranged easily (obviously a deployed app would want to take a more extensive approach) with
R CMD ./a.out
Depending on your ambitions, some parts of section 8 of Writing R Extensions are not relevant, e.g., callbacks are needed to implement a GUI on top of R, but not to evaluate simple code chunks.
Some detail
Running through that in a bit of detail... An SEXP (S-expression) is a data structure fundamental to R's representation of basic types (integer, logical, language calls, etc.). The line
PROTECT(e = lang2(install("library"), mkString("splines")));
makes a symbol library and a string "splines", and places them into a language construct consisting of two elements. This constructs an unevaluated language object, approximately equivalent to quote(library("splines")) in R. lang2 returns an SEXP that has been allocated from R's memory pool, and it needs to be PROTECTed from garbage collection. PROTECT adds the address pointed to by e to a protection stack, when the memory no longer needs to be protected, the address is popped from the stack (with UNPROTECT(1), a few lines down). The line
R_tryEval(e, R_GlobalEnv, &errorOccurred);
tries to evaluate e in R's global environment. errorOccurred is set to non-0 if an error occurs. R_tryEval returns an SEXP representing the result of the function, but we ignore it here. Because we no longer need the memory allocated to store library("splines"), we tell R that it is no longer PROTECT'ed.
The next chunk of code is similar, evaluating options(example.ask=FALSE), but the construction of the call is more complicated. The S-expression created by lang2 is a pair list, conceptually with a node, a left pointer (CAR) and a right pointer (CDR). The left pointer of e points to the symbol options. The right pointer of e points to another node in the pair list, whose left pointer is FALSE (the right pointer is R_NilValue, indicating the end of the language expression). Each node of a pair list can have a TAG, the meaning of which depends on the role played by the node. Here we attach an argument name.
SET_TAG(CDR(e), install("example.ask"));
The next line evaluates the expression that we have constructed (options(example.ask=FALSE)), using NULL to indicate that we'll ignore the success or failure of the function's evaluation. A different way of constructing and evaluating this call is illustrated in R-devel/tests/Embedding/RParseEval.c, adapted here as
PROTECT(tmp = mkString("options(example.ask=FALSE)"));
PROTECT(e = R_ParseVector(tmp, 1, &status, R_NilValue));
R_tryEval(VECTOR_ELT(e, 0), R_GlobalEnv, NULL);
UNPROTECT(2);
but this doesn't seem like a good strategy in general, as it mixes R and C code and does not allow computed arguments to be used in R functions. Instead write and manage R code in R (e.g., creating a package with functions that perform complicated series of R manipulations) that your C code uses.
The final block of code above constructs and evaluates example("ns"). Rf_tryEval returns the result of the function call, so
SEXP result;
PROTECT(result = Rf_tryEval(e, R_GlobalEnv, &errorOccurred));
// ...
UNPROTECT(1);
would capture that for subsequent processing.
There is Rcpp which allows you to easily extend R with C++ code, and also have that C++ code call back to R. There are examples included in the package which show that.
But maybe what you really want is to keep your C++ program (i.e. you own main()) and call out to R? That can be done most easily with
RInside which allows you to very easily embed R inside your C++ application---and the test for library, load if needed and function call are then extremely easy to do, and the (more than a dozen) included examples show you how to. And Rcpp still helps you to get results back and forth.
Edit: As Martin was kind enough to show things the official way I cannot help and contrast it with one of the examples shipping with RInside. It is something I once wrote quickly to help someone who had asked on r-help about how to load (a portfolio optimisation) library and use it. It meets your requirements: load a library, accesses some data in pass a weights vector down from C++ to R, deploy R and get the result back.
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; tab-width: 8; -*-
//
// Simple example for the repeated r-devel mails by Abhijit Bera
//
// Copyright (C) 2009 Dirk Eddelbuettel
// Copyright (C) 2010 - 2011 Dirk Eddelbuettel and Romain Francois
#include <RInside.h> // for the embedded R via RInside
int main(int argc, char *argv[]) {
try {
RInside R(argc, argv); // create an embedded R instance
std::string txt = "suppressMessages(library(fPortfolio))";
R.parseEvalQ(txt); // load library, no return value
txt = "M <- as.matrix(SWX.RET); print(head(M)); M";
// assign mat. M to NumericMatrix
Rcpp::NumericMatrix M = R.parseEval(txt);
std::cout << "M has "
<< M.nrow() << " rows and "
<< M.ncol() << " cols" << std::endl;
txt = "colnames(M)"; // assign columns names of M to ans and
// into string vector cnames
Rcpp::CharacterVector cnames = R.parseEval(txt);
for (int i=0; i<M.ncol(); i++) {
std::cout << "Column " << cnames[i]
<< " in row 42 has " << M(42,i) << std::endl;
}
} catch(std::exception& ex) {
std::cerr << "Exception caught: " << ex.what() << std::endl;
} catch(...) {
std::cerr << "Unknown exception caught" << std::endl;
}
exit(0);
}
This rinside_sample2.cpp, and there are lots more examples in the package. To build it, you just say 'make rinside_sample2' as the supplied Makefile is set up to find R, Rcpp and RInside.
Related
I'm trying to submit a package to CRAN which contains C++ code (I have no clue about C++, the cpp files were written by somebody else).
The R check complains about ‘std::cout’ (C++)
Compiled code should not call entry points which might terminate R nor
write to stdout/stderr instead of to the console, nor the C RNG
I found in the code the following command:
integrate_const(stepper_type( default_error_checker< double >( abs_error , rel_error ) ),
mDifEqn,
x,
0.0,
(precipitationLength * timeStep),
timeStep,
streaming_observer(std::cout) );
I guess R (CRAN) expects something else rather than std::cout... but what?
Your C++ project may well be using standard input and output.
The issue, as discussed in the Writing R Extensions manual, is that you then end up mixing two output systems: R's, and the C++ one.
So you are "encouraged" to replace all uses of, say,
std::cout << "The value of foo is " << foo << std::endl;
with something like
Rprintf("The value of foo is %f\n", foo);
so that your output gets blended properly with R's. In one of my (non-Rcpp) packages I had to do a lot of tedious patching for that...
Now, as mentioned in a comment by #vasicbre and an answer by #Dason, if you use Rcpp you can simply do
Rcpp::Rcout << "The value of foo is " << foo << std::endl;
If you already use Rcpp this is pretty easy, otherwise you need to decide if that makes it worth adding Rcpp...
edit: fixed typo in Rcpp::Rcout.
If you want to stream to R's buffered output you'll want to use Rcpp::Rcout instead of std::cout.
For more details you can read this article by one of Rcpp's authors: http://dirk.eddelbuettel.com/blog/2012/02/18/
I would like to, within my own compiled C++ code, check to see if a library package is loaded in R (if not, load it), call a function from that library and get the results back to in my C++ code.
Could someone point me in the right direction? There seems to be a plethora of info on R and different ways of calling R from C++ and vis versa, but I have not come across exactly what I am wanting to do.
Thanks.
Dirk's probably right that RInside makes life easier. But for the die-hards... The essence comes from Writing R Extensions sections 8.1 and 8.2, and from the examples distributed with R. The material below covers constructing and evaluating the call; dealing with the return value is a different (and in some sense easier) topic.
Setup
Let's suppose a Linux / Mac platform. The first thing is that R must have been compiled to allow linking, either to a shared or static R library. I work with an svn copy of R's source, in the directory ~/src/R-devel. I switch to some other directory, call it ~/bin/R-devel, and then
~/src/R-devel/configure --enable-R-shlib
make -j
this generates ~/bin/R-devel/lib/libR.so; perhaps whatever distribution you're using already has this? The -j flag runs make in parallel, which greatly speeds the build.
Examples for embedding are in ~/src/R-devel/tests/Embedding, and they can be made with cd ~/bin/R-devel/tests/Embedding && make. Obviously, the source code for these examples is extremely instructive.
Code
To illustrate, create a file embed.cpp. Start by including the header that defines R data structures, and the R embedding interface; these are located in bin/R-devel/include, and serve as the primary documentation. We also have a prototype for the function that will do all the work
#include <Rembedded.h>
#include <Rdefines.h>
static void doSplinesExample();
The work flow is to start R, do the work, and end R:
int
main(int argc, char *argv[])
{
Rf_initEmbeddedR(argc, argv);
doSplinesExample();
Rf_endEmbeddedR(0);
return 0;
}
The examples under Embedding include one that calls library(splines), sets a named option, then runs a function example("ns"). Here's the routine that does this
static void
doSplinesExample()
{
SEXP e, result;
int errorOccurred;
// create and evaluate 'library(splines)'
PROTECT(e = lang2(install("library"), mkString("splines")));
R_tryEval(e, R_GlobalEnv, &errorOccurred);
if (errorOccurred) {
// handle error
}
UNPROTECT(1);
// 'options(FALSE)' ...
PROTECT(e = lang2(install("options"), ScalarLogical(0)));
// ... modified to 'options(example.ask=FALSE)' (this is obscure)
SET_TAG(CDR(e), install("example.ask"));
R_tryEval(e, R_GlobalEnv, NULL);
UNPROTECT(1);
// 'example("ns")'
PROTECT(e = lang2(install("example"), mkString("ns")));
R_tryEval(e, R_GlobalEnv, &errorOccurred);
UNPROTECT(1);
}
Compile and run
We're now ready to put everything together. The compiler needs to know where the headers and libraries are
g++ -I/home/user/bin/R-devel/include -L/home/user/bin/R-devel/lib -lR embed.cpp
The compiled application needs to be run in the correct environment, e.g., with R_HOME set correctly; this can be arranged easily (obviously a deployed app would want to take a more extensive approach) with
R CMD ./a.out
Depending on your ambitions, some parts of section 8 of Writing R Extensions are not relevant, e.g., callbacks are needed to implement a GUI on top of R, but not to evaluate simple code chunks.
Some detail
Running through that in a bit of detail... An SEXP (S-expression) is a data structure fundamental to R's representation of basic types (integer, logical, language calls, etc.). The line
PROTECT(e = lang2(install("library"), mkString("splines")));
makes a symbol library and a string "splines", and places them into a language construct consisting of two elements. This constructs an unevaluated language object, approximately equivalent to quote(library("splines")) in R. lang2 returns an SEXP that has been allocated from R's memory pool, and it needs to be PROTECTed from garbage collection. PROTECT adds the address pointed to by e to a protection stack, when the memory no longer needs to be protected, the address is popped from the stack (with UNPROTECT(1), a few lines down). The line
R_tryEval(e, R_GlobalEnv, &errorOccurred);
tries to evaluate e in R's global environment. errorOccurred is set to non-0 if an error occurs. R_tryEval returns an SEXP representing the result of the function, but we ignore it here. Because we no longer need the memory allocated to store library("splines"), we tell R that it is no longer PROTECT'ed.
The next chunk of code is similar, evaluating options(example.ask=FALSE), but the construction of the call is more complicated. The S-expression created by lang2 is a pair list, conceptually with a node, a left pointer (CAR) and a right pointer (CDR). The left pointer of e points to the symbol options. The right pointer of e points to another node in the pair list, whose left pointer is FALSE (the right pointer is R_NilValue, indicating the end of the language expression). Each node of a pair list can have a TAG, the meaning of which depends on the role played by the node. Here we attach an argument name.
SET_TAG(CDR(e), install("example.ask"));
The next line evaluates the expression that we have constructed (options(example.ask=FALSE)), using NULL to indicate that we'll ignore the success or failure of the function's evaluation. A different way of constructing and evaluating this call is illustrated in R-devel/tests/Embedding/RParseEval.c, adapted here as
PROTECT(tmp = mkString("options(example.ask=FALSE)"));
PROTECT(e = R_ParseVector(tmp, 1, &status, R_NilValue));
R_tryEval(VECTOR_ELT(e, 0), R_GlobalEnv, NULL);
UNPROTECT(2);
but this doesn't seem like a good strategy in general, as it mixes R and C code and does not allow computed arguments to be used in R functions. Instead write and manage R code in R (e.g., creating a package with functions that perform complicated series of R manipulations) that your C code uses.
The final block of code above constructs and evaluates example("ns"). Rf_tryEval returns the result of the function call, so
SEXP result;
PROTECT(result = Rf_tryEval(e, R_GlobalEnv, &errorOccurred));
// ...
UNPROTECT(1);
would capture that for subsequent processing.
There is Rcpp which allows you to easily extend R with C++ code, and also have that C++ code call back to R. There are examples included in the package which show that.
But maybe what you really want is to keep your C++ program (i.e. you own main()) and call out to R? That can be done most easily with
RInside which allows you to very easily embed R inside your C++ application---and the test for library, load if needed and function call are then extremely easy to do, and the (more than a dozen) included examples show you how to. And Rcpp still helps you to get results back and forth.
Edit: As Martin was kind enough to show things the official way I cannot help and contrast it with one of the examples shipping with RInside. It is something I once wrote quickly to help someone who had asked on r-help about how to load (a portfolio optimisation) library and use it. It meets your requirements: load a library, accesses some data in pass a weights vector down from C++ to R, deploy R and get the result back.
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; tab-width: 8; -*-
//
// Simple example for the repeated r-devel mails by Abhijit Bera
//
// Copyright (C) 2009 Dirk Eddelbuettel
// Copyright (C) 2010 - 2011 Dirk Eddelbuettel and Romain Francois
#include <RInside.h> // for the embedded R via RInside
int main(int argc, char *argv[]) {
try {
RInside R(argc, argv); // create an embedded R instance
std::string txt = "suppressMessages(library(fPortfolio))";
R.parseEvalQ(txt); // load library, no return value
txt = "M <- as.matrix(SWX.RET); print(head(M)); M";
// assign mat. M to NumericMatrix
Rcpp::NumericMatrix M = R.parseEval(txt);
std::cout << "M has "
<< M.nrow() << " rows and "
<< M.ncol() << " cols" << std::endl;
txt = "colnames(M)"; // assign columns names of M to ans and
// into string vector cnames
Rcpp::CharacterVector cnames = R.parseEval(txt);
for (int i=0; i<M.ncol(); i++) {
std::cout << "Column " << cnames[i]
<< " in row 42 has " << M(42,i) << std::endl;
}
} catch(std::exception& ex) {
std::cerr << "Exception caught: " << ex.what() << std::endl;
} catch(...) {
std::cerr << "Unknown exception caught" << std::endl;
}
exit(0);
}
This rinside_sample2.cpp, and there are lots more examples in the package. To build it, you just say 'make rinside_sample2' as the supplied Makefile is set up to find R, Rcpp and RInside.
I'm trying to write a simple program to show how variables can be manipulated indirectly on the stack. In the code below everything works as planned: even though the address for a is passed in, I can indirectly change the value of c. However, if I delete the last line of code (or any of the last three), then this no longer applies. Do those lines somehow force the compiler to put my 3 in variables sequentially onto the stack? My expectation was that that would always be the case.
#include <iostream>
using namespace std;
void someFunction(int* intPtr)
{
// write some code to break main's critical output
int* cptr = intPtr - 2;
*cptr = 0;
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
someFunction(&a);
cout << a << endl;
cout << b << endl;
cout << "Critical value is (must be 3): " << c << endl;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl; //when commented out, critical value is 3
}
Your code causes undefined behaviour. You can't pass a pointer to an int and then just subtract an arbitrary amount from it and expect it to point to something meaningful. The compiler can put a, b, and c wherever it likes in whatever order it likes. There is no guaranteed relationship of any kind between them, so you you can't assume someFunction will do anything meaningful.
The compiler can place those wherever and in whatever order it likes in the current stack frame, it may even optimize them out if not used. Just make the compiler do what you want, by using arrays, where pointer arithmetic is safe:
int main()
{
int myVars[3] = {1,2,3};
//In C++, one could use immutable (const) references for convenience,
//which should be optimized/eliminated pretty well.
//But I would never ever use them for pointer arithmetic.
int& const a = myVars[0];
int& const b = myVars[1];
int& const c = myVars[2];
}
What you do is undefined behaviour, so anything may happen. But what is probably going on, is that when you don't take the adress of c by commenting out cout << &c << endl;, the compiler may optimize avay the variable c. It then substitutes cout << c with cout << 3.
As many have answered, your code is wrong since triggering undefined behavior, see also this answer to a similar question.
In your original code the optimizing compiler could place a, b and c in registers, overlap their stack location, etc....
There are however legitimate reasons for wanting to know what are the location of local variables on the stack (precise garbage collection, introspection and reflection, ...).
The correct way would then to pack these variables in a struct (or a class) and to have some way to access that structure (for example, linking them in a list, etc.)
So your code might start with
void fun (void)
{
struct {
int a;
int b;
int c;
} _frame;
#define a _frame.a
#define b _frame.b
#define c _frame.c
do_something_with(&_frame); // e.g. link it
You could also use array members (perhaps even flexible or zero-length arrays for housekeeping routines), and #define a _frame.v[0] etc...
Actually, a good optimizing compiler could optimize that nearly as well as your original code.
Probably, the type of the _frame might be outside of the fun function, and you'll generate housekeeping functions for inspecting, or garbage collecting, that _frame.
Don't forget to unlink the frame at end of the routine. Making the frame an object with a proper constructor and destructor definitely helps. The constructor would link the frame and the destructor would unlink it.
For two examples where such techniques are used (both because a precise garbage collector is needed), see my qish garbage collector and the (generated C++) code of MELT (a domain specific language to extend GCC). See also the (generated) C code of Chicken Scheme or Ocaml runtime conventions (and its <caml/memory.h> header).
In practice, such an approach is much more welcome for generated C or C++ code (precisely because you will also generate the housekeeping code). If writing them manually, consider at least fancy macros (and templates) to help you. See e.g. gcc/melt-runtime.h
I actually believe that this is a deficiency in C. There should be some language features (and compiler implementations) to introspect the stack and to (portably) backtrace on it.
Here is what I want to do:
Run a program and initialize some data structures.
Then compile additional code that can access/modify the existing data structures.
Repeat step 2 as needed.
I want to be able to do this with both C and C++ using gcc (and eventually Java) on Unix-like systems (especially Linux and Mac OS X). The idea is to basically implement a read-eval-print loop for these languages that compiles expressions and statements as they are entered and uses them to modify existing data structures (something that is done all the time in scripting languages). I am writing this tool in python, which generates the C/C++ files, but this should not be relevant.
I have explored doing this with shared libraries but learned that modifying shared libraries does not affect programs that are already running. I have also tried using shared memory but could not find a way to load a function onto the heap. I have also considered using assembly code but have not yet attempted to do so.
I would prefer not to use any compilers other than gcc unless there is absolutely no way to do it in gcc.
If anyone has any ideas or knows how to do this, any help will be appreciated.
There is one simple solution:
create own library having special functions
load created library
execute functions from that library, pass structures as function variables
To use your structures you have to include same header files like in host application.
structs.h:
struct S {
int a,b;
};
main.cpp:
#include <iostream>
#include <fstream>
#include <dlfcn.h>
#include <stdlib.h>
#include "structs.h"
using namespace std;
int main ( int argc, char **argv ) {
// create own program
ofstream f ( "tmp.cpp" );
f << "#include<stdlib.h>\n#include \"structs.h\"\n extern \"C\" void F(S &s) { s.a += s.a; s.b *= s.b; }\n";
f.close();
// create library
system ( "/usr/bin/gcc -shared tmp.cpp -o libtmp.so" );
// load library
void * fLib = dlopen ( "./libtmp.so", RTLD_LAZY );
if ( !fLib ) {
cerr << "Cannot open library: " << dlerror() << '\n';
}
if ( fLib ) {
int ( *fn ) ( S & ) = dlsym ( fLib, "F" );
if ( fn ) {
for(int i=0;i<11;i++) {
S s;
s.a = i;
s.b = i;
// use function
fn(s);
cout << s.a << " " << s.b << endl;
}
}
dlclose ( fLib );
}
return 0;
}
output:
0 0
2 1
4 4
6 9
8 16
10 25
12 36
14 49
16 64
18 81
20 100
You can also create mutable program that will be changing itself (source code), recompiling yourself and then replace it's actual execution with execv and save resources with shared memory.
I think you may be able to accomplish this using dynamic libraries and loading them at runtime (using dlopen and friends).
void * lib = dlopen("mynewcode.so", RTLD_LAZY);
if(lib) {
void (*fn)(void) = dlsym(lib, "libfunc");
if(fn) fn();
dlclose(lib);
}
You would obviously have to be compiling the new code as you go along, but if you keep replacing mynewcode.so I think this will work for you.
Even though LLVM is now used today mostly for its optimizations and backend roles in compilation, as its core it is the Low-Level Virtual Machine.
LLVM can JIT code, even though the return types may be quite opaque, so if you are ready to wrap your own code around it and don't worry too much about the casts that are going to take place, it may help you.
However C and C++ are not really friendly for this kind of thing.
Yes - you can do this with Runtime Compiled C++ (or take a look at the RCC++ blog and videos), or one of its alternatives.
This can be done portably with OpenCL
OpenCL is a widely supported standard, mainly used for offloading calculations to specialized hardware, such as GPUs. However, it also works just fine on CPUs and actually performs run-time compilation of C99-like code as one of its core features (this is how the hardware portability is achieved). The newer versions (2.1+) also accept a large subset of C++14.
A basic example of such run-time compilation & execution might look something like this:
#ifdef __APPLE__
#include<OpenCL/opencl.h>
#else
#include<CL/cl.h>
#endif
#include<stdlib.h>
int main(int argc,char**argv){//assumes source code strings are in argv
cl_int e = 0;//error status indicator
cl_platform_id platform = 0;
cl_device_id device = 0;
e=clGetPlatformIDs(1,&platform,0); if(e)exit(e);
e=clGetDeviceIDs(platform,CL_DEVICE_TYPE_ALL,1,&device,0); if(e)exit(e);
cl_context context = clCreateContext(0,1,&device,0,0,&e); if(e)exit(e);
cl_command_queue queue = clCreateCommandQueue(context,device,0,&e); if(e)exit(e);
//the lines below could be done in a loop, assuming you release each program & kernel
cl_program program = clCreateProgramWithSource(context,argc,(const char**)argv,0,&e);
cl_kernel kernel = 0; if(e)exit(e);
e=clBuildProgram(program,1,&device,0,0,0); if(e)exit(e);
e=clCreateKernelsInProgram(program,1,&kernel,0); if(e)exit(e);
e=clSetKernelArg(kernel,0,sizeof(int),&argc); if(e)exit(e);
e=clEnqueueTask(queue,kernel,0,0,0); if(e)exit(e);
//realistically, you'd also need some buffer operations around here to do useful work
}
If nothing else works - in particular, if un-loading a shared library ends up not being supported on your runtime platform, you could do it the hard way.
1) use system() or whatever to execute gcc or make or whatever to build the code
2) either link it as a flat binary or parse whatever format (elf?) the linker outputs on your platform yourself
3) get yourself some executable pages, either by mmap()'ing an executable file or do doing an anonymous mmap with the execute bit set and copying/unpacking your code there (not all platforms care about that bit, but let's assume you have one that does)
4) flush any data and instruction caches (since consistency between the two is typically not guaranteed)
5) call it via a function pointer or whatever
Of course there's another option too - depending on the level of interaction you need, you could build a separate program and either launch it and wait for the result, or fork off and launch it and talk to it by pipes or sockets. If this would meet your needs, it would be a lot less tricky.
It's simple - is there a way to use this lib to call stored procedure or function, that returns more than one result? I know about the ITRoutingManager, but it seems to return just one value..
In details, here's what I mean:
CREATE FUNCTION test_out1( pin INT )
RETURNING INT;
DEFINE param INT;
LET param = 321;
RETURN param;
END FUNCTION;
Returns 321, I can get the value with ITValue and ITConversions. So this is fine. But the following is not:
CREATE FUNCTION test_out2( pin INT )
RETURNING INT, INT;
DEFINE param INT;
LET param = 321;
DEFINE param2 INT;
LET param2 = 123;
RETURN param, param2;
END FUNCTION;
When I do routine.GetRoutine( "function test_out2( int )" ), it's bound fine, so no problem with that. But see this:
std::cout << "Result type: " << routine.ResultType()->Name() IsRow() ? "row, " : ", " )
<< (routine.ResultType()->IsCollection() ? "collection, " : ", " )
<< routine.ResultType()->Quality() << "\n\n";
prints integer, , , null, note the integer.. Why is integer, not row, for example. And how to get the 2 values, returned by the function? Another interesting fact - the returned value is 0(when I convert it to int, using the ITConversions class), not 123, nor 321..
There have to be a way. This is a special library, written for Informix servers, by Informix developers and it would be strange, if this is not possible.
The same for functions, but I guess it's the same there.
NOTE: there's no such thing as out parameters in the common case, for informix procedures/functions (Informix: procedure with output parameters?)
As you note, Informix does not have 'OUT' parameters really; it returns values.
In regular ESQL/C, you treat a list of output values symmetrically with a list of input values; I would expect to do the same with this code, therefore. Whatever technique you use to pass 2 arguments to the function is likely - but by no means guaranteed - to be how you get multiple returned values.
In case of doubt, treat it as you would treat a SELECT statement that returns multiple rows. That is, do the analogues of:
PREPARE s FROM "EXECUTE PROCEDURE test_out2(?)";
DECLARE c CURSOR FOR s;
OPEN c USING :input_value;
while (sqlca.sqlcode == 0)
{
FETCH c INTO :out_value1, :out_value2;
if (sqlca.sqlcode != 0)
break;
...use values...
}
CLOSE c;
FREE c;
FREE s;
I would recommend using ODBC over OIC++. The OIC++ interface is built on top of another library, DMI, which in turn is built on top of another library that accesses the DBMS - I forget whether it is ESQL/C or ODBC based, or one of the core libraries those are built on. Using unixODBC as your driver manager and an appropriate ODBC driver makes more sense to me than using OIC++.
I've no real idea how that looks in OIC++; I've never used it, but only done some cursory maintenance of it.
On the whole, you would be best advised not to use OIC++ for new work. Informix continues to distribute it for backwards compatibility rather than to encourage new use.