中间件实验三:跨语言调用和编程
一、前言
实验内容
一个功能A,用的是L1语言进行编程实现的;请把该功能,在L2语言的环境下进行调用/合并,并能正确的返回结果。
请先自己编写或找到实现A功能的代码,或仅有可执行文件,并进行跨语言开发。
多语言开发一般基于第三方的库或解决方案。
A:数据压缩和解压缩功能, L1: C++ , L2:Python 和 Java
使用语言
Python、Java
开发环境
Python 3.8
IDEA 2020.1.4
Visual Studio 2019
其中,Python调用C++通过SWIG实现,而Java调用C++通过JNI实现。
二、准备工作
1、安装SWIG(Simplified Wrapper and Interface Generator)
1、 下载Swig for Windows:http://www.swig.org/download.html
2、 解压 .zip 文件到目录
3、 添加swig所在目录到环境变量path
4、 简单测试安装是否成功:
打开Dos,在命令行执行: swig --help, 显示 Target Language Options即表明安装成功。
2、使用C++实现数据压缩和解压缩功能
基于哈夫曼编码实现文件的压缩与解压缩
源文件保存为
HuffmanEncoderCompress.h
、HuffmanEncoderCompress.cpp
三、实验步骤
1、Python调用C++
1.1、SWIG实现python对C++封装
编写swig定义文件
.i文件主要包含了三个部分:
%module
后面的名字是被封装的模块名称,Python通过这个名称加载程序。%{...%}
之间所添加的内容,一般包含此文件需要的一些函数声明和头文件。- **最后一部分,声明了要封装的函数和变量。**如果把要封装的函数声明部分写在了头文件里,最后一部分直接用
%include
包含头文件名也行。
此处HuffmanEncoderCompress.h
调用了std::string库,所以需要%include "std::string.i"
The SWIG library
std_string.i
has language specific code for mapping the c++ string to the target languages string class. Adding%include "std_string.i"
before the code that generates your class should fix the error.Note that
%include
is different from#include
in a swig interface file.
通过命令行运行
swig python -c++ huffman.i
这样会创建两个不同的文件,huffman_wrap.cxx
与Huffman.py
1.2、使用python.distutils
生成模块动态库
编写配置文件setup.py
# File: setup.py
from distutils.core import setup, Extension
# 生成一个扩展模块
hfm_module = Extension('_Huffman', # 模块名称,必须要有下划线
sources=['E:\\Project\Huffman\Huffman_wrap.cxx', # 封装后的接口cxx文件
'E:\\Project\Huffman\HuffmanEncoderCompress.cpp' # 以下为原始代码所依赖的文件
],
)
setup(name='Huffman', # 打包后的名称
version='0.1',
author='SWIG Docs',
description='Simple swig pht from docs',
ext_modules=[hfm_module], # 与上面的扩展模块名称一致
py_modules=['Huffman'], # 需要打包的模块列表
)
通过命令行运行
python setup.py build_ext --inplace
这样会在本目录下生成_Huffman.pyd
模块。
出现的问题
1.python\include\pyconfig.h(59): fatal error C1083: Cannot open include file: 'io.h': No such file or directory
解决方法:
添加环境变量INCLUDE,路径指向VC的Include文件夹,如
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\include
2.e:\python\include\pyconfig.h(206): fatal error C1083: Cannot open include file: 'basetsd.h': No such file or directory
解决方法:
In case anyone is currently (2017) facing same error with visual C++ 2015 tools, launch setup again and also select windows 8.1 / 10 SDK depending upon your OS. This will fix basestd.h error.
If it is still not working, try launching build tools from: C:\Program Files (x86)\Microsoft Visual C++ Build Tools.
通过C:\Program Files (x86)\Microsoft Visual C++ Build Tools\Visual C++ 2015 x86 x64 Cross Build Tools Command Prompt
命令行运行
以管理员身份运行
这样会在该目录下生成_Huffman.pyd
模块。
1.3 测试Python调用
注:如果导入模块失败,需要将模块所在路径添加到sys.path
中
import sys
sys.path.append("PATH")
压缩文件
import Huffman
Huffman.Huffman("file\\test.pdf")
成功生成压缩文件
解压缩文件
import Huffman
Huffman.Huffman("file\\test.pdf.hfm")
成功生成解压缩文件
2、Java调用C++
2.1 Java生成C++头文件
新建一个Java项目,项目结构如下:
编写测试代码
package com.huffman.test;
public class JNIDemo {
public native void Huffman();
public static void main(String[] args){
System.loadLibrary("Huffman_DLL");
JNIDemo jnidemo = new JNIDemo();
jnidemo.Huffman();
}
}
生成C++头文件
在java程序包的目录src执行 javac -h
命令
E:\Project\Java\Huffman\src>javac -h ./ com\huffman\test\JNIDemo.java
会生成一个.h的头文件:
2.2 创建C++项目
新建一个C++项目,选择动态链接库(.dll)
配置新项目名称为"Huffman_JNI"
2.3 拷贝头文件
将jdk安装目录下的:include下的jni.h、 include/win32下的jni_md.h,java生成的头文件com_huffman_test_JNIDemo.h拷贝到C++项目空间中
并在C++项目中添加这三个头文件:
修改com_huffman_test_JNIDemo.h,把#include <jni.h>
改成#include "jni.h"
2.4 编写C++代码
新建类,命名为:
在这个类中实现数据的压缩与解压缩
项目结构:
Java接口函数:
2.5 生成DLL文件
配置项目属性为Release X64:
在工程名上右键生成:
工程目录下就会生成.dll文件
2.6 将DLL文件应用到Java项目中
将Huffman_JNI.dll拷贝到如下目录:
将DLL添加到项目的Library中
File --> Project Structure --> Huffman --> Dependencies --> 点**+** --> JARs or Directories --> 选择 Huffman_JNI.dll
2.7 运行Java测试
点击运行“Run JNIDemo”,程序成功调用之前用C++写的代码:
压缩文件:
解压缩文件:
四、实验出现的问题
Java调用C++生成的dll时会出现中文乱码问题
java内部是使用16bit的unicode编码(UTF-16)来表示字符串的,无论中文英文都是2字节; jni内部是使用UTF-8编码来表示字符串的,UTF-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节; c/c++使用的是原始数据,ascii就是一个字节了,中文一般是GB2312编码,用两个字节来表示一个汉字。
C++打印出的信息如果有中文就会导致乱码,如果调用时传输的参数有中文将会导致调用出错.
解决方案:实现utf与gb2312的转换
char* jstringToWindows( JNIEnv *env, jstring jstr )
{ //UTF8/16转换成gb2312
int length = (env)->GetStringLength(jstr );
const jchar* jcstr = (env)->GetStringChars(jstr, 0 );
char* rtn = (char*)malloc( length*2+1 );
int size = 0;
size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL );
if( size <= 0 )
return NULL;
(env)->ReleaseStringChars(jstr, jcstr );
rtn[size] = 0;
return rtn;
}
jstring WindowsTojstring( JNIEnv* env, const char* str )
{//gb2312转换成utf8/16
jstring rtn = 0;
int slen = strlen(str);
unsigned short * buffer = 0;
if( slen == 0 )
rtn = (env)->NewStringUTF(str );
else
{
int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
buffer = (unsigned short *)malloc( length*2 + 1 );
if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 )
rtn = (env)->NewString( (jchar*)buffer, length );
}
if( buffer )
free( buffer );
return rtn;
}
五、实验思考
请讨论跨语言开发的利弊;
利:
可以直接利用现有的库,避免了重复造轮子.
开发跨平台程序是对自己技术的一种挑战,也能在代码重构过程中得到一些新的经验。
能够与不同平台开发者交流以获得更多的技能点提升。
弊:
可能会导致预料之外的错误.
除了直接的跨语言调用,还有哪些方式可以多语言的协同开发?如果无法实现直接的跨语言调用,该如何实现多语言的协作?
还可以通过Web进行通信;打包成库;生成可执行文件等.