Skip to content

Commit e21ecb0

Browse files
committed
Adding example
1 parent c801639 commit e21ecb0

File tree

3 files changed

+63
-17
lines changed

3 files changed

+63
-17
lines changed

docs/source/examples.rst

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,53 @@ Then we can test the module:
9898
we should compile and run the example from the same folder.
9999
To install, please consult
100100
`this pybind11 / CMake example <https://github.com/pybind/cmake_example>`_.
101+
102+
103+
Fall-back cast
104+
==============
105+
106+
The previous example showed you how to design your module to be flexible in accepting data.
107+
From C++ we used ``xt::xarray<double>``,
108+
whereas for the Python API we used ``xt::pyarray<double>`` to operate directly on the memory
109+
of a NumPy array from Python (without copying the data).
110+
111+
Sometimes, you might not have the flexibility to design your module's methods
112+
with template parameters.
113+
This might occur when you want to ``override`` functions
114+
(though it is recommended to use CRTP to still use templates).
115+
In this case we can still bind the module in Python using *xtensor-python*,
116+
however, we have to copy the data from a (NumPy) array.
117+
This means that although the following signatures are quite different when used from C++,
118+
as follows:
119+
120+
1. *Constant reference*: read from the data, without copying it.
121+
122+
.. code-block:: cpp
123+
124+
void foo(const xt::xarray<double>& a);
125+
126+
2. *Reference*: read from and/or write to the data, without copying it.
127+
128+
.. code-block:: cpp
129+
130+
void foo(xt::xarray<double>& a);
131+
132+
3. *Copy*: copy the data.
133+
134+
.. code-block:: cpp
135+
136+
void foo(xt::xarray<double> a);
137+
138+
The Python will all cases result in a copy to a temporary variable
139+
(though the last signature will lead to a copy to a temporary variable, and another copy to ``a``).
140+
On the one hand, this is more costly than when using ``xt::pyarray`` and ``xt::pyxtensor``,
141+
on the other hand, it means that all changes you make to a reference, are made to the temporary
142+
copy, and are thus lost.
143+
144+
Still, it might be a convenient way to create Python bindings, using a minimal effort.
145+
Consider this example:
146+
147+
:download:`main.cpp <examples/copy_cast/main.cpp>`
148+
149+
.. literalinclude:: examples/copy_cast/main.cpp
150+
:language: cpp

docs/source/examples/copy_cast/main.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
#define FORCE_IMPORT_ARRAY
55
#include <xtensor-python/pyarray.hpp>
66

7-
double sum_of_sines(xt::pyarray<double>& m)
7+
template <class T>
8+
double sum_of_sines(T& m)
89
{
910
auto sines = xt::sin(m); // sines does not actually hold values.
1011
return std::accumulate(sines.begin(), sines.end(), 0.0);
1112
}
1213

14+
// In the Python API this a reference to a temporary variable
1315
double sum_of_cosines(const xt::xarray<double>& m)
1416
{
1517
auto cosines = xt::cos(m); // cosines does not actually hold values.
@@ -20,6 +22,6 @@ PYBIND11_MODULE(mymodule, m)
2022
{
2123
xt::import_numpy();
2224
m.doc() = "Test module for xtensor python bindings";
23-
m.def("sum_of_sines", sum_of_sines, "Sum the sines of the input values");
25+
m.def("sum_of_sines", sum_of_sines<xt::pyarray<double>>, "Sum the sines of the input values");
2426
m.def("sum_of_cosines", sum_of_cosines, "Sum the cosines of the input values");
2527
}

include/xtensor-python/xtensor_type_caster_base.hpp

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ namespace pybind11
2626
template <typename T, xt::layout_type L>
2727
struct xtensor_get_buffer
2828
{
29-
template <typename H>
30-
static auto get(H src)
29+
static auto get(handle src)
3130
{
3231
return array_t<T, array::c_style | array::forcecast>::ensure(src);
3332
}
@@ -36,8 +35,7 @@ namespace pybind11
3635
template <typename T>
3736
struct xtensor_get_buffer<T, xt::layout_type::column_major>
3837
{
39-
template <typename H>
40-
static auto get(H src)
38+
static auto get(handle src)
4139
{
4240
return array_t<T, array::f_style>::ensure(src);
4341
}
@@ -51,8 +49,7 @@ namespace pybind11
5149
template <class T, xt::layout_type L>
5250
struct xtensor_check_buffer<xt::xarray<T, L>>
5351
{
54-
template <typename H>
55-
static auto get(H src)
52+
static auto get(handle src)
5653
{
5754
auto buf = xtensor_get_buffer<T, L>::get(src);
5855
return buf;
@@ -62,8 +59,7 @@ namespace pybind11
6259
template <class T, std::size_t N, xt::layout_type L>
6360
struct xtensor_check_buffer<xt::xtensor<T, N, L>>
6461
{
65-
template <typename H>
66-
static auto get(H src)
62+
static auto get(handle src)
6763
{
6864
auto buf = xtensor_get_buffer<T, L>::get(src);
6965
if (buf.ndim() != N) {
@@ -76,8 +72,7 @@ namespace pybind11
7672
template <class CT, class S, xt::layout_type L, class FST>
7773
struct xtensor_check_buffer<xt::xstrided_view<CT, S, L, FST>>
7874
{
79-
template <typename H>
80-
static auto get(H /*src*/)
75+
static auto get(handle /*src*/)
8176
{
8277
return false;
8378
}
@@ -86,18 +81,17 @@ namespace pybind11
8681
template <class EC, xt::layout_type L, class SC, class Tag>
8782
struct xtensor_check_buffer<xt::xarray_adaptor<EC, L, SC, Tag>>
8883
{
89-
template <typename H>
90-
static auto get(H /*src*/)
84+
static auto get(handle src)
9185
{
92-
return false;
86+
auto buf = xtensor_get_buffer<EC, L>::get(src);
87+
return buf;
9388
}
9489
};
9590

9691
template <class EC, std::size_t N, xt::layout_type L, class Tag>
9792
struct xtensor_check_buffer<xt::xtensor_adaptor<EC, N, L, Tag>>
9893
{
99-
template <typename H>
100-
static auto get(H /*src*/)
94+
static auto get(handle /*src*/)
10195
{
10296
return false;
10397
}

0 commit comments

Comments
 (0)