I am writing an app that should use jobject
's fields multiple times at a great speed. Since retrieving fields from jobjects
is pretty slow, I want to do it once and then read and write fields of some C++ object or elements of a multitype array (if one can be created) or whatever that provides rapid data access (hereinafter - target container).
My code has two functions: first writes jobject
fields into the target container, second reads the written data from it.
Target container should be capable of keeping variables of different type, and it's size must not be compile-time-defined (so struct
is not suitable). The question is: what is the best container satisfying this conditions, if quality of it is defined by speed of reading (+converting types to proper ones) from it?
Right now, I use a vector<any>
like below:
#include <vector>
#include <any>
JNIEXPORT void JNICALL Java_runTask(JNIEnv*env,jclass,jobject o){
vector<any>d;
readData(d,/*some other arguments*/,o);
do{
//do something useful
}while(predicate);
}
void readData(vector<any>*data/*some other arguments*/,jobject o){
//fill the vector with jobject's fields' values
}
bool predicate(vector<any>*data){
//determine the return value using vector contents
return true;
}
vector<any>
meets my conditions, BUT...
any_cast<type>()
is CHECKING the type before case, which means that it has imperfect performance.Rather than trying to use a container that can hold arbitrary data, we can wrap the data read and use in a polymorphic type.
As a sketch, you would rework Java_runTask
to not have to deal with the data directly.
struct BaseTask
{
virtual bool predicate() = 0;
virtual Result process() = 0; // whatever "do something useful" does
};
std::unique_ptr<BaseTask> readData(JNIEnv* env, jclass c, jobject o)
{
// based on c, construct a subclass of BaseTask, and read the data from o into it
}
JNIEXPORT void JNICALL Java_runTask(JNIEnv*env,jclass,jobject o){
auto task = readData(/*some other arguments*/,o);
do{
auto res = task->process();
// do more processing
} while(task->predicate());
}
You then need to have subclasses of BaseTask specific to your different kinds of data. One way of doing that is having a template subclass
template <typename T>
struct Task : public BaseTask
{
T data;
bool predicate() override;
Result process() override;
};
template <>
bool Task<Foo>::predicate()
{
// data is a Foo here
}
template <>
bool Task<Bar>::predicate()
{
// data is a Bar here
}
That's basically the same as defining
class FooTask : public BaseTask { /* all the members */ };
class BarTask : public BaseTask { /* all the members */ };