JNI: Create HashMap - java-native-interface

How do I create a HashMap object in JNI?

Here is code, you will need to modify to work
jclass mapClass = (*env)->FindClass(env, "java/util/HashMap");
if(mapClass == NULL)
{
return NULL;
}
jsize map_len = 1;
jmethodID init = (*env)->GetMethodID(env, mapClass, "<init>", "(I)V");
jobject hashMap = (*env)->NewObject(env, mapClass, init, map_len);
jmethodID put = (*env)->GetMethodID(env, mapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
while( ... )
{
jint key = ...;
size_t sz = t->count;
jbyteArray dd = (*env)->NewByteArray(env, sz);
for(i = 0; i < sz; i++)
{
(*env)->SetByteArrayRegion(env, dd, i, 1, *data++);
}
(*env)->CallObjectMethod(env, hashMap, put, key, dd);
(*env)->DeleteLocalRef(env, key);
(*env)->DeleteLocalRef(env, dd);
}
(*env)->DeleteLocalRef(env, hashMap);
(*env)->DeleteLocalRef(env, mapClass);

Below is my contribution to this question, I have used ideas from other answers and other places. Below are two functions that convert std::map<std::string, std::string> to HashMap and back:
jobject StlStringStringMapToJavaHashMap(JNIEnv *env,
const std::map<std::string, std::string>& map);
void JavaHashMapToStlStringStringMap(JNIEnv *env, jobject hashMap,
std::map<std::string, std::string>& mapOut);
Test function:
void TestConversions(JNIEnv *env);
gives examples how to use it - btw. I have run this test on android device and it works.
jobject StlStringStringMapToJavaHashMap(JNIEnv *env, const std::map<std::string, std::string>& map) {
jclass mapClass = env->FindClass("java/util/HashMap");
if(mapClass == NULL)
return NULL;
jmethodID init = env->GetMethodID(mapClass, "<init>", "()V");
jobject hashMap = env->NewObject(mapClass, init);
jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
std::map<std::string, std::string>::const_iterator citr = map.begin();
for( ; citr != map.end(); ++citr) {
jstring keyJava = env->NewStringUTF(citr->first.c_str());
jstring valueJava = env->NewStringUTF(citr->second.c_str());
env->CallObjectMethod(hashMap, put, keyJava, valueJava);
env->DeleteLocalRef(keyJava);
env->DeleteLocalRef(valueJava);
}
jobject hashMapGobal = static_cast<jobject>(env->NewGlobalRef(hashMap));
env->DeleteLocalRef(hashMap);
env->DeleteLocalRef(mapClass);
return hashMapGobal;
}
// Based on android platform code from: /media/jni/android_media_MediaMetadataRetriever.cpp
void JavaHashMapToStlStringStringMap(JNIEnv *env, jobject hashMap, std::map<std::string, std::string>& mapOut) {
// Get the Map's entry Set.
jclass mapClass = env->FindClass("java/util/Map");
if (mapClass == NULL) {
return;
}
jmethodID entrySet =
env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
if (entrySet == NULL) {
return;
}
jobject set = env->CallObjectMethod(hashMap, entrySet);
if (set == NULL) {
return;
}
// Obtain an iterator over the Set
jclass setClass = env->FindClass("java/util/Set");
if (setClass == NULL) {
return;
}
jmethodID iterator =
env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
if (iterator == NULL) {
return;
}
jobject iter = env->CallObjectMethod(set, iterator);
if (iter == NULL) {
return;
}
// Get the Iterator method IDs
jclass iteratorClass = env->FindClass("java/util/Iterator");
if (iteratorClass == NULL) {
return;
}
jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
if (hasNext == NULL) {
return;
}
jmethodID next =
env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
if (next == NULL) {
return;
}
// Get the Entry class method IDs
jclass entryClass = env->FindClass("java/util/Map$Entry");
if (entryClass == NULL) {
return;
}
jmethodID getKey =
env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
if (getKey == NULL) {
return;
}
jmethodID getValue =
env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
if (getValue == NULL) {
return;
}
// Iterate over the entry Set
while (env->CallBooleanMethod(iter, hasNext)) {
jobject entry = env->CallObjectMethod(iter, next);
jstring key = (jstring) env->CallObjectMethod(entry, getKey);
jstring value = (jstring) env->CallObjectMethod(entry, getValue);
const char* keyStr = env->GetStringUTFChars(key, NULL);
if (!keyStr) { // Out of memory
return;
}
const char* valueStr = env->GetStringUTFChars(value, NULL);
if (!valueStr) { // Out of memory
env->ReleaseStringUTFChars(key, keyStr);
return;
}
mapOut.insert(std::make_pair(std::string(keyStr), std::string(valueStr)));
env->DeleteLocalRef(entry);
env->ReleaseStringUTFChars(key, keyStr);
env->DeleteLocalRef(key);
env->ReleaseStringUTFChars(value, valueStr);
env->DeleteLocalRef(value);
}
}
void TestConversions(JNIEnv *env) {
// Empty test
{
std::map<std::string, std::string> map, mapTest;
jobject hashMap = StlStringStringMapToJavaHashMap(env, map);
JavaHashMapToStlStringStringMap(env, hashMap, mapTest);
assert(map == mapTest);
}
// One element test
{
std::map<std::string, std::string> map, mapTest;
map["one"] = "uno";
jobject hashMap = StlStringStringMapToJavaHashMap(env, map);
JavaHashMapToStlStringStringMap(env, hashMap, mapTest);
assert(map == mapTest);
}
// Two element test
{
std::map<std::string, std::string> map, mapTest;
map["one"] = "uno";
map["two"] = "duo";
jobject hashMap = StlStringStringMapToJavaHashMap(env, map);
JavaHashMapToStlStringStringMap(env, hashMap, mapTest);
assert(map == mapTest);
}
// Huge number of elements test
{
std::map<std::string, std::string> map, mapTest;
for (int n = 0; n < 10000; ++n) {
map[std::to_string(n)] = std::to_string(n);
}
jobject hashMap = StlStringStringMapToJavaHashMap(env, map);
JavaHashMapToStlStringStringMap(env, hashMap, mapTest);
assert(map == mapTest);
}
}

For me I found that the signature of the "put" method needed to be different from that listed in the example above. i.e.
jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

See here:
Some example code to call a String constructor:
jstring
MyNewString(JNIEnv *env, jchar *chars, jint len)
{
jclass stringClass;
jmethodID cid;
jcharArray elemArr;
jstring result;
stringClass = (*env)->FindClass(env, "java/lang/String");
if (stringClass == NULL) {
return NULL; /* exception thrown */
}
/* Get the method ID for the String(char[]) constructor */
cid = (*env)->GetMethodID(env, stringClass,
"<init>", "([C)V");
if (cid == NULL) {
return NULL; /* exception thrown */
}
/* Create a char[] that holds the string characters */
elemArr = (*env)->NewCharArray(env, len);
if (elemArr == NULL) {
return NULL; /* exception thrown */
}
(*env)->SetCharArrayRegion(env, elemArr, 0, len, chars);
/* Construct a java.lang.String object */
result = (*env)->NewObject(env, stringClass, cid, elemArr);
/* Free local references */
(*env)->DeleteLocalRef(env, elemArr);
(*env)->DeleteLocalRef(env, stringClass);
return result;
}

One may also consider alternatives to directly using JNI - e.g. tools that can generate JNI code for you. For example, JANET (disclaimer: I wrote it) allows you to embed Java code in your native methods, so creating and using a hash map is then as simple as:
... (C++ code)
`Map map = new HashMap();` // embedded Java
... (C++ code)
... const char* foo = "foo";
`map.put(#$(foo), 50);` // ["foo" -> 50]
the back-ticked statements get translated by JANET to JNI code, so you never have to worry about signatures, reference handling, exception handling, etc. yet you still get the performance of JNI.

Related

Problems creating a char array from a QMap of QString

I'm using the libxslt C library, and need to pass in parameters as const char *. I'm wrapping the library in a Qt C++ class, so the parameters are stored in the C++ class are store as QMap<QString, QString>.
My first attempt was simply:
const char *params[32];
int index = 0;
if (m_params.size() > 0) {
QMapIterator<QString, QString> it(m_params);
while (it.hasNext()) {
it.next();
params[index++] = it.key().toLocal8Bit().data();
params[index++] = it.value().toLocal8Bit().data();
}
}
params[index++] = nullptr;
qDebug() << params[0] << params[1]; // 0 0
But I realise that this isn't working because the QByteArray from toLocal8bit goes out of scope almost as soon as I've used it.
I've tried using strcpy - but have the same scope issues:
m_params.insert("some-key", "some-value", "another-key", "another-value");
if (m_params.size() > 0) {
QMapIterator<QString, QString> it(m_params);
while (it.hasNext()) {
it.next();
char buffer[32];
strcpy(buffer, it.key().toLocal8Bit().data());
params[index++] = buffer;
strcpy(buffer, it.value().toLocal8Bit().data());
params[index++] = buffer;
}
}
params[index++] = nullptr;
qDebug() << params[0] << params[1]; // another-value another-value
So now I have a list of params all with the same value.
When I set all the values manually, I get the expected outcomes:
const char *params[32];
int index = 0;
params[index++] = "something";
params[index++] = "something-else";
params[index++] = nullptr;
qDebug() << params[0] << params[1]; // something something-else
This is easy enough - you need to ensure that the buffer for the parameters persists long enough. Do not use fixed-size arrays - you're setting yourself up for a buffer overflow.
class Params {
QByteArray buf;
QVector<const char *> params;
public:
Params() = default;
template <class T> explicit Params(const T& map) {
QVector<int> indices;
indices.reserve(map.size());
params.reserve(map.size()+1);
for (auto it = map.begin(); it != map.end(); ++it) {
indices.push_back(buf.size());
buf.append(it.key().toLocal8Bit());
buf.append('\0');
indices.push_back(buf.size());
buf.append(it.value().toLocal8Bit());
buf.append('\0');
}
for (int index : qAsConst(indices))
params.push_back(buf.constData() + index);
params.push_back(nullptr);
}
operator const char **() const { return const_cast<const char**>(params.data()); }
operator const char *const*() const { return params.data(); }
operator QVector<const char*>() const { return params; }
};
void MyClass::method() const {
Params params{m_params};
...
res = xsltApplyStylesheet(cur, doc, params);
...
}

Thread-safety of xll functions

I have this stupid xll function :
__declspec(dllexport) long F(long arg1, long arg2)
{
return arg1 + arg2;
}
I guess there's no problem with it, the resulting excel function will be thread-safe.
But what about a function taking arrays and returning arrays ? Consider for instance the function :
__declspec(dllexport) LPXLOPER12 WINAPI G(LPXLOPER12 arrayin1, LPXLOPER12 arrayin2)
{
static XLOPER12 xlArray; // the reference to this one is going to be returned
// treating arrayin1 and arrayin2 and outputting something in xlArray
return static_cast<LPXLOPER12>(&xlArray);
}
Obviously the static is bad for thread-safety and concurrent call in excel. How should I modify my function to ensure thread-safety ?
EDIT
Example of code using handles :
__declspec(dllexport) LPXLOPER12 WINAPI CreateComplex(LPXLOPER12 arrayin1, LPXLOPER12 arrayin2)
{
static XLOPER12 xlArray;
xlArray.xltype = xltypeStr;
if (arrayin1->xltype != xltypeNum)
{
xlArray.val.str = L"blah";
return static_cast<LPXLOPER12>(&xlArray);
}
if (arrayin2->xltype != xltypeNum)
{
xlArray.val.str = L"blahblah";
return static_cast<LPXLOPER12>(&xlArray);
}
double real = arrayin1->val.num;
double imag = arrayin2->val.num;
Complex * z = new Complex(real, imag);
char * handle = StoreObject("Complex", z);
xlArray.xltype = xltypeStr;
FromCharPtrToWChartPtr(handle, &xlArray.val.str);
return static_cast<LPXLOPER12>(&xlArray);
}
__declspec(dllexport) double WINAPI GetComplexNorm(LPXLOPER12 arrayin)
{
char *handle = nullptr;
FromWChartPtrToWharPtr(arrayin->val.str, &handle);
Complex * z = (Complex*)RetrieveObject(handle);
double res = z->getNorm();
return res;
}
Complex being a classic complex numbers class, and the "memory" functions being as follows (beware it's old C code) :
#include "stdafx.h"
#include <string.h>
#include "memory.h"
#include <stdio.h>
#include "XLCALL.H"
#include <cstdlib>
void FromCharPtrToWChartPtr(char * from, XCHAR **to)
{
size_t len = strlen(from);
*to = new XCHAR[len + 1];
*to[0] = static_cast<XCHAR>(len);
if (len > 0)
{
mbstowcs(*to + 1, from, len + 1);
}
}
void FromWChartPtrToWharPtr(XCHAR * from, char **to)
{
size_t len = from[0];
*to = new char[len + 1];
wcstombs(*to, from + 1, len);
}
typedef struct _TObject
{
char *name;
int version; /* increments by 1 for every new store operation */
void *data;
void *next;
} TObject;
static TObject *cache = NULL;
#define SEPARATOR '#'
TObject* FindNode(char* name)
{
TObject *node = cache;
while (node)
{
if (_stricmp(node->name, name) == 0)
break;
node = (TObject*)node->next;
}
return node;
}
#define FAILURE -1
#define SUCCESS 0
char* StoreObject(char* name, void* data)
{
static char *routine = "StoreObject";
int status = FAILURE;
char *handle = NULL;
TObject *node;
static char buffer[255];
if (data == NULL)
{
// error to handle later
goto done;
}
if (name == NULL)
{
// error to handle later
goto done;
}
if (strlen(name) > 200)
{
// error to handle later
goto done;
}
node = FindNode(name);
if (node == NULL)
{
node = new TObject();
if (node == NULL)
goto done;
node->name = _strdup(name);
node->version = 1;
node->data = data;
node->next = cache;
cache = node;
}
else
{
node->version += 1;
delete node->data; // should I template taylor the object destruction diffenrently ?
node->data = data;
}
sprintf(buffer, "%s%c%d\0", node->name, SEPARATOR, node->version);
handle = _strdup(buffer);
if (handle == NULL)
goto done;
strcpy(handle, buffer);
status = SUCCESS;
done:
if (status != SUCCESS)
{
// error to handle later
delete handle;
handle = NULL;
}
return handle;
}
void* RetrieveObject(char* handle)
{
static char *routine = "RetrieveObject";
int status = FAILURE;
void *data = NULL;
char *name = NULL;
TObject *node;
char *sep;
if (handle == NULL)
{
// error to handle later
goto done;
}
name = _strdup(handle);
if (name == NULL)
goto done;
/* remove version number from lookup string */
sep = strchr(name, SEPARATOR);
if (sep != NULL)
*sep = '\0';
node = FindNode(name);
if (node == NULL)
{
// error to handle later
goto done;
}
data = node->data;
status = SUCCESS;
done:
if (status != SUCCESS)
{
// error to handle later
data = NULL;
}
delete name;
return data;
}
void FreeObjects()
{
TObject *next = cache;
TObject *node;
while (next)
{
node = next;
next = (TObject*)node->next;
delete node->name;
delete node->data;
delete node;
}
}
There is a good presentation on this MSN webpage, indeed static is not thread safe, you need to allocate the new variable to a thread local safe memory. You can have a look to the function get_thread_local_xloper12 in the aforementioned page.

HashMap of Function pointers (Class Member Functions)

I have to create a very BASIC hash map of Functions pointers. My requirement is just add values in it and then get it based on key. For some political reason, I can not use any standard librabry. I have a code which works fine. But if I want a functions pointers to my CLASS MEMBER FUNCTIONS then this do not work. Any suggestion what should be the modification in below code.
In this PING and REFRESH are independent functions. So this code works. But if I move these functions to HashMap class then it fails.
Code:--
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <iomanip>
using namespace std;
typedef void (*FunctionPtr)();
void ping(){
cout<<"ping";
}
void refresh(){
cout<<"refresh";
}
class HashEntry {
private:
int key;
FunctionPtr func_ptr1;;
public:
HashEntry(int key, FunctionPtr fptr) {
this->key = key;
this->func_ptr1 = fptr;
}
int getKey() {
return key;
}
FunctionPtr getValue() {
return this->func_ptr1;
}
};
const int TABLE_SIZE = 128;
class HashMap {
private:
HashEntry **table;
public:
HashMap() {
table = new HashEntry*[TABLE_SIZE];
for (int i = 0; i < TABLE_SIZE; i++)
table[i] = NULL;
}
FunctionPtr get(int key) {
int hash = (key % TABLE_SIZE);
while (table[hash] != NULL && table[hash]->getKey() != key)
hash = (hash + 1) % TABLE_SIZE;
if (table[hash] == NULL)
return NULL;
else
return table[hash]->getValue();
}
void put(int key, FunctionPtr fptr) {
int hash = (key % TABLE_SIZE);
while (table[hash] != NULL && table[hash]->getKey() != key)
hash = (hash + 1) % TABLE_SIZE;
if (table[hash] != NULL)
delete table[hash];
table[hash] = new HashEntry(key, fptr);
}
~HashMap() {
for (int i = 0; i < TABLE_SIZE; i++)
if (table[i] != NULL)
delete table[i];
delete[] table;
}
};
void main(){
HashMap* pHashsMap = new HashMap();
pHashsMap->put(1,ping);
pHashsMap->put(2,refresh);
pHashsMap->put(3,ping);
pHashsMap->put(4,refresh);
pHashsMap->put(5,ping);
pHashsMap->put(6,refresh);
cout<<" Key 1---"<<pHashsMap->get(1)<<endl;
pHashsMap->get(1)();
cout<<" Key 5---"<<pHashsMap->get(5)<<endl;
pHashsMap->get(5)();
cout<<" Key 3---"<<pHashsMap->get(3)<<endl;
pHashsMap->get(3)();
cout<<" Key 6---"<<pHashsMap->get(6)<<endl;
pHashsMap->get(6)();
delete pHashsMap;
}
The smart-alec answer: inspect the code for std::bind, learn from it, and create your own (though tbh, not using STL/boost isn't smart...).
the simpler answer: you need to create a union type to hold your normal function pointer and a class member function pointer, then store a bool to indicate if it is a class pointer:
class funcbind_t
{
union
{
void (*pf)();
void (SomeClass::*mfp)();
};
bool member;
funcbind_t(void (*_pf)()) : pf(_pf), member(false)
{
}
funcbind_t(void (SomeClass::*_mpf)()) : mpf(_mpf), member(true)
{
}
void operator ()()
{
if(member)
mfp();
else
fp();
}
};
as you can see, this is going to get messy when you start needing differing parameters to the functions.

c0000005 exception (Access violation) while using JNI in C++

I started using JNI to call Java classes from C++ some weeks ago and today I encountered a peculiar situation. I am new to C++ (familiar with Java though), so this could be a n00b error. I have this class in Java called IntArray.java and I created another class in C++ called IntArrayProxy (split in a .h and a .cpp file) in order to access its methods through JNI. I also have another source file called IntArrayProxyTest.cpp which tests the IntArrayProxy methods.
In IntArrayProxy, I use a data member jobject* intArrayObject which contains the instance of the Java class and I pass this to every method of the IntArrayProxy class. My problem is that when I use it as a pointer (jobject*), after inserting (using insert) some integers and changing some of them (using setElement), when I use the same size() method twice, the executable I create crashes giving me a c0000005 exception (Access violation).
The first thing I noticed was that there is no problem at all if I use a normal jobject (not a jobject*) and the second one was that the exception occurs when I try to call a second non-void method. insert() and setElement(int, int) are both void, so I can call them as many times as I want. I tried it with almost all the non-void methods and the same exception was thrown each time I tried to call two non-void methods.
I thought that maybe the pointer somehow changed, so I tried printing the jobject* in each method but it stayed the same. The second explanation I found in forums was that maybe the object was destroyed but I don't know how to check it and why this could happen. I spent all day searching and debugging but no luck.
I think it's irrelevant but I am using the latest (32-bit) minGW compiler on Win7(64-bit). I also use the 32-bit jvm.dll. I am using the command line to compile it (g++ -I"C:\Program Files (x86)\Java\jdk1.6.0_26\include" -I"C:\Program Files (x86)\Java\jdk1.6.0_26\include\win32" IntArrayProxy.cpp IntArrayProxyTest.cpp -L"C:\Users\jEOPARd\Desktop\Creta\JNI samples" -ljvm -o IntArrayProxyTest.exe)
Hope someone can help me!!
Thanx in advance!!
Kostis
IntArray.java
package SortIntArray;
public class IntArray {
private int[] arrayOfInt;
private int cursor;
private static final int CAPACITY = 5;
public IntArray() {
arrayOfInt = new int[CAPACITY];
cursor = 0;
}
public void insert(int n) {
if (isFull()) {
System.out.println("Inserting in a full array!");
} else {
arrayOfInt[cursor++] = n;
}
}
public int removeLast() {
if (isEmpty()) {
System.out.println("Removing from an empty array!");
return -666;
} else {
return arrayOfInt[--cursor];
}
}
private boolean isEmpty() {
return cursor <= 0;
}
private boolean isFull() {
return cursor >= CAPACITY;
}
public String toString() {
if (isEmpty()) {
return "Empty Array";
}
String s = Integer.toString(arrayOfInt[0]);
for (int i = 1; i < cursor; i++) {
s += ", " + Integer.toString(arrayOfInt[i]);
}
return s;
}
public int size() {
return cursor;
}
public int getElement(int pos) {
return arrayOfInt[pos];
}
public void setElement(int pos, int newElement) {
arrayOfInt[pos] = newElement;
}
}
IntArrayProxy.h
#ifndef INTARRAYPROXY_H
#define INTARRAYPROXY_H
#include <jni.h>
using namespace std;
class IntArrayProxy {
JNIEnv *env;
jclass intArrayClass;
jobject *intArrayObject; //giati oxi pointer?
public:
IntArrayProxy(JNIEnv*);
void insert(int n);
int removeLast();
string toString();
int size();
int getElement(int);
void setElement(int pos, int newElement);
jobject *getIntArrayObject();
};
#endif /* INTARRAYPROXY_H */
IntArrayProxy.cpp
#include <stdio.h>
#include <cstdlib>
#include <iostream>
using namespace std;
#include "IntArrayProxy.h"
IntArrayProxy::IntArrayProxy(JNIEnv *envir) {
env = envir;
intArrayClass = env -> FindClass("SortIntArray/IntArray");
if (intArrayClass == NULL) {
cout << "--intArrayClass = NULL\n";
exit(0);
}
jmethodID IntArrayConstructor = env->GetMethodID(intArrayClass, "<init>", "()V");
if (IntArrayConstructor == NULL) {
cout << "--IntArrayConstructor = NULL";
exit(0);
}
cout << "IntArrayProxy: Got constructor\n";
jobject obj = env -> NewObject(intArrayClass, IntArrayConstructor);
intArrayObject = &obj; // I also can't assign intArrayObject directly at the above line, I don't know why (would be glad if you could tell me)
if (*intArrayObject == NULL) {
cout << "--*intArrayObject = NULL";
exit(0);
}
cout << "IntArrayProxy: Object created\n";
}
void IntArrayProxy::insert(int n) {
jmethodID insertID = env -> GetMethodID(intArrayClass, "insert", "(I)V");
if (insertID == NULL) {
cout << "--insertID = NULL";
exit(0);
}
env -> CallVoidMethod(*intArrayObject, insertID, (jint) n);
}
int IntArrayProxy::removeLast() {
jmethodID removeLastID = env -> GetMethodID(intArrayClass, "removeLast", "()I");
if (removeLastID == NULL) {
cout << "--removeLastID = NULL";
exit(0);
}
return (int) (env -> CallIntMethod(*intArrayObject, removeLastID));
}
string IntArrayProxy::toString() {
jmethodID toStringID = env -> GetMethodID(intArrayClass, "toString", "()Ljava/lang/String;");
if (toStringID == NULL) {
cout << "--toStringID = NULL";
exit(0);
}
jstring intArrayString = (jstring) env -> CallObjectMethod(*intArrayObject, toStringID);
string s = env -> GetStringUTFChars(intArrayString, NULL);
return s;
}
int IntArrayProxy::size(){
jmethodID sizeID = env -> GetMethodID(intArrayClass, "size", "()I");
if (sizeID == NULL) {
cout << "--sizeID = NULL";
exit(0);
}
return (int) (env -> CallIntMethod(*intArrayObject, sizeID));
}
int IntArrayProxy::getElement(int pos) {
jmethodID getElementID = env -> GetMethodID(intArrayClass, "getElement", "(I)I");
if (getElementID == NULL) {
cout << "--getElementID = NULL";
exit(0);
}
return (int) env -> CallObjectMethod(*intArrayObject, getElementID, (jint) pos);
}
void IntArrayProxy::setElement(int pos, int newElement){
jmethodID setElementID = env -> GetMethodID(intArrayClass, "setElement", "(II)V");
if (setElementID == NULL) {
cout << "--setElementID = NULL";
exit(0);
}
env -> CallVoidMethod(*intArrayObject, setElementID, (jint) pos, (jint) newElement);
}
jobject *IntArrayProxy::getIntArrayObject(){
return intArrayObject;
}
IntArrayProxyTest.cpp
#include <stdio.h>
#include <jni.h>
#include <cstdlib>
#include <iostream>
using namespace std;
#include "IntArrayProxy.h"
int main() {
cout << "--Starting..\n";
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=C:\\Users\\jEOPARd\\Desktop\\Creta\\JNI samples\\JNI tests\\build\\classes";
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 */
cout << "--Creating VM..\n";
JNI_CreateJavaVM(&jvm, (void **) &env, &vm_args);
cout << "--VM created successfully!!\n";
delete options;
cout << "--Finding IntArray class..\n";
IntArrayProxy *intArrayProxy = new IntArrayProxy(env);
if (env->ExceptionOccurred())
env->ExceptionDescribe();
intArrayProxy -> insert(1);
intArrayProxy -> insert(10);
intArrayProxy -> insert(3);
intArrayProxy -> insert(88);
intArrayProxy -> insert(32);
intArrayProxy ->setElement(2, 5);
intArrayProxy ->setElement(3, 7);
cout << "Size: " << intArrayProxy -> size() << endl;
cout << "Size: " << intArrayProxy -> size() << endl;
cout << "--Destroying VM..\n";
jvm->DestroyJavaVM();
cout << "--Done!!!\n";
return 0;
}
In the proxy constructor:
intArrayObject = &obj;
You're taking the address of a variable on the stack. When the constructor exits, the address is no longer valid, hence the crash.
The intArrayObject (in the header) should be a jobject, not a jobject*, and the various uses of it should be changed accordingly.

"ambiguous symbol error" while building a C++/Tcl program using boost library

I have gone through the guides given out here & http://cpptcl.sourceforge.net/doc/compiling.html Hence I have pretty much done the basic stuffs.
Here's the code for Hello.cc given in the guide present in C++/Tcl:
#include "cpptcl.h"
#include <iostream>
using namespace std;
void hello()
{
cout << "Hello C++/Tcl!" << endl;
}
CPPTCL_MODULE(Mymodule, i)
{
i.def("hello", hello);
}
According to the guide, we need cpptcl.h & cpptcl.cc & few other files in details/ directory which I have included.
While building the code, it's giving me the following error:
1>Compiling...
1>cpptcl.cc
cpptcl.cc(35) : warning C4800: 'int' : forcing value to bool 'true' or 'false' (performance warning)
cpptcl.cc(325) : error C2039: 'what' : is not a member of 'boost::exception'
1> c:\program files\boost\boost_1_44_0\boost\exception\exception.hpp(194) : see declaration of 'boost::exception'
1>cpptcl.cc(366) : error C2872: 'exception' : ambiguous symbol
1> could be 'c:\program files\boost\boost_1_44_0\boost\exception\exception.hpp(194) : boost::exception'
1> or 'c:\program files\microsoft visual studio 8\vc\include\exception(88) : std::exception'
1>cpptcl.cc(368) : error C2039: 'what' : is not a member of 'boost::exception'
1>c:\program files\boost\boost_1_44_0\boost\exception\exception.hpp(194) : see declaration of 'boost::exception'
1>3 error(s), 2 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
It's not able to resolve exception symbol. I am not sure whether the exception.hpp or the std::exception.
Below is the code for cpptcl.cc
//
// Copyright (C) 2004-2005, Maciej Sobczak
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
#include "cpptcl.h"
#include
#include
#include
#include
using namespace Tcl;
using namespace Tcl::details;
using namespace std;
using namespace boost;
result::result(Tcl_Interp *interp) : interp_(interp) {}
result::operator bool() const
{
Tcl_Obj *obj = Tcl_GetObjResult(interp_);
int val, cc;
cc = Tcl_GetBooleanFromObj(interp_, obj, &val);
if (cc != TCL_OK)
{
throw tcl_error(interp_);
}
return static_cast(val);
}
result::operator double() const
{
Tcl_Obj *obj = Tcl_GetObjResult(interp_);
double val;
int cc = Tcl_GetDoubleFromObj(interp_, obj, &val);
if (cc != TCL_OK)
{
throw tcl_error(interp_);
}
return val;
}
result::operator int() const
{
Tcl_Obj *obj = Tcl_GetObjResult(interp_);
int val, cc;
cc = Tcl_GetIntFromObj(interp_, obj, &val);
if (cc != TCL_OK)
{
throw tcl_error(interp_);
}
return val;
}
result::operator long() const
{
Tcl_Obj *obj = Tcl_GetObjResult(interp_);
long val;
int cc;
cc = Tcl_GetLongFromObj(interp_, obj, &val);
if (cc != TCL_OK)
{
throw tcl_error(interp_);
}
return val;
}
result::operator string() const
{
Tcl_Obj *obj = Tcl_GetObjResult(interp_);
return Tcl_GetString(obj);
}
result::operator object() const
{
return object(Tcl_GetObjResult(interp_));
}
void details::set_result(Tcl_Interp *interp, bool b)
{
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(b));
}
void details::set_result(Tcl_Interp *interp, int i)
{
Tcl_SetObjResult(interp, Tcl_NewIntObj(i));
}
void details::set_result(Tcl_Interp *interp, long i)
{
Tcl_SetObjResult(interp, Tcl_NewLongObj(i));
}
void details::set_result(Tcl_Interp *interp, double d)
{
Tcl_SetObjResult(interp, Tcl_NewDoubleObj(d));
}
void details::set_result(Tcl_Interp *interp, string const &s)
{
Tcl_SetObjResult(interp,
Tcl_NewStringObj(s.data(), static_cast(s.size())));
}
void details::set_result(Tcl_Interp *interp, void *p)
{
ostringstream ss;
ss (s.size())));
}
void details::set_result(Tcl_Interp *interp, object const &o)
{
Tcl_SetObjResult(interp, o.get_object());
}
void details::check_params_no(int objc, int required)
{
if (objc > callback_interp_map;
typedef map callback_map;
callback_map callbacks;
callback_map constructors;
// map of call policies
typedef map policies_interp_map;
typedef map policies_map;
policies_map call_policies;
// map of object handlers
typedef map > class_interp_map;
typedef map class_handlers_map;
class_handlers_map class_handlers;
// helper for finding call policies - returns true when found
bool find_policies(Tcl_Interp *interp, string const &cmdName,
policies_interp_map::iterator &piti)
{
policies_map::iterator pit = call_policies.find(interp);
if (pit == call_policies.end())
{
return false;
}
piti = pit->second.find(cmdName);
return piti != pit->second.end();
}
extern "C"
int object_handler(ClientData cd, Tcl_Interp *interp,
int objc, Tcl_Obj * CONST objv[]);
// helper function for post-processing call policies
// for both free functions (isMethod == false)
// and class methods (isMethod == true)
void post_process_policies(Tcl_Interp *interp, policies &pol,
Tcl_Obj * CONST objv[], bool isMethod)
{
// check if it is a factory
if (!pol.factory_.empty())
{
class_handlers_map::iterator it = class_handlers.find(interp);
if (it == class_handlers.end())
{
throw tcl_error(
"Factory was registered for unknown class.");
}
class_interp_map::iterator oit = it->second.find(pol.factory_);
if (oit == it->second.end())
{
throw tcl_error(
"Factory was registered for unknown class.");
}
class_handler_base *chb = oit->second.get();
// register a new command for the object returned
// by this factory function
// if everything went OK, the result is the address of the
// new object in the 'pXXX' form
// - the new command will be created with this name
Tcl_CreateObjCommand(interp,
Tcl_GetString(Tcl_GetObjResult(interp)),
object_handler, static_cast(chb), 0);
}
// process all declared sinks
// - unregister all object commands that envelopes the pointers
for (vector::iterator s = pol.sinks_.begin();
s != pol.sinks_.end(); ++s)
{
if (isMethod == false)
{
// example: if there is a declared sink at parameter 3,
// and the Tcl command was:
// % fun par1 par2 PAR3 par4
// then the index 3 correctly points into the objv array
int index = *s;
Tcl_DeleteCommand(interp, Tcl_GetString(objv[index]));
}
else
{
// example: if there is a declared sink at parameter 3,
// and the Tcl command was:
// % $p method par1 par2 PAR3 par4
// then the index 3 needs to be incremented
// in order correctly point into the 4th index of objv array
int index = *s + 1;
Tcl_DeleteCommand(interp, Tcl_GetString(objv[index]));
}
}
}
// actual functions handling various callbacks
// generic callback handler
extern "C"
int callback_handler(ClientData cd, Tcl_Interp *interp,
int objc, Tcl_Obj * CONST objv[])
{
callback_map::iterator it = callbacks.find(interp);
if (it == callbacks.end())
{
Tcl_SetResult(interp,
"Trying to invoke non-existent callback (wrong interpreter?)",
TCL_STATIC);
return TCL_ERROR;
}
string cmdName(Tcl_GetString(objv[0]));
callback_interp_map::iterator iti = it->second.find(cmdName);
if (iti == it->second.end())
{
Tcl_SetResult(interp,
"Trying to invoke non-existent callback (wrong cmd name?)",
TCL_STATIC);
return TCL_ERROR;
}
policies_map::iterator pit = call_policies.find(interp);
if (pit == call_policies.end())
{
Tcl_SetResult(interp,
"Trying to invoke callback with no known policies",
TCL_STATIC);
return TCL_ERROR;
}
policies_interp_map::iterator piti;
if (find_policies(interp, cmdName, piti) == false)
{
Tcl_SetResult(interp,
"Trying to invoke callback with no known policies",
TCL_STATIC);
return TCL_ERROR;
}
policies &pol = piti->second;
try
{
iti->second->invoke(interp, objc, objv, pol);
post_process_policies(interp, pol, objv, false);
}
catch (boost::exception const &e)
{
Tcl_SetResult(interp, const_cast(e.what()), TCL_VOLATILE);
return TCL_ERROR;
}
catch (...)
{
Tcl_SetResult(interp, "Unknown error.", TCL_STATIC);
return TCL_ERROR;
}
return TCL_OK;
}
// generic "object" command handler
extern "C"
int object_handler(ClientData cd, Tcl_Interp *interp,
int objc, Tcl_Obj * CONST objv[])
{
// here, client data points to the singleton object
// which is responsible for managing commands for
// objects of a given type
class_handler_base *chb = reinterpret_cast(cd);
// the command name has the form 'pXXX' where XXX is the address
// of the "this" object
string const str(Tcl_GetString(objv[0]));
istringstream ss(str);
char dummy;
void *p;
ss >> dummy >> p;
try
{
string methodName(Tcl_GetString(objv[1]));
policies &pol = chb->get_policies(methodName);
chb->invoke(p, interp, objc, objv, pol);
post_process_policies(interp, pol, objv, true);
}
catch (exception const &e)
{
Tcl_SetResult(interp, const_cast(e.what()), TCL_VOLATILE);
return TCL_ERROR;
}
catch (...)
{
Tcl_SetResult(interp, "Unknown error.", TCL_STATIC);
return TCL_ERROR;
}
return TCL_OK;
}
// generic "constructor" command
extern "C"
int constructor_handler(ClientData cd, Tcl_Interp *interp,
int objc, Tcl_Obj * CONST objv[])
{
// here, client data points to the singleton object
// which is responsible for managing commands for
// objects of a given type
class_handler_base *chb = reinterpret_cast(cd);
callback_map::iterator it = constructors.find(interp);
if (it == constructors.end())
{
Tcl_SetResult(interp,
"Trying to invoke non-existent callback (wrong interpreter?)",
TCL_STATIC);
return TCL_ERROR;
}
string className(Tcl_GetString(objv[0]));
callback_interp_map::iterator iti = it->second.find(className);
if (iti == it->second.end())
{
Tcl_SetResult(interp,
"Trying to invoke non-existent callback (wrong class name?)",
TCL_STATIC);
return TCL_ERROR;
}
policies_interp_map::iterator piti;
if (find_policies(interp, className, piti) == false)
{
Tcl_SetResult(interp,
"Trying to invoke callback with no known policies",
TCL_STATIC);
return TCL_ERROR;
}
policies &pol = piti->second;
try
{
iti->second->invoke(interp, objc, objv, pol);
// if everything went OK, the result is the address of the
// new object in the 'pXXX' form
// - we can create a new command with this name
Tcl_CreateObjCommand(interp,
Tcl_GetString(Tcl_GetObjResult(interp)),
object_handler, static_cast(chb), 0);
}
catch (std::exception const &e)
{
Tcl_SetResult(interp, const_cast(e.what()), TCL_VOLATILE);
return TCL_ERROR;
}
catch (...)
{
Tcl_SetResult(interp, "Unknown error.", TCL_STATIC);
return TCL_ERROR;
}
return TCL_OK;
}
} // namespace anonymous
Tcl::details::no_init_type Tcl::no_init;
policies & policies::factory(string const &name)
{
factory_ = name;
return *this;
}
policies & policies::sink(int index)
{
sinks_.push_back(index);
return *this;
}
policies & policies::variadic()
{
variadic_ = true;
return *this;
}
policies Tcl::factory(string const &name)
{
return policies().factory(name);
}
policies Tcl::sink(int index)
{
return policies().sink(index);
}
policies Tcl::variadic()
{
return policies().variadic();
}
class_handler_base::class_handler_base()
{
// default policies for the -delete command
policies_["-delete"] = policies();
}
void class_handler_base::register_method(string const &name,
shared_ptr ocb, policies const &p)
{
methods_[name] = ocb;
policies_[name] = p;
}
policies & class_handler_base::get_policies(string const &name)
{
policies_map_type::iterator it = policies_.find(name);
if (it == policies_.end())
{
throw tcl_error("Trying to use non-existent policy: " + name);
}
return it->second;
}
object::object()
: interp_(0)
{
obj_ = Tcl_NewObj();
Tcl_IncrRefCount(obj_);
}
object::object(bool b)
: interp_(0)
{
obj_ = Tcl_NewBooleanObj(b);
Tcl_IncrRefCount(obj_);
}
object::object(char const *buf, size_t size)
: interp_(0)
{
obj_ = Tcl_NewByteArrayObj(
reinterpret_cast(buf),
static_cast(size));
Tcl_IncrRefCount(obj_);
}
object::object(double d)
: interp_(0)
{
obj_ = Tcl_NewDoubleObj(d);
Tcl_IncrRefCount(obj_);
}
object::object(int i)
: interp_(0)
{
obj_ = Tcl_NewIntObj(i);
Tcl_IncrRefCount(obj_);
}
object::object(long l)
: interp_(0)
{
obj_ = Tcl_NewLongObj(l);
Tcl_IncrRefCount(obj_);
}
object::object(char const *s)
: interp_(0)
{
obj_ = Tcl_NewStringObj(s, -1);
Tcl_IncrRefCount(obj_);
}
object::object(string const &s)
: interp_(0)
{
obj_ = Tcl_NewStringObj(s.data(), static_cast(s.size()));
Tcl_IncrRefCount(obj_);
}
object::object(Tcl_Obj *o, bool shared)
: interp_(0)
{
init(o, shared);
}
object::object(object const &other, bool shared)
: interp_(other.get_interp())
{
init(other.obj_, shared);
}
void object::init(Tcl_Obj *o, bool shared)
{
if (shared)
{
obj_ = o;
}
else
{
obj_ = Tcl_DuplicateObj(o);
}
Tcl_IncrRefCount(obj_);
}
object::~object()
{
Tcl_DecrRefCount(obj_);
}
object & object::assign(bool b)
{
Tcl_SetBooleanObj(obj_, b);
return *this;
}
object & object::resize(size_t size)
{
Tcl_SetByteArrayLength(obj_, static_cast(size));
return *this;
}
object & object::assign(char const *buf, size_t size)
{
Tcl_SetByteArrayObj(obj_,
reinterpret_cast(buf),
static_cast(size));
return *this;
}
object & object::assign(double d)
{
Tcl_SetDoubleObj(obj_, d);
return *this;
}
object & object::assign(int i)
{
Tcl_SetIntObj(obj_, i);
return *this;
}
object & object::assign(long l)
{
Tcl_SetLongObj(obj_, l);
return *this;
}
object & object::assign(char const *s)
{
Tcl_SetStringObj(obj_, s, -1);
return *this;
}
object & object::assign(string const &s)
{
Tcl_SetStringObj(obj_, s.data(), static_cast(s.size()));
return *this;
}
object & object::assign(object const &other)
{
object(other).swap(*this);
return *this;
}
object & object::assign(Tcl_Obj *o)
{
object(o).swap(*this);
return *this;
}
object & object::swap(object &other)
{
std::swap(obj_, other.obj_);
std::swap(interp_, other.interp_);
return *this;
}
template
bool object::get(interpreter &i) const
{
int retVal;
int res = Tcl_GetBooleanFromObj(i.get(), obj_, &retVal);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
return static_cast(retVal);
}
template
vector object::get >(interpreter &) const
{
size_t size;
char const *buf = get(size);
return vector(buf, buf + size);
}
template
double object::get(interpreter &i) const
{
double retVal;
int res = Tcl_GetDoubleFromObj(i.get(), obj_, &retVal);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
return retVal;
}
template
int object::get(interpreter &i) const
{
int retVal;
int res = Tcl_GetIntFromObj(i.get(), obj_, &retVal);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
return retVal;
}
template
long object::get(interpreter &i) const
{
long retVal;
int res = Tcl_GetLongFromObj(i.get(), obj_, &retVal);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
return retVal;
}
template
char const * object::get(interpreter &) const
{
return get();
}
template
string object::get(interpreter &) const
{
int len;
char const *buf = Tcl_GetStringFromObj(obj_, &len);
return string(buf, buf + len);
}
char const * object::get() const
{
return Tcl_GetString(obj_);
}
char const * object::get(size_t &size) const
{
int len;
unsigned char *buf = Tcl_GetByteArrayFromObj(obj_, &len);
size = len;
return const_cast(reinterpret_cast(buf));
}
size_t object::length(interpreter &i) const
{
int len;
int res = Tcl_ListObjLength(i.get(), obj_, &len);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
return static_cast(len);
}
object object::at(interpreter &i, size_t index) const
{
Tcl_Obj *o;
int res = Tcl_ListObjIndex(i.get(), obj_, static_cast(index), &o);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
if (o == NULL)
{
throw tcl_error("Index out of range.");
}
return object(o);
}
object & object::append(interpreter &i, object const &o)
{
int res = Tcl_ListObjAppendElement(i.get(), obj_, o.obj_);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
return *this;
}
object & object::append_list(interpreter &i, object const &o)
{
int res = Tcl_ListObjAppendList(i.get(), obj_, o.obj_);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
return *this;
}
object & object::replace(interpreter &i, size_t index, size_t count,
object const &o)
{
int res = Tcl_ListObjReplace(i.get(), obj_,
static_cast(index), static_cast(count),
1, &(o.obj_));
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
return *this;
}
object & object::replace_list(interpreter &i, size_t index, size_t count,
object const &o)
{
int objc;
Tcl_Obj **objv;
int res = Tcl_ListObjGetElements(i.get(), obj_, &objc, &objv);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
res = Tcl_ListObjReplace(i.get(), obj_,
static_cast(index), static_cast(count),
objc, objv);
if (res != TCL_OK)
{
throw tcl_error(i.get());
}
return *this;
}
void object::set_interp(Tcl_Interp *interp)
{
interp_ = interp;
}
Tcl_Interp * object::get_interp() const
{
return interp_;
}
interpreter::interpreter()
{
interp_ = Tcl_CreateInterp();
}
interpreter::interpreter(Tcl_Interp *interp, bool owner)
{
interp_ = interp;
owner_ = owner;
}
interpreter::~interpreter()
{
if (owner_)
{
// clear all callback info belonging to this interpreter
clear_definitions(interp_);
Tcl_DeleteInterp(interp_);
}
}
void interpreter::make_safe()
{
int cc = Tcl_MakeSafe(interp_);
if (cc != TCL_OK)
{
throw tcl_error(interp_);
}
}
result interpreter::eval(string const &script)
{
int cc = Tcl_Eval(interp_, script.c_str());
if (cc != TCL_OK)
{
throw tcl_error(interp_);
}
return result(interp_);
}
result interpreter::eval(istream &s)
{
string str(
istreambuf_iterator(s.rdbuf()),
istreambuf_iterator()
);
return eval(str);
}
result interpreter::eval(object const &o)
{
int cc = Tcl_EvalObjEx(interp_, o.get_object(), 0);
if (cc != TCL_OK)
{
throw tcl_error(interp_);
}
return result(interp_);
}
void interpreter::pkg_provide(string const &name, string const &version)
{
int cc = Tcl_PkgProvide(interp_, name.c_str(), version.c_str());
if (cc != TCL_OK)
{
throw tcl_error(interp_);
}
}
void interpreter::create_alias(string const &cmd,
interpreter &targetInterp, string const &targetCmd)
{
int cc = Tcl_CreateAlias(interp_, cmd.c_str(),
targetInterp.interp_, targetCmd.c_str(), 0, 0);
if (cc != TCL_OK)
{
throw tcl_error(interp_);
}
}
void interpreter::clear_definitions(Tcl_Interp *interp)
{
// delete all callbacks that were registered for given interpreter
{
callback_map::iterator it = callbacks.find(interp);
if (it == callbacks.end())
{
// no callbacks defined for this interpreter
return;
}
callback_interp_map &imap = it->second;
for (callback_interp_map::iterator it2 = imap.begin();
it2 != imap.end(); ++it2)
{
Tcl_DeleteCommand(interp, it2->first.c_str());
}
callbacks.erase(interp);
}
// delete all constructors
{
callback_map::iterator it = constructors.find(interp);
if (it == constructors.end())
{
// no callbacks defined for this interpreter
return;
}
callback_interp_map &imap = it->second;
for (callback_interp_map::iterator it2 = imap.begin();
it2 != imap.end(); ++it2)
{
Tcl_DeleteCommand(interp, it2->first.c_str());
}
callbacks.erase(interp);
}
// delete all call policies
call_policies.erase(interp);
// delete all object handlers
// (we have to assume that all living objects were destroyed,
// otherwise Bad Things will happen)
class_handlers.erase(interp);
}
void interpreter::add_function(string const &name,
shared_ptr cb, policies const &p)
{
Tcl_CreateObjCommand(interp_, name.c_str(),
callback_handler, 0, 0);
callbacks[interp_][name] = cb;
call_policies[interp_][name] = p;
}
void interpreter::add_class(string const &name,
shared_ptr chb)
{
class_handlers[interp_][name] = chb;
}
void interpreter::add_constructor(string const &name,
shared_ptr chb, shared_ptr cb,
policies const &p)
{
Tcl_CreateObjCommand(interp_, name.c_str(),
constructor_handler, static_cast(chb.get()), 0);
constructors[interp_][name] = cb;
call_policies[interp_][name] = p;
}
int tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj)
{
int res;
int cc = Tcl_GetIntFromObj(interp, obj, &res);
if (cc != TCL_OK)
{
throw tcl_error(interp);
}
return res;
}
long tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj)
{
long res;
int cc = Tcl_GetLongFromObj(interp, obj, &res);
if (cc != TCL_OK)
{
throw tcl_error(interp);
}
return res;
}
bool tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj)
{
int res;
int cc = Tcl_GetBooleanFromObj(interp, obj, &res);
if (cc != TCL_OK)
{
throw tcl_error(interp);
}
return res != 0;
}
double tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj)
{
double res;
int cc = Tcl_GetDoubleFromObj(interp, obj, &res);
if (cc != TCL_OK)
{
throw tcl_error(interp);
}
return res;
}
string tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj)
{
return Tcl_GetString(obj);
}
char const * tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj)
{
return Tcl_GetString(obj);
}
object tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj)
{
object o(obj);
o.set_interp(interp);
return o;
}
How do I resolve this?
Thanks
The code seems to refer to the regular std::exception, but the symbol gets mixed because of using both the boost and std namespaces. Maybe it's some difference between VC and gcc headers.
In any case, adding the following lines to the file solves this compiler error and another similar conflict with shared_ptr in VC2010 (at least for me):
#define shared_ptr boost::shared_ptr
#define exception std::exception
Regards,
Miguel
A project has been started to fix the problems in this library here: https://github.com/wsong83/cpptcl
I notice cpptcl.cc has the following:
try
{
iti->second->invoke(interp, objc, objv, pol);
post_process_policies(interp, pol, objv, false);
}
catch (boost::exception const &e)
{
Tcl_SetResult(interp, const_cast(e.what()), TCL_VOLATILE);
return TCL_ERROR;
}
The compiler complains that boost::exceptiondoesn't have what() (which indeed it doesn't since that's in std::exception). So you have to change this to use get_error_info().
Also, I notice that the const_cast isn't taking a template argument. This is going to be problematic.