本文首发于个人博客https://kezunlin.me/post/8b9c051d/,欢迎阅读!

Interfacing C++ and Python with pybind11 on windows 10

Series

Guide

requirements:

  • pybind11 v2.3.dev0
  • python 2.7

install pytest

pip install pytest 

compile

git clone https://github.com/pybind/pybind11.git
cd pybind11
mkdir build
cd build
cmake-gui ..

with options

PYBIND11_CPP_STANDARD /std:c++11 # default c++14
PYTHON_EXECUTABLE C:/Python27/python.exe
CMAKE_INSTALL_PREFIX C:/Program Files/pybind11

compile with VS 2015 with x64 Release

install to C:\Program Files\pybind11 with only include and share

$ tree .

.
├── include
│   └── pybind11
│       ├── attr.h
│       ├── buffer_info.h
│       ├── cast.h
│       ├── chrono.h
│       ├── common.h
│       ├── complex.h
│       ├── detail
│       │   ├── class.h
│       │   ├── common.h
│       │   ├── descr.h
│       │   ├── init.h
│       │   ├── internals.h
│       │   └── typeid.h
│       ├── eigen.h
│       ├── embed.h
│       ├── eval.h
│       ├── functional.h
│       ├── iostream.h
│       ├── numpy.h
│       ├── operators.h
│       ├── options.h
│       ├── pybind11.h
│       ├── pytypes.h
│       ├── stl.h
│       └── stl_bind.h
└── share
    └── cmake
        └── pybind11
            ├── FindPythonLibsNew.cmake
            ├── pybind11Config.cmake
            ├── pybind11ConfigVersion.cmake
            ├── pybind11Targets.cmake
            └── pybind11Tools.cmake

6 directories, 29 files

Usage

pybind11 (cpp--->python)

  • module: examplelib
  • target: examplelib

  • cpp: example.cpp

example.cpp

#include <pybind11/pybind11.h>

namespace py = pybind11;

int add(int i, int j) {
    return i + j;
}

/*
#include <pybind11/pybind11.h>

namespace py = pybind11;

int add(int i, int j) {
    return i + j;
}

struct Pet {
    Pet(const std::string &name) : name(name) { }
    void setName(const std::string &name_) { name = name_; }
    const std::string &getName() const { return name; }

    std::string name;
};


/*
module: examplelib
target: examplelib

cpp: example.cpp
*/
PYBIND11_MODULE(examplelib, m)
{
    // optional module docstring
    m.doc() = "pybind11 example plugin";

    // FUNCTIONS
    // expose add function, and add keyword arguments and default arguments
    m.def("add", &add, "A function which adds two numbers", py::arg("i") = 1, py::arg("j") = 2);

    // DATA
    // exporting variables
    m.attr("the_answer") = 42;
    py::object world = py::cast("World");
    m.attr("what") = world;


    // CLASSES
    py::class_<Pet>(m, "Pet")
        .def(py::init<const std::string &>())
        .def("setName", &Pet::setName)
        .def("getName", &Pet::getName);

    /*
    python3
    > help(examplelib)
    */
}

CMakeLists.txt

cmake_minimum_required (VERSION 2.6)

project (pybind)
enable_language(C)
enable_language(CXX)

find_package(pybind11 CONFIG REQUIRED)
include_directories(${pybind11_INCLUDE_DIRS})
message([MAIN] "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}")

MESSAGE( [Main] " pybind11_INCLUDE_DIRS = ${pybind11_INCLUDE_DIRS}")
MESSAGE( [Main] " pybind11_LIBRARIES = ${pybind11_LIBRARIES}")

#
#   # Create an extension module
#   add_library(mylib MODULE main.cpp)
#   target_link_libraries(mylib pybind11::module)
#
#   # Or embed the Python interpreter into an executable
#   add_executable(myexe main.cpp)
#   target_link_libraries(myexe pybind11::embed)

# method (1): generate `examplelib.pyd`
pybind11_add_module(examplelib example.cpp)

# method (2): generate `examplelib.dll` rename to `examplelib.pyd`
#add_library(examplelib MODULE example.cpp)
#target_link_libraries(examplelib pybind11::module)

MESSAGE( [Main] " pybind11_INCLUDE_DIRS = ${pybind11_INCLUDE_DIRS}")
MESSAGE( [Main] " pybind11_LIBRARIES = ${pybind11_LIBRARIES}")

#add_executable(cpp_use_python cpp_use_python.cpp)
#target_link_libraries(cpp_use_python PRIVATE pybind11::embed)

cmake and config

build with vs and we get 3 files:

examplelib.lib
examplelib.exp
examplelib.cp35-win_amd64.pyd

python import examplelib

 python3
Python 3.5.3 (v3.5.3:1880cb95a742, Jan 16 2017, 16:02:32) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import examplelib
>>> help(examplelib)
Help on module examplelib:

NAME
    examplelib - pybind11 example plugin

CLASSES
    pybind11_builtins.pybind11_object(builtins.object)
        Pet

    class Pet(pybind11_builtins.pybind11_object)
     |  Method resolution order:
     |      Pet
     |      pybind11_builtins.pybind11_object
     |      builtins.object
     |
     |  Methods defined here:
     |
     |  __init__(...)
     |      __init__(self: examplelib.Pet, arg0: str) -> None
     |
     |  getName(...)
     |      getName(self: examplelib.Pet) -> str
     |
     |  setName(...)
     |      setName(self: examplelib.Pet, arg0: str) -> None
     |
     |  ----------------------------------------------------------------------
     |  Methods inherited from pybind11_builtins.pybind11_object:
     |
     |  __new__(*args, **kwargs) from pybind11_builtins.pybind11_type
     |      Create and return a new object.  See help(type) for accurate signature.

FUNCTIONS
    add(...) method of builtins.PyCapsule instance
        add(i: int = 1, j: int = 2) -> int

        A function which adds two numbers

DATA
    the_answer = 42
    what = 'World'

FILE
    e:\git\car\extra\pybind11\build\release\examplelib.cp35-win_amd64.pyd


>>> p = examplelib.Pet('kzl')
>>> print(p)
<examplelib.Pet object at 0x0000025EED9E3D18>
>>> p.getName()
'kzl'
>>> examplelib.add(1,2)
3
>>> examplelib.the_answer
42
>>> examplelib.what
'World'
>>>

embed

example.py


def add(i, j):
    print("hello, pybind11")
    return i+j

class MyMath:

    def __init__(self,name):
        self.name = name

    def my_add(self,i,j):
        return i + j

    def my_strcon(self,a,b):
        return a + b

cpp_use_python.cpp

#include <pybind11/embed.h>
#include <iostream>

namespace py = pybind11;

int main() {
    py::scoped_interpreter python;

    /*
    import sys
    print sys.path
    print "Hello,World!"
    */
    py::module sys = py::module::import("sys");
    py::print(sys.attr("path"));
    py::print("Hello, World!"); // use the Python API

    /*
    import example
    n = example.add(1,2)
    */
    py::module example = py::module::import("example");
    py::object result = example.attr("add")(1, 2);
    int n = result.cast<int>();
    assert(n == 3);
    std::cout << "result from example.add(1,2) = " << n << std::endl;

    /*
    from example import MyMath
    obj = MyMath("v0")
    obj.my_add(1,2)
    */
    py::object MyMath = py::module::import("example").attr("MyMath"); // class
    py::object obj = MyMath("v0"); // class object
    py::object my_add = obj.attr("my_add");// object method
    py::object result2 = my_add(1, 2); // result
    int n2 = result2.cast<int>(); // cast from python type to c++ type
    assert(n2 == 3);
    std::cout << "result from obj.my_add(1,2) = " << n2 << std::endl;

    /*
    from example import MyMath
    obj = MyMath("v0")
    obj.my_strcon("abc","123");
    */

    py::object my_strcon = obj.attr("my_strcon"); // object method
    py::object result3 = my_strcon("abc", "123");
    std::string str3 = result3.cast<std::string>();
    std::cout << "result from obj.my_strcon(abc,123) = " << str3 << std::endl;

    return 0;
}

CMakeLists.txt

cmake_minimum_required (VERSION 2.6)

project (pybind)
enable_language(C)
enable_language(CXX)

find_package(pybind11 CONFIG REQUIRED)
include_directories(${pybind11_INCLUDE_DIRS})

MESSAGE( [Main] " pybind11_INCLUDE_DIRS = ${pybind11_INCLUDE_DIRS}")
MESSAGE( [Main] " pybind11_LIBRARIES = ${pybind11_LIBRARIES}")

add_executable(cpp_use_python cpp_use_python.cpp)
target_link_libraries(cpp_use_python PRIVATE pybind11::embed)

Reference

History

  • 20180301: created.

Copyright

12-31 10:02