Related
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.
Often I just want to quickly check the contents of a series of variables (let's call them a,b,c,d and e, and suppose they're a mixture of floats, integers and strings). I'm fed up typing
cout << a << " " << b << " " << " " << c << " " << " " << d << " " << e << endl;
Is there a more convenient (less key-strokes) way to quickly dump a few variables to stdout in C++? Or do C++ people just always define their own simple print function or something? Obviously something like
printf("%d %f %s %d %s\n",a,b,c,d,e);
is not the alternative I'm looking for, but rather something like
print a,b,c,d,e
Even
print*, a,b,c,d,e
or
write(*,*) a,b,c,d,e
isn't too inconvenient to type.
Of course, googling 'quickly print to screen in C++' keeps just sending me back to std::cout.
Is it that, what you want?
print(a, b, c);
That would be this.
template <typename T>
void print(T t)
{
std::cout << t << " ";
}
template<typename T, typename... Args>
void print(T t, Args... args)
{
std::cout << t << " ";
print(args...) ;
}
It's easy to create a "print" class which have an overloaded template operator,, then you could do something like
print(),a,b,c,d;
The expression print() would create a temporary instance of the print class, and then use that temporary instance for the printing with the comma operator. The temporary instance would be destroyed at the end of the expression (after last comma overload is called).
The implementation could look something like this:
struct print
{
template<typename T>
print& operator,(const T& v)
{
std::cout << v;
return *this;
}
};
Note: This is just off my head, without any testing.
Is there a more convenient (less key-strokes) way to quickly dump a
few variables to stdout in C++?
I would have to say No, not in the language. But I do not consider std::cout a challenging amount to type.
You can tryout the template methods provided by other answer's.
But you should try GDB (or some debugger available on your system). GDB can 'dump' automatic variables with no _effort_ at all, as automatic var's for the current stack frame are always kept up-to-date in the "Locals" window.
Or do C++ people just always define their own simple print function or something?
No, or maybe something.
I use std::cout and std::cerr (as defined) for lots of debugging, but not in the 'how can I save the most typing' frame of mind.
My view is that creating a 'convenience' (i.e. not required) function is appropriate for doing something you wish to repeat. My rule of thumb is 3 times ... if I do a particular something 3 (or more) times (like generate a std::cout statement with the same or similar variables in it) then I might write a function (rather than copy the line) for that repeated effort.
Typically, I use one of two (what I call) disposable debug methods ... and most of my objects also have both show() and dump(), and there can be multiple show/dump functions or methods, each with different signatures, and default values.
if(dbg1) show(a,b,c,d,e);
if(dbg1b) show(b);
// etc
and
if(dbg2) dump(a,b,c,d,e);
Show typically uses and does what what std::cout provides, and little else.
Dump does what show does, but also might provide an alternate view of the data, either hex or binary translations of the values, or perhaps tables. What ever helps.
Disposable does not mean I will dispose of them, but rather I might, and I often get tired of output that does not change, so I set dbgX to false when this code seems to be working, at least until I decide to dispose of the debug invocation.
But then, you do have to implement each of the functions and methods, and yes, you are going to have to learn to type.
If these variables are automatic, you should know that the debugger GDB automatically displays them in a window called "Locals", and keeps them up-to-date during single step.
In GDB, object instance contents can often be displayed with "p *obj", and there are ways to add a particular obj name to the local display window.
It does not take a lot to run GDB. If you object to creating the 80 char std::cout code above, it takes far less typing to launch GDB, set break in main, and run the simple task under gdb control, (then single step to observe these variables at any step in your code) not just where you happened to insert a show() or dump() command.
And if you have GDB, you can also command to print using "p show()" (when the show() function is in scope) to see what the in-scope variables look like to std::cout (if you don't believe the "Locals" window).
GDB allows you to "p this->show()" when stepping through a method of the instance, or "p myObj->show()" when the myObj is accessible.
ALso, "p *this" and "p *myObj" will provide a default, typically useful, display of the current contents your object.
Anyway. Yes you can always work hard to shorten your typing effort.
Our project uses a macro to make logging easy and simple in one-line statements, like so:
DEBUG_LOG(TRACE_LOG_LEVEL, "The X value = " << x << ", pointer = " << *x);
The macro translates the 2nd parameter into stringstream arguments, and sends it off to a regular C++ logger. This works great in practice, as it makes multi-parameter logging statements very concise. However, Scott Meyers has said, in Effective C++ 3rd Edition, "You can get all the efficiency of a macro plus all the predictable behavior and type safety of a regular function by using a template for an inline function" (Item 2). I know there are many issues with macro usage in C++ related to predictable behavior, so I'm trying to eliminate as many macros as possible in our code base.
My logging macro is defined similar to:
#define DEBUG_LOG(aLogLevel, aWhat) { \
if (isEnabled(aLogLevel)) { \
std::stringstream outStr; \
outStr<< __FILE__ << "(" << __LINE__ << ") [" << getpid() << "] : " << aWhat; \
logger::log(aLogLevel, outStr.str()); \
}
I've tried several times to rewrite this into something that doesn't use macros, including:
inline void DEBUG_LOG(LogLevel aLogLevel, const std::stringstream& aWhat) {
...
}
And...
template<typename WhatT> inline void DEBUG_LOG(LogLevel aLogLevel, WhatT aWhat) {
... }
To no avail (neither of the above 2 rewrites will compile against our logging code in the 1st example). Any other ideas? Can this be done? Or is it best to just leave it as a macro?
Logging remains one of the few places were you can't completely do away with macros, as you need call-site information (__LINE__, __FILE__, ...) that isn't available otherwise. See also this question.
You can, however, move the logging logic into a seperate function (or object) and provide just the call-site information through a macro. You don't even need a template function for this.
#define DEBUG_LOG(Level, What) \
isEnabled(Level) && scoped_logger(Level, __FILE__, __LINE__).stream() << What
With this, the usage remains the same, which might be a good idea so you don't have to change a load of code. With the &&, you get the same short-curcuit behaviour as you do with your if clause.
Now, the scoped_logger will be a RAII object that will actually log what it gets when it's destroyed, aka in the destructor.
struct scoped_logger
{
scoped_logger(LogLevel level, char const* file, unsigned line)
: _level(level)
{ _ss << file << "(" << line << ") [" << getpid() << "] : "; }
std::stringstream& stream(){ return _ss; }
~scoped_logger(){ logger::log(_level, _ss.str()); }
private:
std::stringstream _ss;
LogLevel _level;
};
Exposing the underlying std::stringstream object saves us the trouble of having to write our own operator<< overloads (which would be silly). The need to actually expose it through a function is important; if the scoped_logger object is a temporary (an rvalue), so is the std::stringstream member and only member overloads of operator<< will be found if we don't somehow transform it to an lvalue (reference). You can read more about this problem here (note that this problem has been fixed in C++11 with rvalue stream inserters). This "transformation" is done by calling a member function that simply returns a normal reference to the stream.
Small live example on Ideone.
No, it is not possible to rewrite this exact macro as a template since you are using operators (<<) in the macro, which can't be passed as a template argument or function argument.
We had the same issue and solved it with a class based approach, using a syntax like
DEBUG_LOG(TRACE_LOG_LEVEL) << "The X value = " << x << ", pointer = " << *x << logger::flush;
This would indeed require to rewrite the code (by using a regular expression) and introduce some class magic, but gives the additional benefit of greater flexibiliy (delayed output, output options per log level (to file or stdout) and things like that).
The problem with converting that particular macro into a function is that things like "The X value = " << x are not valid expressions.
The << operator is left-associative, which means something in the form A << B << C is treated as (A << B) << C. The overloaded insertion operators for iostreams always return a reference to the same stream so you can do more insertions in the same statement. That is, if A is a std::stringstream, since A << B returns A, (A << B) << C; has the same effect as A << B; A << C;.
Now you can pass B << C into a macro just fine. The macro just treats it as a bunch of tokens, and doesn't worry about what they mean until all the substituting is done. At that point, the left-associative rule can kick in. But for any function argument, even if inlined and templated, the compiler needs to figure out what the type of the argument is and how to find its value. If B << C is invalid (because B is neither a stream nor an integer), compiler error. Even if B << C is valid, since function parameters are always evaluated before anything in the invoked function, you'll end up with the behavior A << (B << C), which is not what you want here.
If you're willing to change all the uses of the macro (say, use commas instead of << tokens, or something like #svenihoney's suggestion), there are ways to do something. If not, that macro just can't be treated like a function.
I'd say there's no harm in this macro though, as long as all the programmers who have to use it would understand why on a line starting with DEBUG_LOG, they might see compiler errors relating to std::stringstream and/or logger::log.
If you keep a macro, check out C++ FAQ answers 39.4 and 39.5 for tricks to avoid a few nasty ways macros like this can surprise you.
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.
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.