Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12

目录

  • 相关教程
  • 环境配置
  • 0基础上手例子(C/C++)
  • 使用CMake的例子(C语言)
  • 使用CMake的例子(C++)

本文主要是手把手教萌新们如何用官方用例构建(有许多本人亲身踩坑血泪史)

相关教程

  • 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,包含一些常见错误)

环境配置

  1. 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
  1. 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.
  1. Clion:正常安装配置好Toolchains就可以了。(当然也可以选用其他IDE)
  2. IDEA:正常安装后,在菜单栏依次 File->Project Structure->Project->Project SDK->ADD SDK->Download SDK,环境变量JAVA_HOME = C:\.jdks\openjdk-17.0.1
    Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12_第1张图片

0基础上手例子(C/C++)

首先我们看一个最简单的例子,如何用swig让java调用c。这里使用的是mingw和java8。

首先构建如下example.c文件

/* File : example.c */

#include 

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(&ltime);
    return ctime(&ltime);
}

之后构建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语言)

项目工程目录结构:
Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12_第2张图片
和之前一样,我们首先构建如下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项目
Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12_第3张图片
然后点击build:
在这里插入图片描述
之后会生成:
Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12_第4张图片
把这几个文件复制到一个新建的文件夹里,然后再新建runme.java:
Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12_第5张图片

/* 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++)

项目工程目录结构:
Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12_第6张图片
和之前一样,我们首先构建如下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 
#include 
#include 
#include 
#include 

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项目
Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12_第7张图片
然后点击build:
在这里插入图片描述
之后会生成:
Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12_第8张图片

把这几个文件复制到一个新建的文件夹里,然后再新建runme.java:
Swig超详细入门教程(Java调用C/C++, CMake)——更新于2021.12_第9张图片

/* 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

你可能感兴趣的:(笔记,java,c++,c语言)