Swig super detailed introductory tutorial (Python 3 calls C/C++, CMake) -- updated on December 2021

Bloggers have stepped through many holes, many tutorials are built by Visual Studio (non CMake project), many tutorials are Python 2, and some common errors (mainly from environment configuration problems). This article is mainly to teach Mengxin how to build with official use cases (there are many bloody and tearful histories of stepping on the pit in person)

Related tutorials

Environment configuration

  1. swig official website tutorial: http://www.swig.org/tutorial.html
    swigwin download address: https://sourceforge.net/projects/swig/files/swigwin/
    After installation, for convenience, you can add the installation directory to the environment variable path.
SWIG_DIR = C:\swigwin-4.0.2
SWIG_EXECUTABLE = C:\swigwin-4.0.2\swig.exe
  1. C + +: I use Mingw-w64 (GCC 9.3.0). If I use a higher version of python, the gcc version cannot be too low, otherwise an error will be reported. Remember to configure environment variables! Super detailed tutorial: windows installation MSYS2 (MinGW & & GCC) - updated on 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. Python: I use Anaconda (Python 3.9.7). Configure the environment variable PYTHONHOME = C:\ProgramData\Anaconda3. https://www.anaconda.com/
  2. Clion: install and configure Toolchains normally. (of course, other ides can also be selected)
  3. Copy and rename C:/ProgramData/Anaconda3/libs/python39.lib to C:/ProgramData/Anaconda3/libs/python39_d.lib, mainly to add debug scenarios.
  4. Restart the computer (the environment variable must be restarted to take effect. In fact, many scenarios can take effect without restarting, but this case is necessary, and strange bug s will occur without restarting)

Examples of using CMake (C language)

Project directory structure:

We first build the following example.c file

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

Then build the example.i file

/* File : example.i */
%module example

%inline %{
extern int    gcd(int x, int y);
extern double Foo;
%}

We created CMakeLists.txt:

#/* File : CMakeLists.txt */
#Specifies the minimum version of CMake
cmake_minimum_required(VERSION 3.17)
project(Example)

set(CMAKE_CXX_STANDARD 20)

#Find the environment variable for PYTHONHOME
find_package (Python3 COMPONENTS Interpreter Development)
include_directories(${Python3_INCLUDE_DIRS})
link_libraries(${Python3_LIBRARIES})

#Specify the directory of your. cxx and other files
include_directories(${PROJECT_SOURCE_DIR}/src)

#Looking for the installed Swig is actually going to the computer to find the Swig environment you installed, so we need to install the environment in advance.
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})
#Python file output directory - outdir
set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR}/python)
#Specify an output directory name to place the generated source files. For example, output exampleptyhon_ The location of the wrap.cxx file, etc
set(SWIG_OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/cpp)
#Start build
swig_add_library(example LANGUAGE python SOURCES src/example.i src/example.c)

Then we reload the CMake project

Then click build:

The following is generated:

Copy these files to a new folder, and then create runme.py:

# file: runme.py

import example

# Call our gcd() function

x = 42
y = 105
g = example.gcd(x, y)
print("The gcd of %d and %d is %d" % (x, y, g))

# Manipulate the Foo global variable

# Output its current value
print("Foo = ", example.cvar.Foo)

# Change its value
example.cvar.Foo = 3.1415926

# See if the change took effect
print("Foo = ", example.cvar.Foo)

Run runme:

The gcd of 42 and 105 is 21
Foo =  3.0
Foo =  3.1415926

Examples of using CMake (C + +)

Project directory structure:

We first build the following example.cxx file

/* File : example.cxx */

#include "example.h"

#define M_PI 3.14159265358979323846

/* Move the shape to a new location */
void Shape::move(double dx, double dy) {
    x += dx;
    y += dy;
}

int Shape::nshapes = 0;

double Circle::area() {
    return M_PI * radius * radius;
}

double Circle::perimeter() {
    return 2 * M_PI * radius;
}

double Square::area() {
    return width * width;
}

double Square::perimeter() {
    return 4 * width;
}

Then build the example.i file

/* File : example.i */
%module example

%{
#include "example.h"
%}

/* Let's just grab the original header file here */
%include "example.h"

Then build the example.h file

/* File : example.h */

class Shape {
public:
    Shape() {
        nshapes++;
    }

    virtual ~Shape() {
        nshapes--;
    }

    double x, y;

    void move(double dx, double dy);

    virtual double area() = 0;

    virtual double perimeter() = 0;

    static int nshapes;
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}

    virtual double area();

    virtual double perimeter();
};

class Square : public Shape {
private:
    double width;
public:
    Square(double w) : width(w) {}

    virtual double area();

    virtual double perimeter();
};

We create CMakeLists.txt (note the difference between this and CMakeLists.txt in C language):

#/* File : CMakeLists.txt */
#Specifies the minimum version of CMake
cmake_minimum_required(VERSION 3.17)
project(Example)

set(CMAKE_CXX_STANDARD 20)

#Find the environment variable for PYTHONHOME
find_package (Python3 COMPONENTS Interpreter Development)
include_directories(${Python3_INCLUDE_DIRS})
link_libraries(${Python3_LIBRARIES})

#Specify the directory of your. cxx and other files
include_directories(${PROJECT_SOURCE_DIR}/src)

#Looking for the installed Swig is actually going to the computer to find the Swig environment you installed, so we need to install the environment in advance.
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})
#Python file output directory - outdir
set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR}/python)
#Specify an output directory name to place the generated source files. For example, output exampleptyhon_ The location of the wrap.cxx file, etc
set(SWIG_OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/cpp)
#c + + mode
set_property(SOURCE src/example.i PROPERTY CPLUSPLUS ON)
#Start build
swig_add_library(example LANGUAGE python SOURCES src/example.i src/example.cxx)

Then we reload the CMake project

Then click build:

The following is generated:

Copy these files to a new folder, and then create runme.java:

# file: runme.py

# This file illustrates the proxy class C++ interface generated
# by SWIG.

import example

# ----- Object creation -----

print("Creating some objects:")
c = example.Circle(10)
print("    Created circle", c)
s = example.Square(10)
print("    Created square", s)

# ----- Access a static member -----

print("\nA total of", example.cvar.Shape_nshapes, "shapes were created")

# ----- Member data access -----

# Set the location of the object

c.x = 20
c.y = 30

s.x = -10
s.y = 5

print("\nHere is their current position:")
print("    Circle = (%f, %f)" % (c.x, c.y))
print("    Square = (%f, %f)" % (s.x, s.y))

# ----- Call some methods -----

print
"\nHere are some properties of the shapes:"
for o in [c, s]:
    print("   ", o)
    print("        area      = ", o.area())
    print("        perimeter = ", o.perimeter())
# prevent o from holding a reference to the last object looked at
o = None

print("\nGuess I'll clean up now")

# Note: this invokes the virtual destructor
del c
del s

print(example.cvar.Shape_nshapes, "shapes remain")
print("Goodbye")

Run runme:

Creating some objects:
    Created circle <example.Circle; proxy of <Swig Object of type 'Circle *' at 0x000001691EA1E870> >
    Created square <example.Square; proxy of <Swig Object of type 'Square *' at 0x000001691EA1ECC0> >

A total of 2 shapes were created

Here is their current position:
    Circle = (20.000000, 30.000000)
    Square = (-10.000000, 5.000000)
    <example.Circle; proxy of <Swig Object of type 'Circle *' at 0x000001691EA1E870> >
        area      =  314.1592653589793
        perimeter =  62.83185307179586
    <example.Square; proxy of <Swig Object of type 'Square *' at 0x000001691EA1ECC0> >
        area      =  100.0
        perimeter =  40.0

Guess I'll clean up now
0 shapes remain
Goodbye

Process finished with exit code 0

Tags: Python C C++

Posted on Sun, 05 Dec 2021 08:26:35 -0500 by Yves