Reputation: 381
I'm a Java Programmer; I last wrote C or C++ 20 years ago. Now I've returned, I'm trying to use a more modern C++ such as C++11/C++14 and to avoid old c-style programming.
How do I assign a string, as in Java:
private static final String MY_STRING = "Hello World"; // could also be public
Now I have the reference MY_STRING
to the text "Hello World"
, which I can use as many times in my program as I want, and if I wish to change it to say "Hello Dolly"
, I do the change exactly in one single place.
I would like to achieve the same in C++. Example:
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = (char *)"-Djava.class.path=.";
This works, no more questions, but I don't like it because of the requirement to be able to change it in one place only if required. So, I declare (even in the header file):
std::string classpath = "-Djava.class.path=.";
// or
const std::string classpath = "-Djava.class.path=.";
and I use it as:
options[0].optionString = (char *)classpath.c_str();
Now, using the c_str()
function, this works also, but I'm back to plain old C!
I would really like to know if there is a mean to stay at a more modern C++ level?
Yes I know,
JavaVMOption* options = new JavaVMOption[1];
is declared as it is and I can't change it. But even in C++ it's a good idea to work with a reference rather than with a pointer in the given case, as in Java. Is there a solution? I was not able to find one.
Upvotes: 2
Views: 1339
Reputation: 2463
I'm a java programmer and I found the same example when trying to use JNI to call Java from C++. That said, I don't know if this is "correct", but it got the code to compile without errors by changing the original code:
JavaVMOption* options = new JavaVMOption[1]; // JVM invocation options
options[0].optionString = "-Djava.class.path=."
To use a char array instead.
JavaVMOption* options = new JavaVMOption[1];
char classpath[] = "-Djava.class.path=.";
options[0].optionString = classpath;
My basic understanding is that a char* is the same as a char[].
Upvotes: 0
Reputation: 275906
"Hello"
is a const buffer of 6 char
of value H
e
l
l
o
\0
respectively whose lifetime is the entire program.
std::string bob = "Hello";
copies that buffer into a std::string
object. std::string
is a value semantics object; this is something that Java tends to lack, as all objects are implicitly passed by reference everywhere.
options[0].optionString = (char *)"-Djava.class.path=.";
this is extremely dangerous. You cast that buffer to a non-const
pointer to char
, then assigned it to optionString
.
I have no idea what optionString
is, but if it is a variable of type char*
, this is opening you up to undefined behavior. Any editing of the buffer "-Djava.class.path=."
is undefined behavior, and storing a non-const
pointer to such a buffer is just begging for that to happen.
In short, the type of optionString
is key to how dangerous that is. Is it merely reckless, or actually really stupid?
JavaVMOption* options = new JavaVMOption[1];
this creates on the heap an array of JavaVMOption
of size 1
then stores a pointer to the first element in options
.
There are so many pointless things here. And destroying that requires destroying it as an array?
JavaVMOption options;
this creates a JavaVMOption
on the stack called options
. It is automatically destroyed at the end of scope. This seems much more practical. There is no point in using new
unless you need new
, and you rarely need new
in C++.
I found some sample code:
#include <jni.h> /* where everything is defined */
...
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/usr/lib/java";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
/* load and initialize a Java VM, return a JNI interface
* pointer in env */
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
delete options;
/* invoke the Main.test method using the JNI */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);
/* We are done. */
jvm->DestroyJavaVM();
and that wasn't written by someone who programs in C++ for a living.
Here is my initial stab at it:
struct destroy_java_vm {
void operator()(JavaVM* jvm)const{
jvm->DestroyJavaVM();
}
};
using up_java_vm = std::unique_ptr< JavaVM, destroy_java_vm >;
struct java_vm {
up_java_vm vm;
JNIEnv* env = 0;
};
struct java_vm_option {
std::string string;
std::shared_ptr<void> extra_info;
};
using java_vm_options = std::vector<java_vm_option>;
struct java_vm_init {
unsigned version = JNI_VERSION_1_2;
java_vm_options options;
bool ignore_unrecognized = false;
java_vm init() {
std::vector<JavaVMOption> java_options(options.size());
for (std::size_t i = 0; i < options.size(); ++i) {
java_options[i].optionString = &options.string[0];
java_options[i].extraInfo = options.extra_info.get();
}
JavaVMInitArgs args;
args.version = version;
args.options = java_options.data();
args.nOptions = java_options.size();
args.ignoreUnrecognized = ignore_unrecognized?TRUE:FALSE;
java_vm retval;
JavaVM* tmp = 0;
auto res = JNI_CreateJavaVM(&tmp, (void **)&retval.env, &args);
if (res < 0) {
return {}; // error message? How?
}
retval.vm.reset( tmp );
return retval;
}
}
Use:
java_vm_init init;
init.options.push_back("-Djava.class.path=.");
auto vm = init.init();
if (!vm.env) { return /* error case */; }
/* invoke the Main.test method using the JNI */
jclass cls = vm.env->FindClass("Main");
jmethodID mid = vm.env->GetStaticMethodID(cls, "test", "(I)V");
vm.env->CallStaticVoidMethod(cls, mid, 100);
/* We are done. */
By my use of unique_ptr
, a java_vm
object is a value type that is move-only. When it is destroyed, the java vm is destroyed automatically (if you didn't move it out).
We have to move over to C-style code when talking directly to the API, because many cross-language APIs are written using C-style interfaces. We wrap that C-style interface in a safer and easier to use C++ type.
Code not tested, probably contains typos.
Upvotes: 2