使用JNI和Reflect实现Object向void*的自动转换之五:测试 [原]
来源:互联网 发布:两院院士 知乎 编辑:程序博客网 时间:2024/06/07 08:38
五、测试:享受和更好的享受
好了,完成了这个包装,我们可以渐渐大胆的对这个设备进行测试了。
测试一:
首先拿个Integer来做测试。这个类的成员中,很多静态变量,但是核心的值是一个整型,定义形如:private int value。
根据TDD的思想,先写JUnit的测试案例:
public static void testInteger(){
Integer i = new Integer(1);
TestObjectPtrEntry.plusOneInteger(i);//调用本地方法
assertEquals("1+1应该是2:",2,i.intValue());
}
其中的本地方法函数为:
native static void plusOneInteger(Object i);
对应的C++的实现:
void JNICALL Java_org_JAPI_test_TestObjectPtrEntry_plusOneInteger(JNIEnv * env, jclass that, jobject integer)
{
ObjectPtr* op = new ObjectPtr(env,integer);
//do change: i+=1
int* pi = (int*)op->getBuffer();
*pi += 1;
op->updateObject();
delete op;
}
OK,Green Bar! Junit的绿条出现,说明我们的测试通过,也标志着取得局部的胜利。
测试二:
我们混合int和double,试图构造一个类似如下的java和C的对应关系:
class IntDouble{//in java
double d;
int i;
}
对应的C结构体为:
typedef struct tagIntDouble{
double d;
int i;
}IntDouble;
测试用例为:
public static void testIntDoubleStruct(){
IntDouble id = new IntDouble();
id.d = 10.0;
id.i = 20;
TestObjectPtrEntry.exchangeIntDouble(id);
assertEquals("after exchange,i = 10",10,id.i);
assertTrue("after exchange,d = 20.0",Math.abs(20.0 - id.d) < 0.001);
}
对应的C++实现为:
void JNICALL Java_org_JAPI_test_TestObjectPtrEntry_exchangeIntDouble(JNIEnv * env, jclass that, jobject id)
{
int isz = sizeof(int);
int dsz = sizeof(double);
int sz = sizeof(IntDouble);
//assert(sz == 12);//pls set byte 1 as the struct align
ObjectPtr* op = new ObjectPtr(env,id);
IntDouble* pid = (IntDouble*)op->getBuffer();
//do change:exchange int and double
double d = pid->d;
pid->d = pid->i;
pid->i = d;
op->updateObject();
delete op;
}
很遗憾,运行测试用例没有成功,发现double域不正确,查找原因,发现Sizeof(IntDouble)的值为16,而不是期望的4+8=12,原来c的结构体对齐出了问题。好,在C的编译器中设置对齐属性为1byte即可。重新编译连接,运行测试,ok,pass!
联想一下,我们发现很多的Windows的API接口,要求传递的结构体,都是有讲究的,使用那些结构体,就不需要设定对齐属性。一些结构体中,故意设定一些保留的数据成员(reserved)就是这个目的。许多结构体第一个域要求添入结构体的大小,也有这方面的原因。
测试三:
再往下测试,使用字符串+数组测试,由于java的String类型的内部实现并不是一个字符串,所以还是不要用它直接测试了,我们可以构造一个char[]来测试。如下:
public static void testString(){
String str = "hello";
char[] wchStr = str.toCharArray();
TestObjectPtrEntry.converseString(wchStr);
assertEquals("after converse hello is olleh", "olleh" ,new String(wchStr));
}
本地方法声明为:
native static void converseString(Object str)
在C++的本地方法实现被写成:
JNIEXPORT void JNICALL Java_org_JAPI_test_TestObjectPtrEntry_converseString(JNIEnv *env, jclass that, jobject str){
ObjectPtr* op = new ObjectPtr(env,str);
//as unicode char
wchar_t *data = (wchar_t*)op->getBuffer();//注意到我们要使用宽字符串来操作传来的数组。
int num = op->getMemberNum();
for(int i = 0; i < num/2; i++){
wchar_t temp = data[i];
data[i] = data[num-i-1];
data[num-i-1] = temp;
}
op->updateObject();
delete op;
}
必然通过,我们越来越有信心了。Green means success!
测试四:
我们要让数据成员是object类型。这是最复杂的情况。
Java中构造的数据类型为:
class IntObject{
int i;
Integer obj;
}
期望的C结构为:
typedef struct tagInt{
int io;
}Integer;
typedef struct tagIntInteger{
int i;
Integer* pInt;//same as void* accord to java/lang/Object
}IntInteger;
我们的测试案例为:
public static void testIntObjStruct(){
IntObject io = new IntObject();
io.i = 10;
io.obj = new Integer(io.i*2);
TestObjectPtrEntry.exchangeIntInteger(io);
assertEquals("after exchange,i = 10",20,io.i);
assertEquals("after exchange,integer = 20",10,io.obj.intValue());
}
本地方法被实现为:
void JNICALL Java_org_JAPI_test_TestObjectPtrEntry_exchangeIntInteger(JNIEnv * env, jclass that, jobject id)
{
ObjectPtr* op = new ObjectPtr(env,id);
IntInteger* pid = (IntInteger*)op->getBuffer();
//exchange int and object i
int i = pid->i;
pid->i = pid->pInt->io;
pid->pInt->io = i;
op->updateObject();
delete op;
}
测试又一次成功,也是对我们工作的充分肯定。看来我们做的工作是有意义的。
下一步,就可以包装sendMessage这种类型的函数了。比如下面的一个对PostMessage的实现:
JNIEXPORT jint JNICALL Java_org_JAPI_WindowAPI_postMessage(JNIEnv *env, jobject window, jint msg, jobject lObject, jobject wObject){
HWND hWnd = Java_getHandle(env,window);
if (hWnd == NULL){
Java_Throw_IllegalArgumentException(env,NOT_SET_HANDLE_MESSAGE);
return NULL;
}
ObjectPtr* lob = NULL;
if(lObject != NULL){
lob = new ObjectPtr(env,lObject);
}
ObjectPtr* wob = NULL;
if(wObject != NULL){
wob = new ObjectPtr(env,wObject);
}
LRESULT lRes = 0;
if(lObject == NULL && wObject == NULL)
lRes = ::PostMessage(hWnd,msg,NULL,NULL);
if(lObject == NULL && wObject != NULL)
lRes = ::PostMessage(hWnd,msg,NULL,(WPARAM)wob->getBuffer());
else if(wObject == NULL && lObject != NULL)
lRes = ::PostMessage(hWnd,msg,(LPARAM)lob->getBuffer(),NULL);
else
lRes = ::PostMessage(hWnd,msg,(LPARAM)lob->getBuffer(),(WPARAM)wob->getBuffer());
//update object
if(lob != NULL){
lob->updateObject();
delete lob;
}
if(wob != NULL){
wob->updateObject();
delete wob;
}
return lRes; //交给上层解析
}
好的,在包装了这些关键的函数后,我们发现使用java来进行Windows系统编程是如此的方便,高效,当然,不要忘了参考MSDN的协议定义,这是最关键的。
六、提高和构想
有人会提出,这样包装是不是对C语言的运行速度优势的损害,我认为这点不用过分担心,在系统编程中,速度一般来说并不是主要的。
也可以在java层上实现类似的包装,不过访问private成员就成了问题。
对于Windows的系统API的包装可以更加的自动化,规模更大,如果借助了LoadLibrary和GetProcessAddress函数,可以动态的查找函数,不过参数要定义好了。这种包装,会更有挑战性,当然更加的自动。Windows编程部分中C++和Java所存在的指针上的鸿沟,会更加方便的化解。实现一个“JMFC”,也不是难事。使用Java来操作Windows系统强大的界面,更是可望且可即的。
需要本文源码,可联系作者E-mail: hhhust@gmail.com。
- 使用JNI和Reflect实现Object向void*的自动转换之五:测试 [原]
- 使用JNI和Reflect实现Object向void*的自动转换之四:实现 [原]
- 使用JNI和Reflect实现Object向void*的自动转换之二:构想 [原]
- 使用JNI和Reflect实现Object向void*的自动转换之三:学习 [原]
- 使用JNI和Reflect实现Object向void*的自动转换之一:问题 [原]
- jni object的使用
- Selenium使用Page Object实现页面自动测试
- 关于字符和数字类型的索引,Oracle如何实现内部自动转换以及索引使用的验证测试
- syslog和getopt_long_only的使用和void *指针强制转换
- OpenCV向JNI层的参数转换
- void和void指针解析(原)
- 在reflect基础上的map和object转化
- C++中任意类型向void的强制转换
- reflect的一个使用的例子---------------自动赋值
- void的使用和规则
- 一步步学习java并发编程模式之Active Object模式(五) 使用JDK的内置实现
- java JSON使用之JSONObject、JSONArray与Object的转换
- XML,Object,Json转换之浅析Xstream的使用
- 我的Visual C++入门之路(转载)
- java面试题目
- 使用JNI和Reflect实现Object向void*的自动转换之四:实现 [原]
- 第一次用我的新机子上网
- 应用可扩展性实践之路(一) --纵向拦截和横行拦截
- 使用JNI和Reflect实现Object向void*的自动转换之五:测试 [原]
- 详细解说 STL 排序(Sort)
- 如何学习vc++(vc的用处)
- 利用正则表达式替换功能,将C++注释转变成纯C的注释
- [翻译]-Windows CE 程序设计 (3rd 版)--5.1 公共控件编程
- installshield
- SQL语句导入导出大全
- Java 理论与实践: 线程池与工作队列
- CreateProcess