Initial commit

This commit is contained in:
Wenzel Jakob 2015-07-05 20:05:44 +02:00
commit 38bd71134a
40 changed files with 3827 additions and 0 deletions

20
.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
CMakeCache.txt
CMakeFiles
Makefile
cmake_install.cmake
.DS_Store
/example/example.so
/example/example.pyd
*.sln
*.sdf
*.opensdf
*.vcxproj
*.filters
example.dir
Win32
x64
Release
Debug
.vs
CTestTestfile.cmake
Testing

113
CMakeLists.txt Normal file
View File

@ -0,0 +1,113 @@
# CMakeLists.txt -- Build system for the pybind11 examples
#
# Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
#
# All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
cmake_minimum_required(VERSION 2.8)
project(pybind)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'MinSizeRel' as none was specified.")
set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
"MinSizeRel" "RelWithDebInfo")
endif()
set(Python_ADDITIONAL_VERSIONS 3.4 3.5 3.6)
find_package(PythonLibs 3 REQUIRED)
find_package(PythonInterp 3 REQUIRED)
string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE)
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-unsequenced")
if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
endif()
endif()
# Compile with compiler warnings turned on
if(MSVC)
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
endif()
include_directories(${PYTHON_INCLUDE_DIR} include)
add_library(example SHARED
include/pybind/cast.h
include/pybind/common.h
include/pybind/mpl.h
include/pybind/operators.h
include/pybind/pybind.h
include/pybind/pytypes.h
include/pybind/typeid.h
example/example.cpp
example/example1.cpp
example/example2.cpp
example/example3.cpp
example/example4.cpp
example/example5.cpp
example/example6.cpp
example/example7.cpp
example/example8.cpp
example/example9.cpp
)
set_target_properties(example PROPERTIES PREFIX "")
set_target_properties(example PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/example)
if (WIN32)
if (MSVC)
# Enforce size-based optimization and link time code generation on MSVC (~30% smaller binaries in experiments)
set_target_properties(example PROPERTIES COMPILE_FLAGS "/Os /GL")
set_target_properties(example PROPERTIES LINK_FLAGS "/LTCG")
endif()
# .PYD file extension on Windows
set_target_properties(example PROPERTIES SUFFIX ".pyd")
# Link against the Python shared library
target_link_libraries(example ${PYTHON_LIBRARY})
elseif (UNIX)
# It's quite common to have multiple copies of the same Python version
# installed on one's system. E.g.: one copy from the OS and another copy
# that's statically linked into an application like Blender or Maya.
# If we link our plugin library against the OS Python here and import it
# into Blender or Maya later on, this will cause segfaults when multiple
# conflicting Python instances are active at the same time.
# Windows does not seem to be affected by this issue. The solution for Linux
# and Mac OS is simple: we just don't link against the Python library. The
# resulting shared library will have missing symbols, but that's perfectly
# fine -- they will be resolved at import time.
# .SO file extension on Linux/Mac OS
set_target_properties(example PROPERTIES SUFFIX ".so")
# Strip unnecessary sections of the binary on Linux/Mac OS
if(APPLE)
set_target_properties(example PROPERTIES MACOSX_RPATH ".")
set_target_properties(example PROPERTIES LINK_FLAGS "-undefined dynamic_lookup -dead_strip")
if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG)
add_custom_command(TARGET example POST_BUILD COMMAND strip -u -r ${PROJECT_SOURCE_DIR}/example/example.so)
endif()
else()
if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG)
add_custom_command(TARGET example POST_BUILD COMMAND strip ${PROJECT_SOURCE_DIR}/example/example.so)
endif()
endif()
endif()
enable_testing()
set(RUN_TEST ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/example/run_test.py)
foreach(i RANGE 1 7)
add_test(NAME example${i} COMMAND ${RUN_TEST} example${i})
endforeach()

17
LICENSE Normal file
View File

@ -0,0 +1,17 @@
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

106
README.md Normal file
View File

@ -0,0 +1,106 @@
# pybind11 -- Seamless operability between C++11 and Python
**pybind11** is a lightweight header library that exposes C++ types in Python
and vice versa, mainly to create Python bindings of existing C++ code. Its
goals and syntax are similar to the excellent
[Boost.Python](http://www.boost.org/doc/libs/1_58_0/libs/python/doc/) library
by David Abrahams: to minimize boilerplate code in traditional extension
modules by inferring type information using compile-time introspection.
The main issue with Boost.Python—and the reason for creating such a similar
project—is Boost. Boost is an enormously large and complex suite of utility
libraries that works with almost every C++ compiler in existence. This
compatibility has its cost: arcane template tricks and workarounds are
necessary to support the oldest and buggiest of compiler specimens. Now that
C++11-compatible compilers are widely available, this heavy machinery has
become an excessively large and unnecessary dependency.
Think of this library as a tiny self-contained version of Boost.Python with
everything stripped away that isn't relevant for binding generation. The whole
codebase requires less than 2000 lines of code and just depends on Python and
the C++ standard library. This compact implementation was possible thanks to
some of the new C++11 language features (tuples, lambda functions and variadic
templates), and by only targeting Python 3.x and higher.
## Core features
The following C++ features can be mapped to Python
- Functions accepting and returning custom data structures per value, reference, or pointer
- Instance methods and static methods
- Overloaded functions
- Instance attributes and static attributes
- Exceptions
- Enumerations
- Callbacks
- Custom operators
- STL data structures
- Smart pointers with reference counting like `std::shared_ptr`
- Internal references with correct reference counting
## Goodies
In addition to the core functionality, pybind11 provides some extra goodies:
- It's easy to expose the internal storage of custom data types through
Pythons' buffer protocols. This is handy e.g. for fast conversion between
C++ matrix classes like Eigen and NumPy without expensive copy operations.
- Python's slice-based access and assignment operations can be supported with
just a few lines of code.
- pybind11 uses C++11 move constructors and move assignment operators whenever
possible to efficiently transfer custom data types.
## Limitations
Various things that Boost.Python can do remain unsupported, e.g.:
- Fine grained exception translation: currently, all exceptions derived from
`std::exception` are mapped to a Python `Exception`, but that's it.
- Default arguments in C++ functions are ignored, though their effect can be
emulated by binding multiple overloads using anonymous functions.
- Python keyword arguments are not supported in bindings
- Weak pointers are not supported
## What does the binding code look like?
Here is a simple example. The directory `example` contains many more.
```C++
#include <pybind/pybind.h>
#include <pybind/operators.h>
namespace py = pybind;
/// Example C++ class which should be bound to Python
class Test {
public:
Test();
Test(int value);
std::string toString();
Test operator+(const Test &e) const;
void print_dict(py::dict dict) {
/* Easily interact with Python types */
for (auto item : dict)
std::cout << "key=" << item.first << ", "
<< "value=" << item.second << std::endl;
}
int value = 0;
};
PYTHON_PLUGIN(example) {
py::module m("example", "pybind example plugin");
py::class_<Test>(m, "Test", "docstring for the Test class")
.def(py::init<>(), "docstring for constructor 1")
.def(py::init<int>(), "docstring for constructor 2")
.def(py::self + py::self, "Addition operator")
.def("__str__", &Test::toString, "Convert to a string representation")
.def("print_dict", &Test::print_dict, "Print a Python dictionary")
.def_readwrite("value", &Test::value, "An instance attribute");
return m.ptr();
}
```

36
example/example.cpp Normal file
View File

@ -0,0 +1,36 @@
/*
example/example.cpp -- pybind example plugin
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
void init_ex1(py::module &);
void init_ex2(py::module &);
void init_ex3(py::module &);
void init_ex4(py::module &);
void init_ex5(py::module &);
void init_ex6(py::module &);
void init_ex7(py::module &);
void init_ex8(py::module &);
void init_ex9(py::module &);
PYTHON_PLUGIN(example) {
py::module m("example", "pybind example plugin");
init_ex1(m);
init_ex2(m);
init_ex3(m);
init_ex4(m);
init_ex5(m);
init_ex6(m);
init_ex7(m);
init_ex8(m);
init_ex9(m);
return m.ptr();
}

7
example/example.h Normal file
View File

@ -0,0 +1,7 @@
#include <pybind/pybind.h>
#include <iostream>
using std::cout;
using std::endl;
namespace py = pybind;

92
example/example1.cpp Normal file
View File

@ -0,0 +1,92 @@
/*
example/example1.cpp -- Example 1: constructors, deconstructors,
attribute access, __str__, argument and return value conventions
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
class Example1 {
public:
Example1() {
cout << "Called Example1 default constructor.." << endl;
}
Example1(int value) : value(value) {
cout << "Called Example1 constructor with value " << value << ".." << endl;
}
Example1(const Example1 &e) : value(e.value) {
cout << "Called Example1 copy constructor with value " << value << ".." << endl;
}
Example1(Example1 &&e) : value(e.value) {
cout << "Called Example1 move constructor with value " << value << ".." << endl;
e.value = 0;
}
~Example1() {
cout << "Called Example1 destructor (" << value << ")" << endl;
}
std::string toString() {
return "Example1[value=" + std::to_string(value) + "]";
}
void operator=(const Example1 &e) { cout << "Assignment operator" << endl; value = e.value; }
void operator=(Example1 &&e) { cout << "Move assignment operator" << endl; value = e.value; e.value = 0;}
void add1(Example1 other) { value += other.value; } // passing py value
void add2(Example1 &other) { value += other.value; } // passing by reference
void add3(const Example1 &other) { value += other.value; } // passing by const reference
void add4(Example1 *other) { value += other->value; } // passing py pointer
void add5(const Example1 *other) { value += other->value; } // passing by const pointer
void add6(int other) { value += other; } // passing py value
void add7(int &other) { value += other; } // passing by reference
void add8(const int &other) { value += other; } // passing by const reference
void add9(int *other) { value += *other; } // passing py pointer
void add10(const int *other) { value += *other; } // passing by const pointer
Example1 self1() { return *this; } // return by value
Example1 &self2() { return *this; } // return by reference
const Example1 &self3() { return *this; } // return by const reference
Example1 *self4() { return this; } // return by pointer
const Example1 *self5() { return this; } // return by const pointer
int internal1() { return value; } // return by value
int &internal2() { return value; } // return by reference
const int &internal3() { return value; } // return by const reference
int *internal4() { return &value; } // return by pointer
const int *internal5() { return &value; } // return by const pointer
int value = 0;
};
void init_ex1(py::module &m) {
py::class_<Example1>(m, "Example1")
.def(py::init<>())
.def(py::init<int>())
.def(py::init<const Example1&>())
.def("add1", &Example1::add1)
.def("add2", &Example1::add2)
.def("add3", &Example1::add3)
.def("add4", &Example1::add4)
.def("add5", &Example1::add5)
.def("add6", &Example1::add6)
.def("add7", &Example1::add7)
.def("add8", &Example1::add8)
.def("add9", &Example1::add9)
.def("add10", &Example1::add10)
.def("self1", &Example1::self1)
.def("self2", &Example1::self2)
.def("self3", &Example1::self3)
.def("self4", &Example1::self4)
.def("self5", &Example1::self5)
.def("internal1", &Example1::internal1)
.def("internal2", &Example1::internal2)
.def("internal3", &Example1::internal3)
.def("internal4", &Example1::internal4)
.def("internal5", &Example1::internal5)
.def("__str__", &Example1::toString)
.def_readwrite("value", &Example1::value);
}

36
example/example1.py Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env python3
import sys
sys.path.append('.')
from example import Example1
instance1 = Example1()
instance2 = Example1(32)
instance1.add1(instance2)
instance1.add2(instance2)
instance1.add3(instance2)
instance1.add4(instance2)
instance1.add5(instance2)
instance1.add6(32)
instance1.add7(32)
instance1.add8(32)
instance1.add9(32)
instance1.add10(32)
print("Instance 1: " + str(instance1))
print("Instance 2: " + str(instance2))
print(instance1.self1())
print(instance1.self2())
print(instance1.self3())
print(instance1.self4())
print(instance1.self5())
print(instance1.internal1())
print(instance1.internal2())
print(instance1.internal3())
print(instance1.internal4())
print(instance1.internal5())
print("Instance 1, direct access = %i" % instance1.value)
instance1.value = 100
print("Instance 1: " + str(instance1))

28
example/example1.ref Normal file
View File

@ -0,0 +1,28 @@
Called Example1 default constructor..
Called Example1 constructor with value 32..
Called Example1 copy constructor with value 32..
Called Example1 move constructor with value 32..
Called Example1 move constructor with value 32..
Called Example1 destructor (32)
Called Example1 destructor (0)
Called Example1 destructor (0)
Called Example1 copy constructor with value 320..
Called Example1 copy constructor with value 320..
Called Example1 destructor (320)
Called Example1 destructor (320)
Instance 1: Example1[value=320]
Instance 2: Example1[value=32]
Example1[value=320]
Example1[value=320]
Example1[value=320]
Example1[value=320]
Example1[value=320]
320
320
320
320
320
Instance 1, direct access = 320
Instance 1: Example1[value=100]
Called Example1 destructor (32)
Called Example1 destructor (100)

117
example/example2.cpp Normal file
View File

@ -0,0 +1,117 @@
/*
example/example2.cpp2 -- Example 2: singleton design pattern, static
functions and variables, passing and interacting with Python types
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
class Example2 {
public:
static Example2 *new_instance() {
return new Example2();
}
~Example2() {
std::cout << "Destructing Example2" << std::endl;
}
/* Create and return a Python dictionary */
py::dict get_dict() {
py::dict dict;
dict[py::str("key")] = py::str("value");
return dict;
}
/* Create and return a C++ dictionary */
std::map<std::string, std::string> get_dict_2() {
std::map<std::string, std::string> result;
result["key"] = "value";
return result;
}
/* Create, manipulate, and return a Python list */
py::list get_list() {
py::list list;
list.append(py::str("value"));
cout << "Entry at positon 0: " << py::object(list[0]) << endl;
list[0] = py::str("overwritten");
return list;
}
/* C++ STL data types are automatically casted */
std::vector<std::string> get_list_2() {
std::vector<std::string> list;
list.push_back("value");
return list;
}
/* Easily iterate over a dictionary using a C++11 range-based for loop */
void print_dict(py::dict dict) {
for (auto item : dict)
std::cout << "key: " << item.first << ", value=" << item.second << std::endl;
}
/* STL data types are automatically casted from Python */
void print_dict_2(const std::map<std::string, std::string> &dict) {
for (auto item : dict)
std::cout << "key: " << item.first << ", value=" << item.second << std::endl;
}
/* Easily iterate over a list using a C++11 range-based for loop */
void print_list(py::list list) {
int index = 0;
for (auto item : list)
std::cout << "list item " << index++ << ": " << item << std::endl;
}
/* STL data types are automatically casted from Python */
void print_list_2(std::vector<std::string> &list) {
int index = 0;
for (auto item : list)
std::cout << "list item " << index++ << ": " << item << std::endl;
}
/* pybind automatically translates between C++11 and Python tuples */
std::pair<std::string, bool> pair_passthrough(std::pair<bool, std::string> input) {
return std::make_pair(input.second, input.first);
}
/* pybind automatically translates between C++11 and Python tuples */
std::tuple<int, std::string, bool> tuple_passthrough(std::tuple<bool, std::string, int> input) {
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
}
void throw_exception() {
throw std::runtime_error("This exception was intentionally thrown.");
}
static int value;
static const int value2;
};
int Example2::value = 0;
const int Example2::value2 = 5;
void init_ex2(py::module &m) {
/* No constructor is explicitly defined below. An exception is raised when
trying to construct it directly from Python */
py::class_<Example2>(m, "Example2")
.def("get_dict", &Example2::get_dict, "Return a Python dictionary")
.def("get_dict_2", &Example2::get_dict_2, "Return a C++ dictionary")
.def("get_list", &Example2::get_list, "Return a Python list")
.def("get_list_2", &Example2::get_list_2, "Return a C++ list")
.def("print_dict", &Example2::print_dict, "Print entries of a Python dictionary")
.def("print_dict_2", &Example2::print_dict_2, "Print entries of a C++ dictionary")
.def("print_list", &Example2::print_list, "Print entries of a Python list")
.def("print_list_2", &Example2::print_list_2, "Print entries of a C++ list")
.def("pair_passthrough", &Example2::pair_passthrough, "Return a pair in reversed order")
.def("tuple_passthrough", &Example2::tuple_passthrough, "Return a triple in reversed order")
.def("throw_exception", &Example2::throw_exception, "Throw an exception")
.def_static("new_instance", &Example2::new_instance, "Return an instance")
.def_readwrite_static("value", &Example2::value, "Static value member")
.def_readonly_static("value2", &Example2::value2, "Static value member (readonly)");
}

47
example/example2.py Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python3
import sys, pydoc
sys.path.append('.')
from example import Example2
Example2.value = 15
print(Example2.value)
print(Example2.value2)
try:
Example2()
except Exception as e:
print(e)
try:
Example2.value2 = 15
except Exception as e:
print(e)
instance = Example2.new_instance()
dict_result = instance.get_dict()
dict_result['key2'] = 'value2'
instance.print_dict(dict_result)
dict_result = instance.get_dict_2()
dict_result['key2'] = 'value2'
instance.print_dict_2(dict_result)
list_result = instance.get_list()
list_result.append('value2')
instance.print_list(list_result)
list_result = instance.get_list_2()
list_result.append('value2')
instance.print_list_2(list_result)
try:
instance.throw_exception()
except Exception as e:
print(e)
print(instance.pair_passthrough((True, "test")))
print(instance.tuple_passthrough((True, "test", 5)))
print(pydoc.render_doc(Example2, "Help on %s"))

91
example/example2.ref Normal file
View File

@ -0,0 +1,91 @@
key: key, value=value
key: key2, value=value2
key: key, value=value
key: key2, value=value2
Entry at positon 0: value
list item 0: overwritten
list item 1: value2
list item 0: value
list item 1: value2
15
5
example.Example2: No constructor defined!
can't set attribute
This exception was intentionally thrown.
('test', True)
(5, 'test', True)
Help on class Example2 in module example
class EExxaammppllee22(builtins.object)
| Methods defined here:
|
| ____iinniitt____(self, /, *args, **kwargs)
| Initialize self. See help(type(self)) for accurate signature.
|
| ____nneeww____ = <built-in method __new__ of example.Example2_meta object>
| ggeett__ddiicctt(...) from builtins.PyCapsule
| Signature : get_dict(Example2) -> dict
|
| Return a Python dictionary
|
| ggeett__ddiicctt__22(...) from builtins.PyCapsule
| Signature : get_dict_2(Example2) -> dict<str, str>
|
| Return a C++ dictionary
|
| ggeett__lliisstt(...) from builtins.PyCapsule
| Signature : get_list(Example2) -> list
|
| Return a Python list
|
| ggeett__lliisstt__22(...) from builtins.PyCapsule
| Signature : get_list_2(Example2) -> list<str>
|
| Return a C++ list
|
| nneeww__iinnssttaannccee(...) from builtins.PyCapsule
| Signature : new_instance() -> Example2
|
| Return an instance
|
| ppaaiirr__ppaasssstthhrroouugghh(...) from builtins.PyCapsule
| Signature : pair_passthrough(Example2, (bool, str)) -> (str, bool)
|
| Return a pair in reversed order
|
| pprriinntt__ddiicctt(...) from builtins.PyCapsule
| Signature : print_dict(Example2, dict) -> None
|
| Print entries of a Python dictionary
|
| pprriinntt__ddiicctt__22(...) from builtins.PyCapsule
| Signature : print_dict_2(Example2, dict<str, str>) -> None
|
| Print entries of a C++ dictionary
|
| pprriinntt__lliisstt(...) from builtins.PyCapsule
| Signature : print_list(Example2, list) -> None
|
| Print entries of a Python list
|
| pprriinntt__lliisstt__22(...) from builtins.PyCapsule
| Signature : print_list_2(Example2, list<str>) -> None
|
| Print entries of a C++ list
|
| tthhrrooww__eexxcceeppttiioonn(...) from builtins.PyCapsule
| Signature : throw_exception(Example2) -> None
|
| Throw an exception
|
| ttuuppllee__ppaasssstthhrroouugghh(...) from builtins.PyCapsule
| Signature : tuple_passthrough(Example2, (bool, str, int32_t)) -> (int32_t, str, bool)
|
| Return a triple in reversed order
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| ____iinnssttaannccee__ssiizzee____ = 1
Destructing Example2

65
example/example3.cpp Normal file
View File

@ -0,0 +1,65 @@
/*
example/example3.cpp -- Example 3: operator overloading
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
#include <pybind/operators.h>
class Vector2 {
public:
Vector2(float x, float y) : x(x), y(y) { std::cout << "Value constructor" << std::endl; }
Vector2(const Vector2 &v) : x(v.x), y(v.y) { std::cout << "Copy constructor" << std::endl; }
Vector2(Vector2 &&v) : x(v.x), y(v.y) { std::cout << "Move constructor" << std::endl; v.x = v.y = 0; }
~Vector2() { std::cout << "Destructor." << std::endl; }
std::string toString() const {
return "[" + std::to_string(x) + ", " + std::to_string(y) + "]";
}
void operator=(const Vector2 &v) {
cout << "Assignment operator" << endl;
x = v.x;
y = v.y;
}
void operator=(Vector2 &&v) {
cout << "Move assignment operator" << endl;
x = v.x; y = v.y; v.x = v.y = 0;
}
Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); }
Vector2 operator-(const Vector2 &v) const { return Vector2(x - v.x, y - v.y); }
Vector2 operator-(float value) const { return Vector2(x - value, y - value); }
Vector2 operator+(float value) const { return Vector2(x + value, y + value); }
Vector2 operator*(float value) const { return Vector2(x * value, y * value); }
Vector2 operator/(float value) const { return Vector2(x / value, y / value); }
Vector2& operator+=(const Vector2 &v) { x += v.x; y += v.y; return *this; }
Vector2& operator-=(const Vector2 &v) { x -= v.x; y -= v.y; return *this; }
Vector2& operator*=(float v) { x *= v; y *= v; return *this; }
Vector2& operator/=(float v) { x /= v; y /= v; return *this; }
private:
float x, y;
};
void init_ex3(py::module &m) {
py::class_<Vector2>(m, "Vector2")
.def(py::init<float, float>())
.def(py::self + py::self)
.def(py::self + float())
.def(py::self - py::self)
.def(py::self - float())
.def(py::self * float())
.def(py::self / float())
.def(py::self += py::self)
.def(py::self -= py::self)
.def(py::self *= float())
.def(py::self /= float())
.def("__str__", &Vector2::toString);
m.attr("Vector") = m.attr("Vector2");
}

22
example/example3.py Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python3
import sys
sys.path.append('.')
from example import Vector2, Vector
v1 = Vector2(1, 2)
v2 = Vector(3, -1)
print("v1 = " + str(v1))
print("v2 = " + str(v2))
print("v1+v2 = " + str(v1+v2))
print("v1-v2 = " + str(v1-v2))
print("v1-8 = " + str(v1-8))
print("v1+8 = " + str(v1+8))
print("v1*8 = " + str(v1*8))
print("v1/8 = " + str(v1/8))
v1 += v2
v1 *= 2
print("(v1+v2)*2 = " + str(v1))

37
example/example3.ref Normal file
View File

@ -0,0 +1,37 @@
Value constructor
Value constructor
Value constructor
Copy constructor
Destructor.
Destructor.
Value constructor
Copy constructor
Destructor.
Destructor.
Value constructor
Copy constructor
Destructor.
Destructor.
Value constructor
Copy constructor
Destructor.
Destructor.
Value constructor
Copy constructor
Destructor.
Destructor.
Value constructor
Copy constructor
Destructor.
Destructor.
v1 = [1.000000, 2.000000]
v2 = [3.000000, -1.000000]
v1+v2 = [4.000000, 1.000000]
v1-v2 = [-2.000000, 3.000000]
v1-8 = [-7.000000, -6.000000]
v1+8 = [9.000000, 10.000000]
v1*8 = [8.000000, 16.000000]
v1/8 = [0.125000, 0.250000]
(v1+v2)*2 = [8.000000, 2.000000]
Destructor.
Destructor.

60
example/example4.cpp Normal file
View File

@ -0,0 +1,60 @@
/*
example/example4.cpp -- Example 4: global constants and functions, enumerations
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
enum EMyEnumeration {
EFirstEntry = 1,
ESecondEntry
};
class Example4 {
public:
enum EMode {
EFirstMode = 1,
ESecondMode
};
static void test_function(EMode mode) {
std::cout << "Example4::test_function(enum=" << mode << ")" << std::endl;
}
};
bool test_function1() {
std::cout << "test_function()" << std::endl;
return false;
}
float test_function2(int i) {
std::cout << "test_function(" << i << ")" << std::endl;
return i / 2.f;
}
void test_function3(EMyEnumeration k) {
std::cout << "test_function(enum=" << k << ")" << std::endl;
}
void init_ex4(py::module &m) {
m.def("test_function", &test_function1);
m.def("test_function", &test_function2);
m.def("test_function", &test_function3);
m.attr("some_constant") = py::int_(14);
py::enum_<EMyEnumeration>(m, "EMyEnumeration")
.value("EFirstEntry", EFirstEntry)
.value("ESecondEntry", ESecondEntry)
.export_values();
py::class_<Example4> ex4_class(m, "Example4");
ex4_class.def_static("test_function", &Example4::test_function);
py::enum_<Example4::EMode>(ex4_class, "EMode")
.value("EFirstMode", Example4::EFirstMode)
.value("ESecondMode", Example4::ESecondMode)
.export_values();
}

24
example/example4.py Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
import sys
sys.path.append('.')
from example import test_function
from example import some_constant
from example import EMyEnumeration
from example import EFirstEntry
from example import Example4
print(EMyEnumeration)
print(EMyEnumeration.EFirstEntry)
print(EMyEnumeration.ESecondEntry)
print(EFirstEntry)
print(test_function())
print(test_function(7))
print(test_function(EMyEnumeration.EFirstEntry))
print(test_function(EMyEnumeration.ESecondEntry))
print(Example4.EMode)
print(Example4.EMode.EFirstMode)
print(Example4.EFirstMode)
Example4.test_function(Example4.EFirstMode)

16
example/example4.ref Normal file
View File

@ -0,0 +1,16 @@
test_function()
test_function(7)
test_function(enum=1)
test_function(enum=2)
Example4::test_function(enum=1)
<class 'example.EMyEnumeration'>
EMyEnumeration.EFirstEntry
EMyEnumeration.ESecondEntry
EMyEnumeration.EFirstEntry
False
3.5
None
None
<class 'example.Example4.EMode'>
EMode.EFirstMode
EMode.EFirstMode

95
example/example5.cpp Normal file
View File

@ -0,0 +1,95 @@
/*
example/example5.cpp -- Example 5: inheritance, callbacks, acquiring
and releasing the global interpreter lock
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
class Pet {
public:
Pet(const std::string &name, const std::string &species)
: m_name(name), m_species(species) {}
std::string name() const { return m_name; }
std::string species() const { return m_species; }
private:
std::string m_name;
std::string m_species;
};
class Dog : public Pet {
public:
Dog(const std::string &name) : Pet(name, "dog") {}
void bark() const { std::cout << "Woof!" << std::endl; }
};
void pet_print(const Pet &pet) {
std::cout << pet.name() + " is a " + pet.species() << std::endl;
}
void dog_bark(const Dog &dog) {
dog.bark();
}
class Example5 {
public:
Example5(py::handle self, int state)
: self(self), state(state) {
cout << "Constructing Example5.." << endl;
}
~Example5() {
cout << "Destructing Example5.." << endl;
}
void callback(int value) {
py::gil_scoped_acquire gil;
cout << "In Example5::callback() " << endl;
py::object method = self.attr("callback");
method.call(state, value);
}
private:
py::handle self;
int state;
};
bool test_callback1(py::object func) {
func.call();
return false;
}
int test_callback2(py::object func) {
py::object result = func.call("Hello", true, 5);
return result.cast<int>();
}
void test_callback3(Example5 *ex, int value) {
py::gil_scoped_release gil;
ex->callback(value);
}
void init_ex5(py::module &m) {
py::class_<Pet> pet_class(m, "Pet");
pet_class
.def(py::init<std::string, std::string>())
.def("name", &Pet::name)
.def("species", &Pet::species);
py::class_<Dog>(m, "Dog", pet_class)
.def(py::init<std::string>());
m.def("pet_print", pet_print);
m.def("dog_bark", dog_bark);
m.def("test_callback1", &test_callback1);
m.def("test_callback2", &test_callback2);
m.def("test_callback3", &test_callback3);
py::class_<Example5>(m, "Example5")
.def(py::init<py::object, int>());
}

45
example/example5.py Executable file
View File

@ -0,0 +1,45 @@
#!/usr/bin/env python3
import sys
sys.path.append('.')
from example import Pet
from example import Dog
from example import dog_bark
from example import pet_print
polly = Pet('Polly', 'parrot')
molly = Dog('Molly')
print(polly.name() + " is a " + polly.species())
pet_print(polly)
print(molly.name() + " is a " + molly.species())
pet_print(molly)
dog_bark(molly)
try:
dog_bark(polly)
except Exception as e:
print('The following error is expected: ' + str(e))
from example import test_callback1
from example import test_callback2
from example import test_callback3
from example import Example5
def func1():
print('Callback function 1 called!')
def func2(a, b, c):
print('Callback function 2 called : ' + str(a) + ", " + str(b) + ", "+ str(c))
return c
class MyCallback(Example5):
def __init__(self, value):
Example5.__init__(self, self, value)
def callback(self, value1, value2):
print('got callback: %i %i' % (value1, value2))
print(test_callback1(func1))
print(test_callback2(func2))
callback = MyCallback(3)
test_callback3(callback, 4)

16
example/example5.ref Normal file
View File

@ -0,0 +1,16 @@
Polly is a parrot
Molly is a dog
Woof!
Constructing Example5..
In Example5::callback()
Polly is a parrot
Molly is a dog
The following error is expected: Incompatible function arguments. The following argument types are supported:
1. dog_bark(Dog) -> None
Callback function 1 called!
False
Callback function 2 called : Hello, True, 5
5
got callback: 3 4
Destructing Example5..

175
example/example6.cpp Normal file
View File

@ -0,0 +1,175 @@
/*
example/example6.cpp -- Example 6: supporting Pythons' sequence
protocol, iterators, etc.
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
#include <pybind/operators.h>
class Sequence {
public:
Sequence(size_t size) : m_size(size) {
std::cout << "Value constructor: Creating a sequence with " << m_size << " entries" << std::endl;
m_data = new float[size];
memset(m_data, 0, sizeof(float) * size);
}
Sequence(const std::vector<float> &value) : m_size(value.size()) {
std::cout << "Value constructor: Creating a sequence with " << m_size << " entries" << std::endl;
m_data = new float[m_size];
memcpy(m_data, &value[0], sizeof(float) * m_size);
}
Sequence(const Sequence &s) : m_size(s.m_size) {
std::cout << "Copy constructor: Creating a sequence with " << m_size << " entries" << std::endl;
m_data = new float[m_size];
memcpy(m_data, s.m_data, sizeof(float)*m_size);
}
Sequence(Sequence &&s) : m_size(s.m_size), m_data(s.m_data) {
std::cout << "Move constructor: Creating a sequence with " << m_size << " entries" << std::endl;
s.m_size = 0;
s.m_data = nullptr;
}
~Sequence() {
std::cout << "Freeing a sequence with " << m_size << " entries" << std::endl;
delete[] m_data;
}
Sequence &operator=(const Sequence &s) {
std::cout << "Assignment operator: Creating a sequence with " << s.m_size << " entries" << std::endl;
delete[] m_data;
m_size = s.m_size;
m_data = new float[m_size];
memcpy(m_data, s.m_data, sizeof(float)*m_size);
return *this;
}
Sequence &operator=(Sequence &&s) {
std::cout << "Move assignment operator: Creating a sequence with " << s.m_size << " entries" << std::endl;
if (&s != this) {
delete[] m_data;
m_size = s.m_size;
m_data = s.m_data;
s.m_size = 0;
s.m_data = nullptr;
}
return *this;
}
bool operator==(const Sequence &s) const {
if (m_size != s.size())
return false;
for (size_t i=0; i<m_size; ++i)
if (m_data[i] != s[i])
return false;
return true;
}
bool operator!=(const Sequence &s) const {
return !operator==(s);
}
float operator[](size_t index) const {
return m_data[index];
}
float &operator[](size_t index) {
return m_data[index];
}
bool contains(float v) const {
for (size_t i=0; i<m_size; ++i)
if (v == m_data[i])
return true;
return false;
}
Sequence reversed() const {
Sequence result(m_size);
for (size_t i=0; i<m_size; ++i)
result[m_size-i-1] = m_data[i];
return result;
}
size_t size() const { return m_size; }
private:
size_t m_size;
float *m_data;
};
namespace {
// Special iterator data structure for python
struct PySequenceIterator {
PySequenceIterator(const Sequence &seq, py::object ref) : seq(seq), ref(ref) { }
float next() {
if (index == seq.size())
throw py::stop_iteration();
return seq[index++];
}
const Sequence &seq;
py::object ref; // keep a reference
size_t index = 0;
};
};
void init_ex6(py::module &m) {
py::class_<Sequence> seq(m, "Sequence");
seq.def(py::init<size_t>())
.def(py::init<const std::vector<float>&>())
/// Bare bones interface
.def("__getitem__", [](const Sequence &s, size_t i) {
if (i >= s.size())
throw py::index_error();
return s[i];
})
.def("__setitem__", [](Sequence &s, size_t i, float v) {
if (i >= s.size())
throw py::index_error();
s[i] = v;
})
.def("__len__", &Sequence::size)
/// Optional sequence protocol operations
.def("__iter__", [](py::object s) { return PySequenceIterator(s.cast<const Sequence &>(), s); })
.def("__contains__", [](const Sequence &s, float v) { return s.contains(v); })
.def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); })
/// Slicing protocol (optional)
.def("__getitem__", [](const Sequence &s, py::slice slice) -> Sequence* {
py::ssize_t start, stop, step, slicelength;
if (!slice.compute(s.size(), &start, &stop, &step, &slicelength))
throw py::error_already_set();
Sequence *seq = new Sequence(slicelength);
for (int i=0; i<slicelength; ++i) {
(*seq)[i] = s[start]; start += step;
}
return seq;
})
.def("__setitem__", [](Sequence &s, py::slice slice, const Sequence &value) {
py::ssize_t start, stop, step, slicelength;
if (!slice.compute(s.size(), &start, &stop, &step, &slicelength))
throw py::error_already_set();
if ((size_t) slicelength != value.size())
throw std::runtime_error("Left and right hand size of slice assignment have different sizes!");
for (int i=0; i<slicelength; ++i) {
s[start] = value[i]; start += step;
}
})
/// Comparisons
.def(py::self == py::self)
.def(py::self != py::self);
// Could also define py::self + py::self for concatenation, etc.
py::class_<PySequenceIterator>(seq, "Iterator")
.def("__iter__", [](PySequenceIterator &it) -> PySequenceIterator& { return it; })
.def("__next__", &PySequenceIterator::next);
}

29
example/example6.py Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/env python3
import sys
sys.path.append('.')
from example import Sequence
s = Sequence(5)
print("s = " + str(s))
print("len(s) = " + str(len(s)))
print("s[0], s[3] = %f %f" % (s[0], s[3]))
print('12.34 in s: ' + str(12.34 in s))
s[0], s[3] = 12.34, 56.78
print('12.34 in s: ' + str(12.34 in s))
print("s[0], s[3] = %f %f" % (s[0], s[3]))
rev = reversed(s)
rev2 = s[::-1]
print("rev[0], rev[1], rev[2], rev[3], rev[4] = %f %f %f %f %f" % (rev[0], rev[1], rev[2], rev[3], rev[4]))
for i in rev:
print(i, end=' ')
print('')
for i in rev2:
print(i, end=' ')
print('')
print(rev == rev2)
rev[0::2] = Sequence([2.0, 2.0, 2.0])
for i in rev:
print(i, end=' ')
print('')

18
example/example6.ref Normal file
View File

@ -0,0 +1,18 @@
Value constructor: Creating a sequence with 5 entries
Value constructor: Creating a sequence with 5 entries
Copy constructor: Creating a sequence with 5 entries
Freeing a sequence with 5 entries
s = <example.Sequence object at 0x100380fb0>
len(s) = 5
s[0], s[3] = 0.000000 0.000000
12.34 in s: False
12.34 in s: True
s[0], s[3] = 12.340000 56.779999
rev[0], rev[1], rev[2], rev[3], rev[4] = 0.000000 56.779999 0.000000 0.000000 12.340000
12.34000015258789
0.0
0.0
56.779998779296875
0.0
Freeing a sequence with 5 entries
Freeing a sequence with 5 entries

115
example/example7.cpp Normal file
View File

@ -0,0 +1,115 @@
/*
example/example7.cpp -- Example 7: supporting Pythons' buffer protocol
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
class Matrix {
public:
Matrix(size_t rows, size_t cols) : m_rows(rows), m_cols(cols) {
std::cout << "Value constructor: Creating a " << rows << "x" << cols << " matrix " << std::endl;
m_data = new float[rows*cols];
memset(m_data, 0, sizeof(float) * rows * cols);
}
Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
std::cout << "Copy constructor: Creating a " << m_rows << "x" << m_cols << " matrix " << std::endl;
m_data = new float[m_rows * m_cols];
memcpy(m_data, s.m_data, sizeof(float) * m_rows * m_cols);
}
Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
std::cout << "Move constructor: Creating a " << m_rows << "x" << m_cols << " matrix " << std::endl;
s.m_rows = 0;
s.m_cols = 0;
s.m_data = nullptr;
}
~Matrix() {
std::cout << "Freeing a " << m_rows << "x" << m_cols << " matrix " << std::endl;
delete[] m_data;
}
Matrix &operator=(const Matrix &s) {
std::cout << "Assignment operator : Creating a " << s.m_rows << "x" << s.m_cols << " matrix " << std::endl;
delete[] m_data;
m_rows = s.m_rows;
m_cols = s.m_cols;
m_data = new float[m_rows * m_cols];
memcpy(m_data, s.m_data, sizeof(float) * m_rows * m_cols);
return *this;
}
Matrix &operator=(Matrix &&s) {
std::cout << "Move assignment operator : Creating a " << s.m_rows << "x" << s.m_cols << " matrix " << std::endl;
if (&s != this) {
delete[] m_data;
m_rows = s.m_rows; m_cols = s.m_cols; m_data = s.m_data;
s.m_rows = 0; s.m_cols = 0; s.m_data = nullptr;
}
return *this;
}
float operator()(size_t i, size_t j) const {
return m_data[i*m_cols + j];
}
float &operator()(size_t i, size_t j) {
return m_data[i*m_cols + j];
}
float *data() { return m_data; }
size_t rows() const { return m_rows; }
size_t cols() const { return m_cols; }
private:
size_t m_rows;
size_t m_cols;
float *m_data;
};
void init_ex7(py::module &m) {
py::class_<Matrix> mtx(m, "Matrix");
mtx.def(py::init<size_t, size_t>())
/// Construct from a buffer
.def("__init__", [](Matrix &v, py::buffer b) {
py::buffer_info info = b.request();
if (info.format != py::format_descriptor<float>::value() || info.ndim != 2)
throw std::runtime_error("Incompatible buffer format!");
new (&v) Matrix(info.shape[0], info.shape[1]);
memcpy(v.data(), info.ptr, sizeof(float) * v.rows() * v.cols());
})
.def("rows", &Matrix::rows)
.def("cols", &Matrix::cols)
/// Bare bones interface
.def("__getitem__", [](const Matrix &m, std::pair<size_t, size_t> i) {
if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error();
return m(i.first, i.second);
})
.def("__setitem__", [](Matrix &m, std::pair<size_t, size_t> i, float v) {
if (i.first >= m.rows() || i.second >= m.cols())
throw py::index_error();
m(i.first, i.second) = v;
})
/// Provide buffer access
.def_buffer([](Matrix &m) -> py::buffer_info {
return py::buffer_info(
m.data(), /* Pointer to buffer */
sizeof(float), /* Size of one scalar */
py::format_descriptor<float>::value(), /* Python struct-style format descriptor */
2, /* Number of dimensions */
{ m.rows(), m.cols() }, /* Buffer dimensions */
{ sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
sizeof(float) }
);
});
}

26
example/example7.py Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env python3
import sys
sys.path.append('.')
from example import Matrix
import numpy as np
m = Matrix(5, 5)
print(m[2, 3])
m[2, 3] = 4
print(m[2, 3])
m2 = np.array(m, copy=False)
print(m2)
print(m2[2, 3])
m2[2, 3] = 5
print(m[2, 3])
m3 = np.array([[1,2,3],[4,5,6]]).astype(np.float32)
print(m3)
m4 = Matrix(m3)
for i in range(m4.rows()):
for j in range(m4.cols()):
print(m4[i, j], end = ' ')
print()

11
example/example7.ref Normal file
View File

@ -0,0 +1,11 @@
Value constructor: Creating a 5x5 matrix
0.0
4.0
[[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 4. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]]
4.0
5.0
Freeing a 5x5 matrix

76
example/example8.cpp Normal file
View File

@ -0,0 +1,76 @@
/*
example/example8.cpp -- Example 8: binding classes with
custom reference counting, implicit conversions between types
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
#include "object.h"
/// Object subclass
class MyObject : public Object {
public:
MyObject(int value) : value(value) {
std::cout << toString() << " constructor" << std::endl;
}
std::string toString() const {
return "MyObject[" + std::to_string(value) + "]";
}
protected:
virtual ~MyObject() {
std::cout << toString() << " destructor" << std::endl;
}
private:
int value;
};
/// Make pybind aware of the ref-counted wrapper type
namespace pybind { namespace detail {
template <typename T> class type_caster<ref<T>>
: public type_caster_holder<T, ref<T>> { };
}}
Object *make_object_1() { return new MyObject(1); }
ref<Object> make_object_2() { return new MyObject(2); }
MyObject *make_myobject_4() { return new MyObject(4); }
ref<MyObject> make_myobject_5() { return new MyObject(5); }
void print_object_1(const Object *obj) { std::cout << obj->toString() << std::endl; }
void print_object_2(ref<Object> obj) { std::cout << obj->toString() << std::endl; }
void print_object_3(const ref<Object> &obj) { std::cout << obj->toString() << std::endl; }
void print_object_4(const ref<Object> *obj) { std::cout << (*obj)->toString() << std::endl; }
void print_myobject_1(const MyObject *obj) { std::cout << obj->toString() << std::endl; }
void print_myobject_2(ref<MyObject> obj) { std::cout << obj->toString() << std::endl; }
void print_myobject_3(const ref<MyObject> &obj) { std::cout << obj->toString() << std::endl; }
void print_myobject_4(const ref<MyObject> *obj) { std::cout << (*obj)->toString() << std::endl; }
void init_ex8(py::module &m) {
py::class_<Object, ref<Object>> obj(m, "Object");
obj.def("getRefCount", &Object::getRefCount);
py::class_<MyObject, ref<MyObject>>(m, "MyObject", obj)
.def(py::init<int>());
m.def("make_object_1", &make_object_1);
m.def("make_object_2", &make_object_2);
m.def("make_myobject_4", &make_myobject_4);
m.def("make_myobject_5", &make_myobject_5);
m.def("print_object_1", &print_object_1);
m.def("print_object_2", &print_object_2);
m.def("print_object_3", &print_object_3);
m.def("print_object_4", &print_object_4);
m.def("print_myobject_1", &print_myobject_1);
m.def("print_myobject_2", &print_myobject_2);
m.def("print_myobject_3", &print_myobject_3);
m.def("print_myobject_4", &print_myobject_4);
py::implicitly_convertible<py::int_, MyObject>();
}

36
example/example8.py Normal file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env python3
import sys
sys.path.append('.')
from example import MyObject
from example import make_object_1
from example import make_object_2
from example import make_myobject_4
from example import make_myobject_5
from example import print_object_1
from example import print_object_2
from example import print_object_3
from example import print_object_4
from example import print_myobject_1
from example import print_myobject_2
from example import print_myobject_3
from example import print_myobject_4
for o in [make_object_1(), make_object_2(), MyObject(3)]:
print("Reference count = %i" % o.getRefCount())
print_object_1(o)
print_object_2(o)
print_object_3(o)
print_object_4(o)
for o in [make_myobject_4(), make_myobject_5(), MyObject(6), 7]:
print(o)
if not isinstance(o, int):
print_object_1(o)
print_object_2(o)
print_object_3(o)
print_object_4(o)
print_myobject_1(o)
print_myobject_2(o)
print_myobject_3(o)
print_myobject_4(o)

53
example/example9.cpp Normal file
View File

@ -0,0 +1,53 @@
/*
example/example9.cpp -- Example 9: nested modules
and internal references
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "example.h"
void submodule_func() {
std::cout << "submodule_func()" << std::endl;
}
class A {
public:
A(int v) : v(v) { std::cout << "A constructor" << std::endl; }
~A() { std::cout << "A destructor" << std::endl; }
A(const A&) { std::cout << "A copy constructor" << std::endl; }
std::string toString() { return "A[" + std::to_string(v) + "]"; }
private:
int v;
};
class B {
public:
B() { std::cout << "B constructor" << std::endl; }
~B() { std::cout << "B destructor" << std::endl; }
B(const B&) { std::cout << "B copy constructor" << std::endl; }
A &get_a1() { return a1; }
A &get_a2() { return a2; }
A a1{1};
A a2{2};
};
void init_ex9(py::module &m) {
py::module m_sub = m.def_submodule("submodule");
m_sub.def("submodule_func", &submodule_func);
py::class_<A>(m_sub, "A")
.def(py::init<int>())
.def("__repr__", &A::toString);
py::class_<B>(m_sub, "B")
.def(py::init<>())
.def("get_a1", &B::get_a1, "Return the internal A 1", py::return_value_policy::reference_internal)
.def("get_a2", &B::get_a2, "Return the internal A 2", py::return_value_policy::reference_internal)
.def_readwrite("a1", &B::a1) // def_readonly uses an internal reference return policy by default
.def_readwrite("a2", &B::a2);
}

27
example/example9.py Normal file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env python3
import sys
sys.path.append('.')
import example
print(example.__name__)
print(example.submodule.__name__)
from example.submodule import *
submodule_func()
b = B()
print(b.get_a1())
print(b.a1)
print(b.get_a2())
print(b.a2)
b.a1 = A(42)
b.a2 = A(43)
print(b.get_a1())
print(b.a1)
print(b.get_a2())
print(b.a2)

160
example/object.h Normal file
View File

@ -0,0 +1,160 @@
#if !defined(__OBJECT_H)
#define __OBJECT_H
#include <atomic>
/// Reference counted object base class
class Object {
public:
/// Default constructor
Object() { }
/// Copy constructor
Object(const Object &) : m_refCount(0) {}
/// Return the current reference count
int getRefCount() const { return m_refCount; };
/// Increase the object's reference count by one
void incRef() const { ++m_refCount; }
/** \brief Decrease the reference count of
* the object and possibly deallocate it.
*
* The object will automatically be deallocated once
* the reference count reaches zero.
*/
void decRef(bool dealloc = true) const {
--m_refCount;
if (m_refCount == 0 && dealloc)
delete this;
else if (m_refCount < 0)
throw std::runtime_error("Internal error: reference count < 0!");
}
virtual std::string toString() const = 0;
protected:
/** \brief Virtual protected deconstructor.
* (Will only be called by \ref ref)
*/
virtual ~Object() { }
private:
mutable std::atomic<int> m_refCount { 0 };
};
/**
* \brief Reference counting helper
*
* The \a ref refeference template is a simple wrapper to store a
* pointer to an object. It takes care of increasing and decreasing
* the reference count of the object. When the last reference goes
* out of scope, the associated object will be deallocated.
*
* \ingroup libcore
*/
template <typename T> class ref {
public:
/// Create a nullptr reference
ref() : m_ptr(nullptr) { std::cout << "Created empty ref" << std::endl; }
/// Construct a reference from a pointer
ref(T *ptr) : m_ptr(ptr) {
std::cout << "Initialized ref from pointer " << ptr<< std::endl;
if (m_ptr) ((Object *) m_ptr)->incRef();
}
/// Copy constructor
ref(const ref &r) : m_ptr(r.m_ptr) {
std::cout << "Initialized ref from ref " << r.m_ptr << std::endl;
if (m_ptr)
((Object *) m_ptr)->incRef();
}
/// Move constructor
ref(ref &&r) : m_ptr(r.m_ptr) {
std::cout << "Initialized ref with move from ref " << r.m_ptr << std::endl;
r.m_ptr = nullptr;
}
/// Destroy this reference
~ref() {
std::cout << "Destructing ref " << m_ptr << std::endl;
if (m_ptr)
((Object *) m_ptr)->decRef();
}
/// Move another reference into the current one
ref& operator=(ref&& r) {
std::cout << "Move-assigning ref " << r.m_ptr << std::endl;
if (*this == r)
return *this;
if (m_ptr)
((Object *) m_ptr)->decRef();
m_ptr = r.m_ptr;
r.m_ptr = nullptr;
return *this;
}
/// Overwrite this reference with another reference
ref& operator=(const ref& r) {
std::cout << "Assigning ref " << r.m_ptr << std::endl;
if (m_ptr == r.m_ptr)
return *this;
if (m_ptr)
((Object *) m_ptr)->decRef();
m_ptr = r.m_ptr;
if (m_ptr)
((Object *) m_ptr)->incRef();
return *this;
}
/// Overwrite this reference with a pointer to another object
ref& operator=(T *ptr) {
std::cout << "Assigning ptr " << ptr << " to ref" << std::endl;
if (m_ptr == ptr)
return *this;
if (m_ptr)
((Object *) m_ptr)->decRef();
m_ptr = ptr;
if (m_ptr)
((Object *) m_ptr)->incRef();
return *this;
}
/// Compare this reference with another reference
bool operator==(const ref &r) const { return m_ptr == r.m_ptr; }
/// Compare this reference with another reference
bool operator!=(const ref &r) const { return m_ptr != r.m_ptr; }
/// Compare this reference with a pointer
bool operator==(const T* ptr) const { return m_ptr == ptr; }
/// Compare this reference with a pointer
bool operator!=(const T* ptr) const { return m_ptr != ptr; }
/// Access the object referenced by this reference
T* operator->() { return m_ptr; }
/// Access the object referenced by this reference
const T* operator->() const { return m_ptr; }
/// Return a C++ reference to the referenced object
T& operator*() { return *m_ptr; }
/// Return a const C++ reference to the referenced object
const T& operator*() const { return *m_ptr; }
/// Return a pointer to the referenced object
operator T* () { return m_ptr; }
/// Return a const pointer to the referenced object
T* get() { return m_ptr; }
/// Return a pointer to the referenced object
const T* get() const { return m_ptr; }
private:
T *m_ptr;
};
#endif /* __OBJECT_H */

16
example/run_test.py Normal file
View File

@ -0,0 +1,16 @@
import subprocess, sys, os
path = os.path.dirname(__file__)
if path != '':
os.chdir(path)
name = sys.argv[1]
output = subprocess.check_output([sys.executable, name + ".py"]).decode('utf-8')
reference = open(name + '.ref', 'r').read()
if output == reference:
print('Test "%s" succeeded.' % name)
exit(0)
else:
print('Test "%s" FAILED!' % name)
exit(-1)

526
include/pybind/cast.h Normal file
View File

@ -0,0 +1,526 @@
/*
pybind/cast.h: Partial template specializations to cast between
C++ and Python types
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#if !defined(__PYBIND_CAST)
#define __PYBIND_CAST
#include "pytypes.h"
#include "mpl.h"
#include "typeid.h"
#include <map>
#include <array>
NAMESPACE_BEGIN(pybind)
NAMESPACE_BEGIN(detail)
/// Generic type caster for objects stored on the heap
template <typename type> class type_caster {
public:
typedef instance<type> instance_type;
static std::string name() { return type_id<type>(); }
type_caster() {
auto const& registered_types = get_internals().registered_types;
auto it = registered_types.find(type_id<type>());
if (it != registered_types.end())
typeinfo = &it->second;
}
bool load(PyObject *src, bool convert) {
if (src == nullptr || typeinfo == nullptr)
return false;
if (PyType_IsSubtype(Py_TYPE(src), typeinfo->type)) {
value = ((instance_type *) src)->value;
return true;
}
if (convert) {
for (auto &converter : typeinfo->implicit_conversions) {
temp = object(converter(src, typeinfo->type), false);
if (load(temp.ptr(), false))
return true;
}
}
return false;
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
if (policy == return_value_policy::automatic)
policy = return_value_policy::copy;
return cast(&src, policy, parent);
}
static PyObject *cast(const type *_src, return_value_policy policy, PyObject *parent) {
type *src = const_cast<type *>(_src);
if (src == nullptr) {
Py_INCREF(Py_None);
return Py_None;
}
// avoid an issue with internal references matching their parent's address
bool dont_cache = parent && ((instance<void> *) parent)->value == (void *) src;
auto& internals = get_internals();
auto it_instance = internals.registered_instances.find(src);
if (it_instance != internals.registered_instances.end() && !dont_cache) {
PyObject *inst = it_instance->second;
Py_INCREF(inst);
return inst;
}
auto it = internals.registered_types.find(type_id<type>());
if (it == internals.registered_types.end()) {
std::string msg = std::string("Unregistered type : ") + type_id<type>();
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
}
auto &type_info = it->second;
instance_type *inst = (instance_type *) PyType_GenericAlloc(type_info.type, 0);
inst->value = src;
inst->owned = true;
inst->parent = nullptr;
if (policy == return_value_policy::automatic)
policy = return_value_policy::take_ownership;
handle_return_value_policy<type>(inst, policy, parent);
PyObject *inst_pyobj = (PyObject *) inst;
type_info.init_holder(inst_pyobj);
if (!dont_cache)
internals.registered_instances[inst->value] = inst_pyobj;
return inst_pyobj;
}
template <class T, typename std::enable_if<std::is_copy_constructible<T>::value, int>::type = 0>
static void handle_return_value_policy(instance<T> *inst, return_value_policy policy, PyObject *parent) {
if (policy == return_value_policy::copy) {
inst->value = new T(*(inst->value));
} else if (policy == return_value_policy::reference) {
inst->owned = false;
} else if (policy == return_value_policy::reference_internal) {
inst->owned = false;
inst->parent = parent;
Py_XINCREF(parent);
}
}
template <class T, typename std::enable_if<!std::is_copy_constructible<T>::value, int>::type = 0>
static void handle_return_value_policy(instance<T> *inst, return_value_policy policy, PyObject *parent) {
if (policy == return_value_policy::copy) {
throw cast_error("return_value_policy = copy, but the object is non-copyable!");
} else if (policy == return_value_policy::reference) {
inst->owned = false;
} else if (policy == return_value_policy::reference_internal) {
inst->owned = false;
inst->parent = parent;
Py_XINCREF(parent);
}
}
operator type*() { return value; }
operator type&() { return *value; }
protected:
type *value = nullptr;
const type_info *typeinfo = nullptr;
object temp;
};
#define TYPE_CASTER(type, py_name) \
protected: \
type value; \
public: \
static std::string name() { return py_name; } \
static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) { \
return cast(*src, policy, parent); \
} \
operator type*() { return &value; } \
operator type&() { return value; } \
#define TYPE_CASTER_NUMBER(type, py_type, from_type, to_pytype) \
template <> class type_caster<type> { \
public: \
bool load(PyObject *src, bool) { \
value = (type) from_type(src); \
if (value == (type) -1 && PyErr_Occurred()) { \
PyErr_Clear(); \
return false; \
} \
return true; \
} \
static PyObject *cast(type src, return_value_policy /* policy */, PyObject * /* parent */) { \
return to_pytype((py_type) src); \
} \
TYPE_CASTER(type, #type); \
};
TYPE_CASTER_NUMBER(int32_t, long, PyLong_AsLong, PyLong_FromLong)
TYPE_CASTER_NUMBER(uint32_t, unsigned long, PyLong_AsUnsignedLong, PyLong_FromUnsignedLong)
TYPE_CASTER_NUMBER(int64_t, PY_LONG_LONG, PyLong_AsLongLong, PyLong_FromLongLong)
TYPE_CASTER_NUMBER(uint64_t, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong, PyLong_FromUnsignedLongLong)
#if defined(__APPLE__) // size_t/ssize_t are separate types on Mac OS X
TYPE_CASTER_NUMBER(ssize_t, Py_ssize_t, PyLong_AsSsize_t, PyLong_FromSsize_t)
TYPE_CASTER_NUMBER(size_t, size_t, PyLong_AsSize_t, PyLong_FromSize_t)
#endif
TYPE_CASTER_NUMBER(float, float, PyFloat_AsDouble, PyFloat_FromDouble)
TYPE_CASTER_NUMBER(double, double, PyFloat_AsDouble, PyFloat_FromDouble)
template <> class type_caster<mpl::detail::void_type> {
public:
bool load(PyObject *, bool) { return true; }
static PyObject *cast(mpl::detail::void_type, return_value_policy /* policy */, PyObject * /* parent */) {
Py_INCREF(Py_None);
return Py_None;
}
TYPE_CASTER(mpl::detail::void_type, "None");
};
template <> class type_caster<bool> {
public:
bool load(PyObject *src, bool) {
if (src == Py_True) { value = true; return true; }
else if (src == Py_False) { value = false; return true; }
else return false;
}
static PyObject *cast(bool src, return_value_policy /* policy */, PyObject * /* parent */) {
PyObject *result = src ? Py_True : Py_False;
Py_INCREF(result);
return result;
}
TYPE_CASTER(bool, "bool");
};
template <> class type_caster<std::string> {
public:
bool load(PyObject *src, bool) {
const char *ptr = PyUnicode_AsUTF8(src);
if (!ptr) { PyErr_Clear(); return false; }
value = std::string(ptr);
return true;
}
static PyObject *cast(const std::string &src, return_value_policy /* policy */, PyObject * /* parent */) {
return PyUnicode_FromString(src.c_str());
}
TYPE_CASTER(std::string, "str");
};
template <> class type_caster<char> {
public:
bool load(PyObject *src, bool) {
char *ptr = PyUnicode_AsUTF8(src);
if (!ptr) { PyErr_Clear(); return false; }
value = ptr;
return true;
}
static PyObject *cast(const char *src, return_value_policy /* policy */, PyObject * /* parent */) {
return PyUnicode_FromString(src);
}
static PyObject *cast(char src, return_value_policy /* policy */, PyObject * /* parent */) {
char str[2] = { src, '\0' };
return PyUnicode_DecodeLatin1(str, 1, nullptr);
}
static std::string name() { return "str"; }
operator char*() { return value; }
operator char() { return *value; }
protected:
char *value;
};
template <typename Value> struct type_caster<std::vector<Value>> {
typedef std::vector<Value> type;
typedef type_caster<Value> value_conv;
public:
bool load(PyObject *src, bool convert) {
if (!PyList_Check(src))
return false;
size_t size = (size_t) PyList_GET_SIZE(src);
value.reserve(size);
value.clear();
for (size_t i=0; i<size; ++i) {
value_conv conv;
if (!conv.load(PyList_GetItem(src, (ssize_t) i), convert))
return false;
value.push_back((Value) conv);
}
return true;
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
PyObject *list = PyList_New(src.size());
size_t index = 0;
for (auto const &value: src) {
PyObject *value_ = value_conv::cast(value, policy, parent);
if (!value_) {
Py_DECREF(list);
return nullptr;
}
PyList_SetItem(list, index++, value_);
}
return list;
}
TYPE_CASTER(type, "list<" + value_conv::name() + ">");
};
template <typename Key, typename Value> struct type_caster<std::map<Key, Value>> {
public:
typedef std::map<Key, Value> type;
typedef type_caster<Key> key_conv;
typedef type_caster<Value> value_conv;
bool load(PyObject *src, bool convert) {
if (!PyDict_Check(src))
return false;
value.clear();
PyObject *key_, *value_;
ssize_t pos = 0;
key_conv kconv;
value_conv vconv;
while (PyDict_Next(src, &pos, &key_, &value_)) {
if (!kconv.load(key_, convert) || !vconv.load(value_, convert))
return false;
value[kconv] = vconv;
}
return true;
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
PyObject *dict = PyDict_New();
for (auto const &kv: src) {
PyObject *key = key_conv::cast(kv.first, policy, parent);
PyObject *value = value_conv::cast(kv.second, policy, parent);
if (!key || !value || PyDict_SetItem(dict, key, value) < 0) {
Py_XDECREF(key);
Py_XDECREF(value);
Py_DECREF(dict);
return nullptr;
}
Py_DECREF(key);
Py_DECREF(value);
}
return dict;
}
TYPE_CASTER(type, "dict<" + key_conv::name() + ", " + value_conv::name() + ">");
};
template <typename T1, typename T2> class type_caster<std::pair<T1, T2>> {
typedef std::pair<T1, T2> type;
public:
bool load(PyObject *src, bool convert) {
if (!PyTuple_Check(src) || PyTuple_Size(src) != 2)
return false;
if (!first.load(PyTuple_GetItem(src, 0), convert))
return false;
return second.load(PyTuple_GetItem(src, 1), convert);
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
PyObject *o1 = type_caster<typename mpl::normalize_type<T1>::type>::cast(src.first, policy, parent);
PyObject *o2 = type_caster<typename mpl::normalize_type<T2>::type>::cast(src.second, policy, parent);
if (!o1 || !o2) {
Py_XDECREF(o1);
Py_XDECREF(o2);
return nullptr;
}
PyObject *tuple = PyTuple_New(2);
PyTuple_SetItem(tuple, 0, o1);
PyTuple_SetItem(tuple, 1, o2);
return tuple;
}
static std::string name() {
return "(" + type_caster<T1>::name() + ", " + type_caster<T2>::name() + ")";
}
operator type() {
return type(first, second);
}
protected:
type_caster<typename mpl::normalize_type<T1>::type> first;
type_caster<typename mpl::normalize_type<T2>::type> second;
};
template <typename ... Tuple> class type_caster<std::tuple<Tuple...>> {
typedef std::tuple<Tuple...> type;
public:
enum { size = sizeof...(Tuple) };
bool load(PyObject *src, bool convert) {
return load(src, convert, typename mpl::make_index_sequence<sizeof...(Tuple)>::type());
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
return cast(src, policy, parent, typename mpl::make_index_sequence<size>::type());
}
static std::string name() {
std::array<std::string, size> names {{
type_caster<typename mpl::normalize_type<Tuple>::type>::name()...
}};
std::string result("(");
int counter = 0;
for (auto const &name : names) {
result += name;
if (++counter < size)
result += ", ";
}
result += ")";
return result;
}
operator type() {
return cast(typename mpl::make_index_sequence<sizeof...(Tuple)>::type());
}
protected:
template <size_t ... Index> type cast(mpl::index_sequence<Index...>) {
return type((Tuple) std::get<Index>(value)...);
}
template <size_t ... Indices> bool load(PyObject *src, bool convert, mpl::index_sequence<Indices...>) {
if (!PyTuple_Check(src))
return false;
if (PyTuple_Size(src) != size)
return false;
std::array<bool, size> results {{
std::get<Indices>(value).load(PyTuple_GetItem(src, Indices), convert)...
}};
for (bool r : results)
if (!r)
return false;
return true;
}
/* Implementation: Convert a C++ tuple into a Python tuple */
template <size_t ... Indices> static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent, mpl::index_sequence<Indices...>) {
std::array<PyObject *, size> results {{
type_caster<typename mpl::normalize_type<Tuple>::type>::cast(std::get<Indices>(src), policy, parent)...
}};
bool success = true;
for (auto result : results)
if (result == nullptr)
success = false;
if (success) {
PyObject *tuple = PyTuple_New(size);
int counter = 0;
for (auto result : results)
PyTuple_SetItem(tuple, counter++, result);
return tuple;
} else {
for (auto result : results) {
Py_XDECREF(result);
}
return nullptr;
}
}
protected:
std::tuple<type_caster<typename mpl::normalize_type<Tuple>::type>...> value;
};
/// Type caster for holder types like std::shared_ptr, etc.
template <typename type, typename holder_type> class type_caster_holder : public type_caster<type> {
public:
typedef type_caster<type> parent;
bool load(PyObject *src, bool convert) {
if (!parent::load(src, convert))
return false;
holder = holder_type(parent::value);
return true;
}
explicit operator type*() { return this->value; }
explicit operator type&() { return *(this->value); }
explicit operator holder_type&() { return holder; }
explicit operator holder_type*() { return &holder; }
protected:
holder_type holder;
};
template <> class type_caster<handle> {
public:
bool load(PyObject *src) {
value = handle(src);
return true;
}
static PyObject *cast(const handle &src, return_value_policy /* policy */, PyObject * /* parent */) {
src.inc_ref();
return (PyObject *) src.ptr();
}
TYPE_CASTER(handle, "handle");
};
#define TYPE_CASTER_PYTYPE(name) \
template <> class type_caster<name> { \
public: \
bool load(PyObject *src, bool) { value = name(src, true); return true; } \
static PyObject *cast(const name &src, return_value_policy /* policy */, PyObject * /* parent */) { \
src.inc_ref(); return (PyObject *) src.ptr(); \
} \
TYPE_CASTER(name, #name); \
};
TYPE_CASTER_PYTYPE(object)
TYPE_CASTER_PYTYPE(buffer)
TYPE_CASTER_PYTYPE(capsule)
TYPE_CASTER_PYTYPE(dict)
TYPE_CASTER_PYTYPE(float_)
TYPE_CASTER_PYTYPE(int_)
TYPE_CASTER_PYTYPE(list)
TYPE_CASTER_PYTYPE(slice)
TYPE_CASTER_PYTYPE(tuple)
#undef TYPE_CASTER
#undef TYPE_CASTER_NUMBER
#undef TYPE_CASTER_PYTYPE
NAMESPACE_END(detail)
template <typename T> inline T cast(PyObject *object) {
detail::type_caster<typename mpl::normalize_type<T>::type> conv;
if (!conv.load(object, true))
throw cast_error("Unable to cast Python object to C++ type");
return conv;
}
template <typename T> inline object cast(const T &value, return_value_policy policy = return_value_policy::automatic, PyObject *parent = nullptr) {
if (policy == return_value_policy::automatic)
policy = std::is_pointer<T>::value ? return_value_policy::take_ownership : return_value_policy::copy;
return object(detail::type_caster<typename mpl::normalize_type<T>::type>::cast(value, policy, parent), false);
}
template <typename T> inline T handle::cast() { return pybind::cast<T>(m_ptr); }
template <typename ... Args> inline object handle::call(Args&&... args_) {
const size_t size = sizeof...(Args);
std::array<PyObject *, size> args{
{ detail::type_caster<typename mpl::normalize_type<Args>::type>::cast(
std::forward<Args>(args_), return_value_policy::automatic, nullptr)... }
};
bool fail = false;
for (auto result : args)
if (result == nullptr)
fail = true;
if (fail) {
for (auto result : args) {
Py_XDECREF(result);
}
throw cast_error("handle::call(): unable to convert input arguments to Python objects");
}
PyObject *tuple = PyTuple_New(size);
int counter = 0;
for (auto result : args)
PyTuple_SetItem(tuple, counter++, result);
PyObject *result = PyObject_CallObject(m_ptr, tuple);
Py_DECREF(tuple);
return object(result, false);
}
NAMESPACE_END(pybind)
#endif /* __PYBIND_CAST */

149
include/pybind/common.h Normal file
View File

@ -0,0 +1,149 @@
/*
pybind/common.h -- Basic macros
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#if !defined(__PYBIND_COMMON_H)
#define __PYBIND_COMMON_H
#if !defined(NAMESPACE_BEGIN)
#define NAMESPACE_BEGIN(name) namespace name {
#endif
#if !defined(NAMESPACE_END)
#define NAMESPACE_END(name) }
#endif
#if !defined(PYTHON_EXPORT)
#if defined(WIN32)
#define PYTHON_EXPORT __declspec(dllexport)
#else
#define PYTHON_EXPORT __attribute__ ((visibility("default")))
#endif
#endif
#define PYTHON_PLUGIN(name) \
extern "C" PYTHON_EXPORT PyObject *PyInit_##name()
#include <vector>
#include <string>
#include <stdexcept>
#include <functional>
#include <unordered_map>
#include <iostream>
#include <memory>
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
#if defined(_MSC_VER)
#define HAVE_ROUND
#pragma warning(push)
#pragma warning(disable: 4510 4610 4512)
#if _DEBUG
#define _DEBUG_MARKER
#undef _DEBUG
#endif
#endif
#include <Python.h>
#if defined(_MSC_VER)
#if defined(_DEBUG_MARKER)
#define _DEBUG
#undef _DEBUG_MARKER
#endif
#pragma warning(pop)
#endif
NAMESPACE_BEGIN(pybind)
typedef Py_ssize_t ssize_t;
/// Approach used to cast a previously unknown C++ instance into a Python object
enum class return_value_policy : int {
/** Automatic: copy objects returned as values and take ownership of objects
returned as pointers */
automatic = 0,
/** Reference the object and take ownership. Python will call the
destructor and delete operator when the reference count reaches zero */
take_ownership,
/** Reference the object, but do not take ownership (dangerous when C++ code
deletes it and Python still has a nonzero reference count) */
reference,
/** Reference the object, but do not take ownership. The object is considered
be owned by the C++ instance whose method or property returned it. The
Python object will increase the reference count of this 'parent' by 1 */
reference_internal,
/// Create a new copy of the returned object, which will be owned by Python
copy
};
/// Format strings for basic number types
template <typename type> struct format_descriptor { };
template<> struct format_descriptor<int8_t> { static std::string value() { return "b"; }; };
template<> struct format_descriptor<uint8_t> { static std::string value() { return "B"; }; };
template<> struct format_descriptor<int16_t> { static std::string value() { return "h"; }; };
template<> struct format_descriptor<uint16_t> { static std::string value() { return "H"; }; };
template<> struct format_descriptor<int32_t> { static std::string value() { return "i"; }; };
template<> struct format_descriptor<uint32_t> { static std::string value() { return "I"; }; };
template<> struct format_descriptor<int64_t> { static std::string value() { return "q"; }; };
template<> struct format_descriptor<uint64_t> { static std::string value() { return "Q"; }; };
template<> struct format_descriptor<float> { static std::string value() { return "f"; }; };
template<> struct format_descriptor<double> { static std::string value() { return "d"; }; };
/// Information record describing a Python buffer object
struct buffer_info {
void *ptr;
size_t itemsize;
std::string format; // for dense contents, this should be set to format_descriptor<T>::value
int ndim;
std::vector<size_t> shape;
std::vector<size_t> strides;
buffer_info(void *ptr, size_t itemsize, const std::string &format,
int ndim, const std::vector<size_t> &shape,
const std::vector<size_t> &strides)
: ptr(ptr), itemsize(itemsize), format(format), ndim(ndim),
shape(shape), strides(strides) {}
};
// C++ bindings of core Python exceptions
struct stop_iteration : public std::runtime_error { public: stop_iteration(const std::string &w="") : std::runtime_error(w) {} };
struct index_error : public std::runtime_error { public: index_error(const std::string &w="") : std::runtime_error(w) {} };
struct error_already_set : public std::exception { public: error_already_set() {} };
/// Thrown when pybind::cast or handle::call fail due to a type casting error
struct cast_error : public std::runtime_error { public: cast_error(const std::string &w = "") : std::runtime_error(w) {} };
NAMESPACE_BEGIN(detail)
/// PyObject wrapper around generic types
template <typename type, typename holder_type = std::unique_ptr<type>> struct instance {
PyObject_HEAD
type *value;
PyObject *parent;
bool owned : 1;
bool constructed : 1;
holder_type holder;
};
/// Additional type information which does not fit into the PyTypeObjet
struct type_info {
PyTypeObject *type;
size_t type_size;
void (*init_holder)(PyObject *);
std::function<buffer_info *(PyObject *)> get_buffer;
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
};
/// Internal data struture used to track registered instances and types
struct internals {
std::unordered_map<std::string, type_info> registered_types;
std::unordered_map<void *, PyObject *> registered_instances;
};
inline internals &get_internals();
NAMESPACE_END(detail)
NAMESPACE_END(pybind)
#endif /* __PYBIND_COMMON_H */

190
include/pybind/mpl.h Normal file
View File

@ -0,0 +1,190 @@
/*
pybind/mpl.h: Simple library for type manipulation and template metaprogramming
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#if !defined(__PYBIND_MPL_H)
#define __PYBIND_MPL_H
#include "common.h"
#include <tuple>
NAMESPACE_BEGIN(pybind)
NAMESPACE_BEGIN(mpl)
/// Index sequence for convenient template metaprogramming involving tuples
template<size_t ...> struct index_sequence { };
template<size_t N, size_t ...S> struct make_index_sequence : make_index_sequence <N - 1, N - 1, S...> { };
template<size_t ...S> struct make_index_sequence <0, S...> { typedef index_sequence<S...> type; };
/// Helper template to strip away type modifiers
template <typename T> struct normalize_type { typedef T type; };
template <typename T> struct normalize_type<const T> { typedef typename normalize_type<T>::type type; };
template <typename T> struct normalize_type<T*> { typedef typename normalize_type<T>::type type; };
template <typename T> struct normalize_type<T&> { typedef typename normalize_type<T>::type type; };
template <typename T> struct normalize_type<T&&> { typedef typename normalize_type<T>::type type; };
template <typename T, size_t N> struct normalize_type<const T[N]> { typedef typename normalize_type<T>::type type; };
template <typename T, size_t N> struct normalize_type<T[N]> { typedef typename normalize_type<T>::type type; };
NAMESPACE_BEGIN(detail)
/// Strip the class from a method type
template <typename T> struct remove_class {};
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...)> { typedef R type(A...); };
template <typename C, typename R, typename... A> struct remove_class<R (C::*)(A...) const> { typedef R type(A...); };
/**
* \brief Convert a lambda function to a std::function
* From http://stackoverflow.com/questions/11893141/inferring-the-call-signature-of-a-lambda-or-arbitrary-callable-for-make-functio
*/
template <typename T> struct lambda_signature_impl {
using type = typename remove_class<
decltype(&std::remove_reference<T>::type::operator())>::type;
};
template <typename R, typename... A> struct lambda_signature_impl<R (A...)> { typedef R type(A...); };
template <typename R, typename... A> struct lambda_signature_impl<R (&)(A...)> { typedef R type(A...); };
template <typename R, typename... A> struct lambda_signature_impl<R (*)(A...)> { typedef R type(A...); };
template <typename T> using lambda_signature = typename lambda_signature_impl<T>::type;
template <typename F> using make_function_type = std::function<lambda_signature<F>>;
NAMESPACE_END(detail)
template<typename F> detail::make_function_type<F> make_function(F &&f) {
return detail::make_function_type<F>(std::forward<F>(f)); }
NAMESPACE_BEGIN(detail)
struct void_type { };
/// Helper functions for calling a function using a tuple argument while dealing with void/non-void return values
template <typename RetType> struct tuple_dispatch {
typedef RetType return_type;
template<typename Func, typename Arg, size_t ... S> return_type operator()(const Func &f, Arg && args, index_sequence<S...>) {
return f(std::get<S>(std::forward<Arg>(args))...);
}
};
/// Helper functions for calling a function using a tuple argument (special case for void return values)
template <> struct tuple_dispatch<void> {
typedef void_type return_type;
template<typename Func, typename Arg, size_t ... S> return_type operator()(const Func &f, Arg &&args, index_sequence<S...>) {
f(std::get<S>(std::forward<Arg>(args))...);
return return_type();
}
};
NAMESPACE_END(detail)
/// For lambda functions delegate to their 'operator()'
template <typename T> struct function_traits : public function_traits<typename detail::make_function_type<T>> { };
/// Type traits for function pointers
template <typename ReturnType, typename... Args>
struct function_traits<ReturnType(*)(Args...)> {
enum {
nargs = sizeof...(Args),
is_method = 0,
is_const = 0
};
typedef std::function<ReturnType (Args...)> f_type;
typedef detail::tuple_dispatch<ReturnType> dispatch_type;
typedef typename dispatch_type::return_type return_type;
typedef std::tuple<Args...> args_type;
template <size_t i> struct arg {
typedef typename std::tuple_element<i, args_type>::type type;
};
static f_type cast(ReturnType (*func)(Args ...)) { return func; }
static return_type dispatch(const f_type &f, args_type &&args) {
return dispatch_type()(f, std::move(args),
typename make_index_sequence<nargs>::type());
}
};
/// Type traits for ordinary methods
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...)> {
enum {
nargs = sizeof...(Args),
is_method = 1,
is_const = 0
};
typedef std::function<ReturnType(ClassType &, Args...)> f_type;
typedef detail::tuple_dispatch<ReturnType> dispatch_type;
typedef typename dispatch_type::return_type return_type;
typedef std::tuple<ClassType&, Args...> args_type;
template <size_t i> struct arg {
typedef typename std::tuple_element<i, args_type>::type type;
};
static f_type cast(ReturnType (ClassType::*func)(Args ...)) { return std::mem_fn(func); }
static return_type dispatch(const f_type &f, args_type &&args) {
return dispatch_type()(f, std::move(args),
typename make_index_sequence<nargs+1>::type());
}
};
/// Type traits for const methods
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
enum {
nargs = sizeof...(Args),
is_method = 1,
is_const = 1
};
typedef std::function<ReturnType (const ClassType &, Args...)> f_type;
typedef detail::tuple_dispatch<ReturnType> dispatch_type;
typedef typename dispatch_type::return_type return_type;
typedef std::tuple<const ClassType&, Args...> args_type;
template <size_t i> struct arg {
typedef typename std::tuple_element<i, args_type>::type type;
};
static f_type cast(ReturnType (ClassType::*func)(Args ...) const) {
return std::mem_fn(func);
}
static return_type dispatch(const f_type &f, args_type &&args) {
return dispatch_type()(f, std::move(args),
typename make_index_sequence<nargs+1>::type());
}
};
/// Type traits for std::functions
template <typename ReturnType, typename... Args>
struct function_traits<std::function<ReturnType(Args...)>> {
enum {
nargs = sizeof...(Args),
is_method = 0,
is_const = 0
};
typedef std::function<ReturnType (Args...)> f_type;
typedef detail::tuple_dispatch<ReturnType> dispatch_type;
typedef typename dispatch_type::return_type return_type;
typedef std::tuple<Args...> args_type;
template <size_t i> struct arg {
typedef typename std::tuple_element<i, args_type>::type type;
};
static f_type cast(const f_type &func) { return func; }
static return_type dispatch(const f_type &f, args_type &&args) {
return dispatch_type()(f, std::move(args),
typename make_index_sequence<nargs>::type());
}
};
NAMESPACE_END(mpl)
NAMESPACE_END(pybind)
#endif /* __PYBIND_MPL_H */

151
include/pybind/operators.h Normal file
View File

@ -0,0 +1,151 @@
/*
pybind/operator.h: Metatemplates for operator overloading
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#if !defined(__PYBIND_OPERATOR)
#define __PYBIND_OPERATOR
#include "pybind.h"
#include <type_traits>
NAMESPACE_BEGIN(pybind)
NAMESPACE_BEGIN(detail)
/// Enumeration with all supported operator types
enum op_id : int {
op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow,
op_lshift, op_rshift, op_and, op_xor, op_or, op_neg,
op_pos, op_abs, op_invert, op_int, op_long, op_float,
op_str, op_cmp, op_gt, op_ge, op_lt, op_le, op_eq, op_ne,
op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift,
op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool,
op_nonzero, op_repr, op_truediv
};
enum op_type : int {
op_l, /* base type on left */
op_r, /* base type on right */
op_u /* unary operator */
};
struct self_t { };
/// Type for an unused type slot
struct undefined_t { };
static const self_t self = self_t();
/// Don't warn about an unused variable
inline self_t __self() { return self; }
/// base template of operator implementations
template <op_id, op_type, typename B, typename L, typename R> struct op_impl { };
/// Operator implementation generator
template <op_id id, op_type ot, typename L, typename R> struct op_ {
template <typename base, typename holder> void execute(pybind::class_<base, holder> &class_, const char *doc, return_value_policy policy) const {
typedef typename std::conditional<std::is_same<L, self_t>::value, base, L>::type L_type;
typedef typename std::conditional<std::is_same<R, self_t>::value, base, R>::type R_type;
typedef op_impl<id, ot, base, L_type, R_type> op;
class_.def(op::name(), &op::execute, doc, policy);
}
template <typename base, typename holder> void execute_cast(pybind::class_<base, holder> &class_, const char *doc, return_value_policy policy) const {
typedef typename std::conditional<std::is_same<L, self_t>::value, base, L>::type L_type;
typedef typename std::conditional<std::is_same<R, self_t>::value, base, R>::type R_type;
typedef op_impl<id, ot, base, L_type, R_type> op;
class_.def(op::name(), &op::execute_cast, doc, policy);
}
};
#define PYBIND_BINARY_OPERATOR(id, rid, op, expr) \
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \
static char const* name() { return "__" #id "__"; } \
static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \
static B execute_cast(const L &l, const R &r) { return B(expr); } \
}; \
template <typename B, typename L, typename R> struct op_impl<op_##id, op_r, B, L, R> { \
static char const* name() { return "__" #rid "__"; } \
static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \
static B execute_cast(const L &l, const R &r) { return B(expr); } \
}; \
inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) { \
return op_<op_##id, op_l, self_t, self_t>(); \
}; \
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
return op_<op_##id, op_l, self_t, T>(); \
}; \
template <typename T> op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) { \
return op_<op_##id, op_r, T, self_t>(); \
};
#define PYBIND_INPLACE_OPERATOR(id, op, expr) \
template <typename B, typename L, typename R> struct op_impl<op_##id, op_l, B, L, R> { \
static char const* name() { return "__" #id "__"; } \
static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \
static B execute_cast(L &l, const R &r) { return B(expr); } \
}; \
template <typename T> op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
return op_<op_##id, op_l, self_t, T>(); \
};
#define PYBIND_UNARY_OPERATOR(id, op, expr) \
template <typename B, typename L> struct op_impl<op_##id, op_u, B, L, undefined_t> { \
static char const* name() { return "__" #id "__"; } \
static auto execute(const L &l) -> decltype(expr) { return expr; } \
static B execute_cast(const L &l) { return B(expr); } \
}; \
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \
return op_<op_##id, op_u, self_t, undefined_t>(); \
};
PYBIND_BINARY_OPERATOR(sub, rsub, operator-, l - r)
PYBIND_BINARY_OPERATOR(add, radd, operator+, l + r)
PYBIND_BINARY_OPERATOR(mul, rmul, operator*, l * r)
PYBIND_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
PYBIND_BINARY_OPERATOR(mod, rmod, operator%, l % r)
PYBIND_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
PYBIND_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
PYBIND_BINARY_OPERATOR(and, rand, operator&, l & r)
PYBIND_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
PYBIND_BINARY_OPERATOR(eq, eq, operator==, l == r)
PYBIND_BINARY_OPERATOR(ne, ne, operator!=, l != r)
PYBIND_BINARY_OPERATOR(or, ror, operator|, l | r)
PYBIND_BINARY_OPERATOR(gt, lt, operator>, l > r)
PYBIND_BINARY_OPERATOR(ge, le, operator>=, l >= r)
PYBIND_BINARY_OPERATOR(lt, gt, operator<, l < r)
PYBIND_BINARY_OPERATOR(le, ge, operator<=, l <= r)
//PYBIND_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
PYBIND_INPLACE_OPERATOR(iadd, operator+=, l += r)
PYBIND_INPLACE_OPERATOR(isub, operator-=, l -= r)
PYBIND_INPLACE_OPERATOR(imul, operator*=, l *= r)
PYBIND_INPLACE_OPERATOR(idiv, operator/=, l /= r)
PYBIND_INPLACE_OPERATOR(imod, operator%=, l %= r)
PYBIND_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
PYBIND_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
PYBIND_INPLACE_OPERATOR(iand, operator&=, l &= r)
PYBIND_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
PYBIND_INPLACE_OPERATOR(ior, operator|=, l |= r)
PYBIND_UNARY_OPERATOR(neg, operator-, -l)
PYBIND_UNARY_OPERATOR(pos, operator+, +l)
PYBIND_UNARY_OPERATOR(abs, abs, std::abs(l))
PYBIND_UNARY_OPERATOR(invert, operator~, ~l)
PYBIND_UNARY_OPERATOR(bool, operator!, !!l)
PYBIND_UNARY_OPERATOR(int, int_, (int) l)
PYBIND_UNARY_OPERATOR(float, float_, (double) l)
#undef PYBIND_BINARY_OPERATOR
#undef PYBIND_INPLACE_OPERATOR
#undef PYBIND_UNARY_OPERATOR
NAMESPACE_END(detail)
using detail::self;
NAMESPACE_END(pybind)
#endif /* __PYBIND_OPERATOR */

587
include/pybind/pybind.h Normal file
View File

@ -0,0 +1,587 @@
/*
pybind/pybind.h: Main header file of the C++11 python binding generator library
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#if !defined(__PYBIND_H)
#define __PYBIND_H
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning)
#pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
#pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter
#pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted
#endif
#include "cast.h"
NAMESPACE_BEGIN(pybind)
class function : public object {
private:
struct function_entry {
std::function<PyObject* (PyObject *)> impl;
std::string signature, doc;
bool is_constructor;
function_entry *next = nullptr;
};
public:
PYTHON_OBJECT_DEFAULT(function, object, PyFunction_Check)
template <typename Func>
function(const char *name, Func _func, bool is_method,
function overload_sibling = function(), const char *doc = nullptr,
return_value_policy policy = return_value_policy::automatic) {
/* Function traits extracted from the template type 'Func' */
typedef mpl::function_traits<Func> f_traits;
/* Suitable input and output casters */
typedef typename detail::type_caster<typename f_traits::args_type> cast_in;
typedef typename detail::type_caster<typename mpl::normalize_type<typename f_traits::return_type>::type> cast_out;
typename f_traits::f_type func = f_traits::cast(_func);
auto impl = [func, policy](PyObject *pyArgs) -> PyObject *{
cast_in args;
if (!args.load(pyArgs, true))
return nullptr;
PyObject *parent = policy != return_value_policy::reference_internal
? nullptr : PyTuple_GetItem(pyArgs, 0);
return cast_out::cast(
f_traits::dispatch(func, (typename f_traits::args_type) args),
policy, parent);
};
/* Linked list of function call handlers (for overloading) */
function_entry *entry = new function_entry();
entry->impl = impl;
entry->signature = std::string(name) + cast_in::name() + std::string(" -> ") + cast_out::name();
entry->is_constructor = !strcmp(name, "__init__");
if (doc) entry->doc = doc;
install_function(name, entry, is_method, overload_sibling);
}
private:
static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject * /* kwargs */) {
function_entry *overloads = (function_entry *) PyCapsule_GetPointer(self, nullptr);
PyObject *result = nullptr;
try {
for (function_entry *it = overloads; it != nullptr; it = it->next) {
if ((result = it->impl(args)) != nullptr)
break;
}
} catch (const error_already_set &) { return nullptr;
} catch (const index_error &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr;
} catch (const stop_iteration &e) { PyErr_SetString(PyExc_StopIteration, e.what()); return nullptr;
} catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return nullptr;
} catch (...) {
PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
return nullptr;
}
if (result) {
if (overloads->is_constructor) {
PyObject *inst = PyTuple_GetItem(args, 0);
const detail::type_info *type_info =
capsule(PyObject_GetAttrString((PyObject *) Py_TYPE(inst),
const_cast<char *>("__pybind__")), false);
type_info->init_holder(inst);
}
return result;
} else {
std::string signatures = "Incompatible function arguments. The "
"following argument types are supported:\n";
int ctr = 0;
for (function_entry *it = overloads; it != nullptr; it = it->next) {
signatures += " "+ std::to_string(++ctr) + ". ";
signatures += it->signature;
signatures += "\n";
}
PyErr_SetString(PyExc_TypeError, signatures.c_str());
return nullptr;
}
}
void install_function(const char *name, function_entry *entry, bool is_method, function overload_sibling) {
if (!overload_sibling.ptr() || !PyCFunction_Check(overload_sibling.ptr())) {
PyMethodDef *def = new PyMethodDef();
memset(def, 0, sizeof(PyMethodDef));
def->ml_name = strdup(name);
def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
def->ml_flags = METH_VARARGS | METH_KEYWORDS;
capsule entry_capsule(entry);
m_ptr = PyCFunction_New(def, entry_capsule.ptr());
if (!m_ptr)
throw std::runtime_error("function::function(): Could not allocate function object");
} else {
m_ptr = overload_sibling.ptr();
inc_ref();
capsule entry_capsule(PyCFunction_GetSelf(m_ptr), true);
function_entry *parent = (function_entry *) entry_capsule, *backup = parent;
while (parent->next)
parent = parent->next;
parent->next = entry;
entry = backup;
}
std::string signatures;
while (entry) { /* Create pydoc entry */
signatures += "Signature : " + std::string(entry->signature) + "\n";
if (!entry->doc.empty())
signatures += "\n" + std::string(entry->doc) + "\n";
if (entry->next)
signatures += "\n";
entry = entry->next;
}
PyCFunctionObject *func = (PyCFunctionObject *) m_ptr;
if (func->m_ml->ml_doc)
std::free((char *) func->m_ml->ml_doc);
func->m_ml->ml_doc = strdup(signatures.c_str());
if (is_method) {
m_ptr = PyInstanceMethod_New(m_ptr);
if (!m_ptr)
throw std::runtime_error("function::function(): Could not allocate instance method object");
Py_DECREF(func);
}
}
};
class module : public object {
public:
PYTHON_OBJECT_DEFAULT(module, object, PyModule_Check)
module(const char *name, const char *doc = nullptr) {
PyModuleDef *def = new PyModuleDef();
memset(def, 0, sizeof(PyModuleDef));
def->m_name = name;
def->m_doc = doc;
def->m_size = -1;
Py_INCREF(def);
m_ptr = PyModule_Create(def);
if (m_ptr == nullptr)
throw std::runtime_error("Internal error in module::module()");
inc_ref();
}
template <typename Func> module& def(const char *name, Func f, const char *doc = nullptr) {
function func(name, f, false, (function) attr(name), doc);
func.inc_ref(); /* The following line steals a reference to 'func' */
PyModule_AddObject(ptr(), name, func.ptr());
return *this;
}
module def_submodule(const char *name) {
std::string full_name = std::string(PyModule_GetName(m_ptr))
+ std::string(".") + std::string(name);
module result(PyImport_AddModule(full_name.c_str()), true);
attr(name) = result;
return result;
}
};
NAMESPACE_BEGIN(detail)
/* Forward declarations */
enum op_id : int;
enum op_type : int;
struct undefined_t;
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
template <typename ... Args> struct init;
/// Basic support for creating new Python heap types
class custom_type : public object {
public:
PYTHON_OBJECT_DEFAULT(custom_type, object, PyType_Check)
custom_type(object &scope, const char *name_, const std::string &type_name,
size_t type_size, size_t instance_size,
void (*init_holder)(PyObject *), const destructor &dealloc,
PyObject *parent, const char *doc) {
PyHeapTypeObject *type = (PyHeapTypeObject*) PyType_Type.tp_alloc(&PyType_Type, 0);
PyObject *name = PyUnicode_FromString(name_);
if (type == nullptr || name == nullptr)
throw std::runtime_error("Internal error in custom_type::custom_type()");
Py_INCREF(name);
std::string full_name(name_);
pybind::str scope_name = (object) scope.attr("__name__"),
module_name = (object) scope.attr("__module__");
if (scope_name.check())
full_name = std::string(scope_name) + "." + full_name;
if (module_name.check())
full_name = std::string(module_name) + "." + full_name;
type->ht_name = type->ht_qualname = name;
type->ht_type.tp_name = strdup(full_name.c_str());
type->ht_type.tp_basicsize = instance_size;
type->ht_type.tp_doc = doc;
type->ht_type.tp_init = (initproc) init;
type->ht_type.tp_new = (newfunc) new_instance;
type->ht_type.tp_dealloc = dealloc;
type->ht_type.tp_flags |=
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC;
type->ht_type.tp_as_number = &type->as_number;
type->ht_type.tp_as_sequence = &type->as_sequence;
type->ht_type.tp_as_mapping = &type->as_mapping;
type->ht_type.tp_base = (PyTypeObject *) parent;
Py_XINCREF(parent);
if (PyType_Ready(&type->ht_type) < 0)
throw std::runtime_error("Internal error in custom_type::custom_type()");
m_ptr = (PyObject *) type;
/* Needed by pydoc */
if (((module &) scope).check())
attr("__module__") = scope_name;
auto &type_info = detail::get_internals().registered_types[type_name];
type_info.type = (PyTypeObject *) m_ptr;
type_info.type_size = type_size;
type_info.init_holder = init_holder;
attr("__pybind__") = capsule(&type_info);
scope.attr(name) = *this;
}
protected:
/* Allocate a metaclass on demand (for static properties) */
handle metaclass() {
auto &ht_type = ((PyHeapTypeObject *) m_ptr)->ht_type;
auto &ob_type = ht_type.ob_base.ob_base.ob_type;
if (ob_type == &PyType_Type) {
std::string name_ = std::string(ht_type.tp_name) + "_meta";
PyHeapTypeObject *type = (PyHeapTypeObject*) PyType_Type.tp_alloc(&PyType_Type, 0);
PyObject *name = PyUnicode_FromString(name_.c_str());
if (type == nullptr || name == nullptr)
throw std::runtime_error("Internal error in custom_type::metaclass()");
Py_INCREF(name);
type->ht_name = type->ht_qualname = name;
type->ht_type.tp_name = strdup(name_.c_str());
type->ht_type.tp_base = &PyType_Type;
type->ht_type.tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE;
type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC;
if (PyType_Ready(&type->ht_type) < 0)
throw std::runtime_error("Internal error in custom_type::metaclass()");
ob_type = (PyTypeObject *) type;
Py_INCREF(type);
}
return handle((PyObject *) ob_type);
}
static int init(void *self, PyObject *, PyObject *) {
std::string msg = std::string(Py_TYPE(self)->tp_name) + ": No constructor defined!";
PyErr_SetString(PyExc_TypeError, msg.c_str());
return -1;
}
static PyObject *new_instance(PyTypeObject *type, PyObject *, PyObject *) {
const detail::type_info *type_info = capsule(
PyObject_GetAttrString((PyObject *) type, const_cast<char*>("__pybind__")), false);
instance<void> *self = (instance<void> *) PyType_GenericAlloc(type, 0);
self->value = ::operator new(type_info->type_size);
self->owned = true;
self->parent = nullptr;
self->constructed = false;
detail::get_internals().registered_instances[self->value] = (PyObject *) self;
return (PyObject *) self;
}
static void dealloc(instance<void> *self) {
if (self->value) {
bool dont_cache = self->parent && ((instance<void> *) self->parent)->value == self->value;
if (!dont_cache) { // avoid an issue with internal references matching their parent's address
auto &registered_instances = detail::get_internals().registered_instances;
auto it = registered_instances.find(self->value);
if (it == registered_instances.end())
throw std::runtime_error("Deallocating unregistered instance!");
registered_instances.erase(it);
}
Py_XDECREF(self->parent);
}
Py_TYPE(self)->tp_free((PyObject*) self);
}
void install_buffer_funcs(const std::function<buffer_info *(PyObject *)> &func) {
PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr;
type->ht_type.tp_as_buffer = &type->as_buffer;
type->as_buffer.bf_getbuffer = getbuffer;
type->as_buffer.bf_releasebuffer = releasebuffer;
((detail::type_info *) capsule(attr("__pybind__")))->get_buffer = func;
}
static int getbuffer(PyObject *obj, Py_buffer *view, int flags) {
auto const &info_func = ((detail::type_info *) capsule(handle(obj).attr("__pybind__")))->get_buffer;
if (view == nullptr || obj == nullptr || !info_func) {
PyErr_SetString(PyExc_BufferError, "Internal error");
return -1;
}
memset(view, 0, sizeof(Py_buffer));
buffer_info *info = info_func(obj);
view->obj = obj;
view->ndim = 1;
view->internal = info;
view->buf = info->ptr;
view->itemsize = info->itemsize;
view->len = view->itemsize;
for (auto s : info->shape)
view->len *= s;
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
view->format = const_cast<char *>(info->format.c_str());
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
view->ndim = info->ndim;
view->strides = (Py_ssize_t *)&info->strides[0];
view->shape = (Py_ssize_t *) &info->shape[0];
}
Py_INCREF(view->obj);
return 0;
}
static void releasebuffer(PyObject *, Py_buffer *view) { delete (buffer_info *) view->internal; }
};
NAMESPACE_END(detail)
template <typename type, typename holder_type = std::unique_ptr<type>> class class_ : public detail::custom_type {
public:
typedef detail::instance<type, holder_type> instance_type;
PYTHON_OBJECT(class_, detail::custom_type, PyType_Check)
class_(object &scope, const char *name, const char *doc = nullptr)
: detail::custom_type(scope, name, type_id<type>(), sizeof(type),
sizeof(instance_type), init_holder, dealloc,
nullptr, doc) { }
class_(object &scope, const char *name, object &parent,
const char *doc = nullptr)
: detail::custom_type(scope, name, type_id<type>(), sizeof(type),
sizeof(instance_type), init_holder, dealloc,
parent.ptr(), doc) { }
template <typename Func>
class_ &def(const char *name, Func f, const char *doc = nullptr,
return_value_policy policy = return_value_policy::automatic) {
attr(name) = function(name, f, true, (function) attr(name), doc, policy);
return *this;
}
template <typename Func> class_ &
def_static(const char *name, Func f, const char *doc = nullptr,
return_value_policy policy = return_value_policy::automatic) {
attr(name) = function(name, f, false, (function) attr(name), doc, policy);
return *this;
}
template <detail::op_id id, detail::op_type ot, typename L, typename R>
class_ &def(const detail::op_<id, ot, L, R> &op, const char *doc = nullptr,
return_value_policy policy = return_value_policy::automatic) {
op.template execute<type>(*this, doc, policy);
return *this;
}
template <detail::op_id id, detail::op_type ot, typename L, typename R> class_ &
def_cast(const detail::op_<id, ot, L, R> &op, const char *doc = nullptr,
return_value_policy policy = return_value_policy::automatic) {
op.template execute_cast<type>(*this, doc, policy);
return *this;
}
template <typename... Args>
class_ &def(const detail::init<Args...> &init, const char *doc = nullptr) {
init.template execute<type>(*this, doc);
return *this;
}
class_& def_buffer(const std::function<buffer_info(type&)> &func) {
install_buffer_funcs([func](PyObject *obj) -> buffer_info* {
detail::type_caster<type> caster;
if (!caster.load(obj, false))
return nullptr;
return new buffer_info(func(caster));
});
return *this;
}
template <typename C, typename D>
class_ &def_readwrite(const char *name, D C::*pm,
const char *doc = nullptr) {
function fget("", [=](C * ptr) -> D & { return ptr->*pm; }, true,
function(), doc, return_value_policy::reference_internal),
fset("", [=](C *ptr, const D &value) { ptr->*pm = value; }, true, function(), doc);
def_property(name, fget, fset, doc);
return *this;
}
template <typename C, typename D>
class_ &def_readonly(const char *name, const D C::*pm,
const char *doc = nullptr) {
function fget("", [=](C * ptr) -> const D & { return ptr->*pm; }, true,
function(), doc, return_value_policy::reference_internal);
def_property(name, fget, doc);
return *this;
}
template <typename D>
class_ &def_readwrite_static(const char *name, D *pm,
const char *doc = nullptr) {
function fget("", [=](object) -> D & { return *pm; }, true),
fset("", [=](object, const D &value) { *pm = value; }, true);
def_property_static(name, fget, fset, doc);
return *this;
}
template <typename D>
class_ &def_readonly_static(const char *name, const D *pm,
const char *doc = nullptr) {
function fget("", [=](object) -> const D & { return *pm; }, true);
def_property_static(name, fget, doc);
return *this;
}
class_ &def_property(const char *name, const function &fget,
const char *doc = nullptr) {
def_property(name, fget, function(), doc);
return *this;
}
class_ &def_property_static(const char *name, const function &fget,
const char *doc = nullptr) {
def_property_static(name, fget, function(), doc);
return *this;
}
class_ &def_property(const char *name, const function &fget,
const function &fset, const char *doc = nullptr) {
object property(
PyObject_CallFunction((PyObject *)&PyProperty_Type,
const_cast<char *>("OOOs"), fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc), false);
attr(name) = property;
return *this;
}
class_ &def_property_static(const char *name, const function &fget,
const function &fset,
const char *doc = nullptr) {
object property(
PyObject_CallFunction((PyObject *)&PyProperty_Type,
const_cast<char *>("OOOs"), fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc), false);
metaclass().attr(name) = property;
return *this;
}
private:
static void init_holder(PyObject *inst_) {
instance_type *inst = (instance_type *) inst_;
new (&inst->holder) holder_type(inst->value);
inst->constructed = true;
}
static void dealloc(PyObject *inst_) {
instance_type *inst = (instance_type *) inst_;
if (inst->owned) {
if (inst->constructed)
inst->holder.~holder_type();
else
::operator delete(inst->value);
}
custom_type::dealloc((detail::instance<void> *) inst);
}
};
/// Binds C++ enumerations and enumeration classes to Python
template <typename Type> class enum_ : public class_<Type> {
public:
enum_(object &scope, const char *name, const char *doc = nullptr)
: class_<Type>(scope, name, doc), m_parent(scope) {
auto entries = new std::unordered_map<int, const char *>();
this->def("__str__", [name, entries](Type value) -> std::string {
auto it = entries->find(value);
return std::string(name) + "." +
((it == entries->end()) ? std::string("???")
: std::string(it->second));
});
m_entries = entries;
}
/// Export enumeration entries into the parent scope
void export_values() {
PyObject *dict = ((PyTypeObject *) this->m_ptr)->tp_dict;
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(dict, &pos, &key, &value))
if (PyObject_IsInstance(value, this->m_ptr))
m_parent.attr(key) = value;
}
/// Add an enumeration entry
enum_& value(char const* name, Type value) {
this->attr(name) = pybind::cast(value, return_value_policy::copy);
(*m_entries)[(int) value] = name;
return *this;
}
private:
std::unordered_map<int, const char *> *m_entries;
object &m_parent;
};
NAMESPACE_BEGIN(detail)
template <typename ... Args> struct init {
template <typename Base, typename Holder> void execute(pybind::class_<Base, Holder> &class_, const char *doc) const {
/// Function which calls a specific C++ in-place constructor
class_.def("__init__", [](Base *instance, Args... args) { new (instance) Base(args...); }, doc);
}
};
NAMESPACE_END(detail)
template <typename... Args> detail::init<Args...> init() { return detail::init<Args...>(); };
template <typename InputType, typename OutputType> void implicitly_convertible() {
auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject *{
if (!detail::type_caster<InputType>().load(obj, false))
return nullptr;
tuple args(1);
args[0] = obj;
PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr);
if (result == nullptr)
PyErr_Clear();
return result;
};
std::string output_type_name = type_id<OutputType>();
auto & registered_types = detail::get_internals().registered_types;
auto it = registered_types.find(output_type_name);
if (it == registered_types.end())
throw std::runtime_error("implicitly_convertible: Unable to find type " + output_type_name);
it->second.implicit_conversions.push_back(implicit_caster);
}
inline void init_threading() { PyEval_InitThreads(); }
class gil_scoped_acquire {
PyGILState_STATE state;
public:
inline gil_scoped_acquire() { state = PyGILState_Ensure(); }
inline ~gil_scoped_acquire() { PyGILState_Release(state); }
};
class gil_scoped_release {
PyThreadState *state;
public:
inline gil_scoped_release() { state = PyEval_SaveThread(); }
inline ~gil_scoped_release() { PyEval_RestoreThread(state); }
};
NAMESPACE_END(pybind)
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#undef PYTHON_OBJECT
#undef PYTHON_OBJECT_DEFAULT
#endif /* __PYBIND_H */

339
include/pybind/pytypes.h Normal file
View File

@ -0,0 +1,339 @@
/*
pybind/typeid.h: Convenience wrapper classes for basic Python types
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#if !defined(__PYBIND_PYTYPES_H)
#define __PYBIND_PYTYPES_H
#include "common.h"
#include <utility>
NAMESPACE_BEGIN(pybind)
/* A few forward declarations */
class object;
class str;
class object;
class dict;
NAMESPACE_BEGIN(detail)
class accessor;
NAMESPACE_END(detail)
/// Holds a reference to a Python object (no reference counting)
class handle {
public:
handle() : m_ptr(nullptr) { }
handle(const handle &other) : m_ptr(other.m_ptr) { }
handle(PyObject *ptr) : m_ptr(ptr) { }
PyObject *ptr() { return m_ptr; }
const PyObject *ptr() const { return m_ptr; }
void inc_ref() const { Py_XINCREF(m_ptr); }
void dec_ref() const { Py_XDECREF(m_ptr); }
int ref_count() const { return (int) Py_REFCNT(m_ptr); }
inline detail::accessor operator[](handle key);
inline detail::accessor operator[](const char *key);
inline detail::accessor attr(handle key);
inline detail::accessor attr(const char *key);
inline pybind::str str() const;
template <typename T> T cast();
template <typename ... Args> object call(Args&&... args_);
operator bool() const { return m_ptr != nullptr; }
protected:
PyObject *m_ptr;
};
/// Holds a reference to a Python object (with reference counting)
class object : public handle {
public:
object() { }
object(const object &o) : handle(o) { inc_ref(); }
object(const handle &h, bool borrowed) : handle(h) { if (borrowed) inc_ref(); }
object(PyObject *ptr, bool borrowed) : handle(ptr) { if (borrowed) inc_ref(); }
object(object &&other) { m_ptr = other.m_ptr; other.m_ptr = nullptr; }
~object() { dec_ref(); }
object& operator=(object &other) {
Py_XINCREF(other.m_ptr);
Py_XDECREF(m_ptr);
m_ptr = other.m_ptr;
return *this;
}
object& operator=(object &&other) {
if (this != &other) {
PyObject *temp = m_ptr;
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
Py_XDECREF(temp);
}
return *this;
}
};
NAMESPACE_BEGIN(detail)
class accessor {
public:
accessor(PyObject *obj, PyObject *key, bool attr)
: obj(obj), key(key), attr(attr) { Py_INCREF(key); }
accessor(PyObject *obj, const char *key, bool attr)
: obj(obj), key(PyUnicode_FromString(key)), attr(attr) { }
accessor(const accessor &a) : obj(a.obj), key(a.key), attr(a.attr)
{ Py_INCREF(key); }
~accessor() { Py_DECREF(key); }
void operator=(accessor o) { operator=(object(o)); }
void operator=(const handle &h) {
if (attr) {
if (PyObject_SetAttr(obj, key, (PyObject *) h.ptr()) < 0)
throw std::runtime_error("Unable to set object attribute");
} else {
if (PyObject_SetItem(obj, key, (PyObject *) h.ptr()) < 0)
throw std::runtime_error("Unable to set object item");
}
}
operator object() const {
object result(attr ? PyObject_GetAttr(obj, key)
: PyObject_GetItem(obj, key), false);
if (!result) PyErr_Clear();
return result;
}
operator bool() const {
if (attr) {
return (bool) PyObject_HasAttr(obj, key);
} else {
object result(PyObject_GetItem(obj, key), false);
if (!result) PyErr_Clear();
return (bool) result;
}
};
private:
PyObject *obj;
PyObject *key;
bool attr;
};
struct list_accessor {
public:
list_accessor(PyObject *list, size_t index) : list(list), index(index) { }
void operator=(list_accessor o) { return operator=(object(o)); }
void operator=(const handle &o) {
o.inc_ref(); // PyList_SetItem steals a reference
if (PyList_SetItem(list, (ssize_t) index, (PyObject *) o.ptr()) < 0)
throw std::runtime_error("Unable to assign value in Python list!");
}
operator object() const {
PyObject *result = PyList_GetItem(list, (ssize_t) index);
if (!result)
throw std::runtime_error("Unable to retrieve value from Python list!");
return object(result, true);
}
private:
PyObject *list;
size_t index;
};
struct tuple_accessor {
public:
tuple_accessor(PyObject *tuple, size_t index) : tuple(tuple), index(index) { }
void operator=(tuple_accessor o) { return operator=(object(o)); }
void operator=(const handle &o) {
o.inc_ref(); // PyTuple_SetItem steals a reference
if (PyTuple_SetItem(tuple, (ssize_t) index, (PyObject *) o.ptr()) < 0)
throw std::runtime_error("Unable to assign value in Python tuple!");
}
operator object() const {
PyObject *result = PyTuple_GetItem(tuple, (ssize_t) index);
if (!result)
throw std::runtime_error("Unable to retrieve value from Python tuple!");
return object(result, true);
}
private:
PyObject *tuple;
size_t index;
};
class list_iterator {
public:
list_iterator(PyObject *list, ssize_t pos) : list(list), pos(pos) { }
list_iterator& operator++() { ++pos; return *this; }
object operator*() { return object(PyList_GetItem(list, pos), true); }
bool operator==(const list_iterator &it) const { return it.pos == pos; }
bool operator!=(const list_iterator &it) const { return it.pos != pos; }
private:
PyObject *list;
ssize_t pos;
};
struct dict_iterator {
public:
dict_iterator(PyObject *dict = nullptr, ssize_t pos = -1) : dict(dict), pos(pos) { }
dict_iterator& operator++() {
if (!PyDict_Next(dict, &pos, &key, &value))
pos = -1;
return *this;
}
std::pair<object, object> operator*() {
return std::make_pair(object(key, true), object(value, true));
}
bool operator==(const dict_iterator &it) const { return it.pos == pos; }
bool operator!=(const dict_iterator &it) const { return it.pos != pos; }
private:
PyObject *dict, *key, *value;
ssize_t pos = 0;
};
NAMESPACE_END(detail)
inline detail::accessor handle::operator[](handle key) { return detail::accessor(ptr(), key.ptr(), false); }
inline detail::accessor handle::operator[](const char *key) { return detail::accessor(ptr(), key, false); }
inline detail::accessor handle::attr(handle key) { return detail::accessor(ptr(), key.ptr(), true); }
inline detail::accessor handle::attr(const char *key) { return detail::accessor(ptr(), key, true); }
#define PYTHON_OBJECT(Name, Parent, CheckFun) \
Name(const handle &h, bool borrowed) : Parent(h, borrowed) { } \
Name(const object& o): Parent(o) { } \
Name(object&& o): Parent(std::move(o)) { } \
Name& operator=(object&& o) { return static_cast<Name&>(object::operator=(std::move(o))); } \
Name& operator=(object& o) { return static_cast<Name&>(object::operator=(o)); } \
bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); }
#define PYTHON_OBJECT_DEFAULT(Name, Parent, CheckFun) \
PYTHON_OBJECT(Name, Parent, CheckFun) \
Name() : object() { }
class str : public object {
public:
PYTHON_OBJECT_DEFAULT(str, object, PyUnicode_Check)
str(const char *s) : object(PyUnicode_FromString(s), false) { }
operator const char *() const { return PyUnicode_AsUTF8(m_ptr); }
};
inline pybind::str handle::str() const { return pybind::str(PyObject_Str(m_ptr), false); }
inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (const char *) obj.str(); return os; }
class bool_ : public object {
public:
PYTHON_OBJECT_DEFAULT(bool_, object, PyBool_Check)
operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; }
};
class int_ : public object {
public:
PYTHON_OBJECT_DEFAULT(int_, object, PyLong_Check)
int_(int value) : object(PyLong_FromLong((long) value), false) { }
int_(size_t value) : object(PyLong_FromSize_t(value), false) { }
int_(ssize_t value) : object(PyLong_FromSsize_t(value), false) { }
operator int() const { return (int) PyLong_AsLong(m_ptr); }
};
class float_ : public object {
public:
PYTHON_OBJECT_DEFAULT(float_, object, PyFloat_Check)
float_(float value) : object(PyFloat_FromDouble((double) value), false) { }
float_(double value) : object(PyFloat_FromDouble((double) value), false) { }
operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
operator double() const { return (double) PyFloat_AsDouble(m_ptr); }
};
class slice : public object {
public:
PYTHON_OBJECT_DEFAULT(slice, object, PySlice_Check)
slice(ssize_t start_, ssize_t stop_, ssize_t step_) {
int_ start(start_), stop(stop_), step(step_);
m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr());
}
bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, ssize_t *slicelength) const {
return PySlice_GetIndicesEx(m_ptr, length, start, stop, step, slicelength) == 0;
}
};
class capsule : public object {
public:
PYTHON_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
capsule(void *value) : object(PyCapsule_New(value, nullptr, nullptr), false) { }
template <typename T> operator T *() const {
T * result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, nullptr));
if (!result) throw std::runtime_error("Unable to extract capsule contents!");
return result;
}
};
class tuple : public object {
public:
PYTHON_OBJECT_DEFAULT(tuple, object, PyTuple_Check)
tuple(size_t size) : object(PyTuple_New((Py_ssize_t) size), false) { }
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
detail::tuple_accessor operator[](size_t index) { return detail::tuple_accessor(ptr(), index); }
};
class dict : public object {
public:
PYTHON_OBJECT(dict, object, PyDict_Check)
dict() : object(PyDict_New(), false) { }
size_t size() const { return (size_t) PyDict_Size(m_ptr); }
detail::dict_iterator begin() { return (++detail::dict_iterator(ptr(), 0)); }
detail::dict_iterator end() { return detail::dict_iterator(); }
};
class list : public object {
public:
PYTHON_OBJECT(list, object, PyList_Check)
list(size_t size = 0) : object(PyList_New((ssize_t) size), false) { }
size_t size() const { return (size_t) PyList_Size(m_ptr); }
detail::list_iterator begin() { return detail::list_iterator(ptr(), 0); }
detail::list_iterator end() { return detail::list_iterator(ptr(), (ssize_t) size()); }
detail::list_accessor operator[](size_t index) { return detail::list_accessor(ptr(), index); }
void append(const object &object) { PyList_Append(m_ptr, (PyObject *) object.ptr()); }
};
class buffer : public object {
public:
PYTHON_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer)
buffer_info request(bool writable = false) {
int flags = PyBUF_STRIDES | PyBUF_FORMAT;
if (writable) flags |= PyBUF_WRITABLE;
view = new Py_buffer();
if (PyObject_GetBuffer(m_ptr, view, flags) != 0)
throw error_already_set();
std::vector<size_t> shape(view->ndim), strides(view->ndim);
for (int i=0; i<view->ndim; ++i) {
shape[i] = (size_t) view->shape[i];
strides[i] = (size_t) view->strides[i];
}
return buffer_info(view->buf, view->itemsize, view->format,
view->ndim, shape, strides);
}
~buffer() { if (view) { PyBuffer_Release(view); delete view; } }
private:
Py_buffer *view = nullptr;
};
NAMESPACE_BEGIN(detail)
inline internals &get_internals() {
static internals *internals_ptr = nullptr;
if (internals_ptr)
return *internals_ptr;
handle builtins(PyEval_GetBuiltins());
capsule caps(builtins["__pybind__"]);
if (caps.check()) {
internals_ptr = caps;
} else {
internals_ptr = new internals();
builtins["__pybind__"] = capsule(internals_ptr);
}
return *internals_ptr;
}
NAMESPACE_END(detail)
NAMESPACE_END(pybind)
#endif /* __PYBIND_PYTYPES_H */

53
include/pybind/typeid.h Normal file
View File

@ -0,0 +1,53 @@
/*
pybind/typeid.h: Compiler-independent access to type identifiers
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#if !defined(__PYBIND_TYPEID_H)
#define __PYBIND_TYPEID_H
#include "common.h"
#include <cstdio>
#include <cstdlib>
#if defined(__GNUG__)
#include <cxxabi.h>
#endif
NAMESPACE_BEGIN(pybind)
NAMESPACE_BEGIN(detail)
/// Erase all occurrences of a substring
inline void erase_all(std::string &string, const std::string &search) {
for (size_t pos = 0;;) {
pos = string.find(search, pos);
if (pos == std::string::npos) break;
string.erase(pos, search.length());
}
}
NAMESPACE_END(detail)
/// Return a string representation of a C++ type
template <typename T> static std::string type_id() {
std::string name(typeid(T).name());
#if defined(__GNUG__)
int status = 0;
std::unique_ptr<char, void (*)(void *)> res {
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free };
if (status == 0)
name = res.get();
#else
detail::erase_all(name, "class ");
detail::erase_all(name, "struct ");
detail::erase_all(name, "enum ");
#endif
detail::erase_all(name, "pybind::");
return name;
}
NAMESPACE_END(pybind)
#endif /* __PYBIND_TYPEID_H */