本文主要是手把手教萌新们如何用官方用例构建(有许多本人亲身踩坑血泪史)
相关教程
- Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12
- 【超详细教程】Java调用C/C++中的Debug(JNI/Swig通用)——更新于2022.01
- Swig超详细入门教程(Python3调用C/C++, CMake)——更新于2021.12
- 【从零开始】C++调用python(CMake,Clion,windows,包含一些常见错误)
环境配置
- swig官网教程:http://www.swig.org/tutorial.html
swigwin下载地址:https://sourceforge.net/projects/swig/files/swigwin/
安装后为了方便,可以添加安装目录到环境变量path。
SWIG_DIR = C:\swigwin-4.0.2
SWIG_EXECUTABLE = C:\swigwin-4.0.2\swig.exe
- C++:我使用的是Mingw-w64(GCC 9.3.0),如果使用高版本的python,gcc版本不能太低,否则会报错。记得配置环境变量哦! 超详细教程:windows安装MSYS2(mingw && gcc)——更新于2021.11
$ gcc --version
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- Clion:正常安装配置好Toolchains就可以了。(当然也可以选用其他IDE)
- IDEA:正常安装后,在菜单栏依次 File->Project Structure->Project->Project SDK->ADD SDK->Download SDK,环境变量
JAVA_HOME = C:\.jdks\openjdk-17.0.1
0基础上手例子(C/C++)
首先我们看一个最简单的例子,如何用swig让java调用c。这里使用的是mingw和java8。
首先构建如下example.c文件
/* File : example.c */
#include <time.h>
double My_variable = 3.0;
int fact(int n) {
if (n <= 1) return 1;
else return n * fact(n - 1);
}
int my_mod(int x, int y) {
return (x % y);
}
char *get_time() {
time_t ltime;
time(<ime);
return ctime(<ime);
}
之后构建example.i文件
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern double My_variable;
extern int fact(int n);
extern int my_mod(int x, int y);
extern char *get_time();
%}
extern double My_variable;
extern int fact(int n);
extern int my_mod(int x, int y);
extern char *get_time();
在当前目录打开cmd,然后输入:
$ swig -java example.i
$ gcc -c example.c example_wrap.c -I "C:\Program Files\jdk1.8.0_262\include" -I "C:\Program Files\jdk1.8.0_262\include\win32"
$ gcc -shared example.o example_wrap.o -mno-clwb -Wl,--add-stdcall-alias -o example.dll
如果想用C++编译可以用如下(注意和上面的区别):
$ swig -c++ -java example.i
$ g++ -c example.c example_wrap.cxx -I "C:\Program Files\jdk1.8.0_262\include" -I "C:\Program Files\jdk1.8.0_262\include\win32"
$ g++ -shared example.o example_wrap.o -mno-clwb -Wl,--add-stdcall-alias -o example.dll
创建main.java
/* main.java */
public class main {
public static void main(String argv[]) {
System.loadLibrary("example");
System.out.println(example.getMy_variable());
System.out.println(example.fact(5));
System.out.println(example.get_time());
}
}
$ javac main.java
$ java main
3.0
120
Mon Mar 4 18:20:31 2002
$
使用CMake的例子(C语言)
项目工程目录结构:
和之前一样,我们首先构建如下example.c文件
/* File : example.c */
/* A global variable */
double Foo = 3.0;
/* Compute the greatest common divisor of positive integers */
int gcd(int x, int y) {
int g;
g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
}
之后构建example.i文件
/* File : example.i */
%module example
%inline %{
extern int gcd(int x, int y);
extern double Foo;
%}
我们创建CMakeLists.txt:
#[[ CMakeLists.txt ]]
#指定CMake的最小版本
cmake_minimum_required(VERSION 3.17)
project(Example)
set(CMAKE_CXX_STANDARD 20)
include_directories($ENV{JAVA_HOME}/include)
include_directories($ENV{JAVA_HOME}/include/win32)
#指定你的.cxx等文件的目录
include_directories(${PROJECT_SOURCE_DIR}/src)
#寻找安装好的swig,其实就是去电脑中找你安装好的Swig环境,所以我们需要提前安装环境。
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})
#JAVA文件输出目录 -outdir
set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR}/java)
#指定一个输出目录名,用于放置生成的源文件。例如输出的exampleJAVA_wrap.cxx文件等的位置
set(SWIG_OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/cpp)
#开始生成
swig_add_library(example LANGUAGE java SOURCES src/example.i src/example.c)
之后我们重载CMake项目
然后点击build:
之后会生成:
把这几个文件复制到一个新建的文件夹里,然后再新建runme.java:
/* File : runme.java */
public class runme {
static {
try {
System.loadLibrary("example");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void main(String argv[]) {
// Call our gcd() function
int x = 42;
int y = 105;
int g = example.gcd(x, y);
System.out.println("The gcd of " + x + " and " + y + " is " + g);
// Manipulate the Foo global variable
// Output its current value
System.out.println("Foo = " + example.getFoo());
// Change its value
example.setFoo(3.1415926);
// See if the change took effect
System.out.println("Foo = " + example.getFoo());
}
}
运行runme.main():
The gcd of 42 and 105 is 21
Foo = 3.0
Foo = 3.1415926
使用CMake的例子(C++)
项目工程目录结构:
和之前一样,我们首先构建如下example.cxx文件
/* File : example.cxx */
#include "example.h"
之后构建example.i文件
/* File : example.i */
%module(directors="1") example
%{
#include "example.h"
%}
%include "std_vector.i"
%include "std_string.i"
/* turn on director wrapping for Manager */
%feature("director") Employee;
%feature("director") Manager;
%include "example.h"
之后构建example.h文件
/* File : example.h */
#include <cstdio>
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
class Employee {
private:
std::string name;
public:
Employee(const char* n): name(n) {}
virtual std::string getTitle() { return getPosition() + " " + getName(); }
virtual std::string getName() { return name; }
virtual std::string getPosition() const { return "Employee"; }
virtual ~Employee() { printf("~Employee() @ %p\n", (void *)this); }
};
class Manager: public Employee {
public:
Manager(const char* n): Employee(n) {}
virtual std::string getPosition() const { return "Manager"; }
};
class EmployeeList {
std::vector<Employee*> list;
public:
EmployeeList() {
list.push_back(new Employee("Bob"));
list.push_back(new Employee("Jane"));
list.push_back(new Manager("Ted"));
}
void addEmployee(Employee *p) {
list.push_back(p);
std::cout << "New employee added. Current employees are:" << std::endl;
std::vector<Employee*>::iterator i;
for (i=list.begin(); i!=list.end(); i++) {
std::cout << " " << (*i)->getTitle() << std::endl;
}
}
const Employee *get_item(int i) {
return list[i];
}
~EmployeeList() {
std::vector<Employee*>::iterator i;
std::cout << "~EmployeeList, deleting " << list.size() << " employees." << std::endl;
for (i=list.begin(); i!=list.end(); i++) {
delete *i;
}
std::cout << "~EmployeeList empty." << std::endl;
}
};
我们创建CMakeLists.txt(注意这里和C语言的CMakeLists.txt的区别):
#[[ CMakeLists.txt ]]
#指定CMake的最小版本
cmake_minimum_required(VERSION 3.17)
project(Example)
set(CMAKE_CXX_STANDARD 20)
include_directories($ENV{JAVA_HOME}/include)
include_directories($ENV{JAVA_HOME}/include/win32)
#指定你的.cxx等文件的目录
include_directories(${PROJECT_SOURCE_DIR}/src)
#寻找安装好的swig,其实就是去电脑中找你安装好的Swig环境,所以我们需要提前安装环境。
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})
#JAVA文件输出目录 -outdir
set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR}/java)
#指定一个输出目录名,用于放置生成的源文件。例如输出的exampleJAVA_wrap.cxx文件等的位置
set(SWIG_OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/cpp)
#c ++模式
set_property(SOURCE src/example.i PROPERTY CPLUSPLUS ON)
#开始生成
swig_add_library(example LANGUAGE java SOURCES src/example.i)
之后我们重载CMake项目
然后点击build:
之后会生成:
把这几个文件复制到一个新建的文件夹里,然后再新建runme.java:
/* File : runme.java */
// This file illustrates the cross language polymorphism using directors.
// CEO class, which overrides Employee::getPosition().
class CEO extends Manager {
public CEO(String name) {
super(name);
}
public String getPosition() {
return "CEO";
}
// Public method to stop the SWIG proxy base class from thinking it owns the underlying C++ memory.
public void disownMemory() {
swigCMemOwn = false;
}
}
public class runme {
static {
try {
System.loadLibrary("example");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void main(String argv[]) {
// Create an instance of CEO, a class derived from the Java proxy of the
// underlying C++ class. The calls to getName() and getPosition() are standard,
// the call to getTitle() uses the director wrappers to call CEO.getPosition().
CEO e = new CEO("Alice");
System.out.println(e.getName() + " is a " + e.getPosition());
System.out.println("Just call her \"" + e.getTitle() + "\"");
System.out.println("----------------------");
// Create a new EmployeeList instance. This class does not have a C++
// director wrapper, but can be used freely with other classes that do.
EmployeeList list = new EmployeeList();
// EmployeeList owns its items, so we must surrender ownership of objects we add.
e.disownMemory();
list.addEmployee(e);
System.out.println("----------------------");
// Now we access the first four items in list (three are C++ objects that
// EmployeeList's constructor adds, the last is our CEO). The virtual
// methods of all these instances are treated the same. For items 0, 1, and
// 2, all methods resolve in C++. For item 3, our CEO, getTitle calls
// getPosition which resolves in Java. The call to getPosition is
// slightly different, however, because of the overridden getPosition() call, since
// now the object reference has been "laundered" by passing through
// EmployeeList as an Employee*. Previously, Java resolved the call
// immediately in CEO, but now Java thinks the object is an instance of
// class Employee. So the call passes through the
// Employee proxy class and on to the C wrappers and C++ director,
// eventually ending up back at the Java CEO implementation of getPosition().
// The call to getTitle() for item 3 runs the C++ Employee::getTitle()
// method, which in turn calls getPosition(). This virtual method call
// passes down through the C++ director class to the Java implementation
// in CEO. All this routing takes place transparently.
System.out.println("(position, title) for items 0-3:");
System.out.println(" " + list.get_item(0).getPosition() + ", \"" + list.get_item(0).getTitle() + "\"");
System.out.println(" " + list.get_item(1).getPosition() + ", \"" + list.get_item(1).getTitle() + "\"");
System.out.println(" " + list.get_item(2).getPosition() + ", \"" + list.get_item(2).getTitle() + "\"");
System.out.println(" " + list.get_item(3).getPosition() + ", \"" + list.get_item(3).getTitle() + "\"");
System.out.println("----------------------");
// Time to delete the EmployeeList, which will delete all the Employee*
// items it contains. The last item is our CEO, which gets destroyed as well.
list.delete();
System.out.println("----------------------");
// All done.
System.out.println("java exit");
}
}
运行runme.main():
Alice is a CEO
Just call her "CEO Alice"
----------------------
New employee added. Current employees are:
Employee Bob
Employee Jane
Manager Ted
CEO Alice
----------------------
(position, title) for items 0-3:
Employee, "Employee Bob"
Employee, "Employee Jane"
Manager, "Manager Ted"
CEO, "CEO Alice"
----------------------
~EmployeeList, deleting 4 employees.
~Employee() @ 00000299491925A0
~Employee() @ 0000029949192970
~Employee() @ 00000299491929C0
~Employee() @ 00000299491928F0
~EmployeeList empty.
----------------------
java exit
Process finished with exit code 0