PyBind - Overloaded functions - swig

Firstly, my thanks to all of you for trying to resolve this doubt of mine. I am working on converting a minimal C++ project to be used in Python. The real reason behind this effort is for speed.
I came across PyBind and was quite surprised at its capabilities and also the amount of documentation they have provided. Right now there is something stopping the work because I have no idea on how to do it. Consider the below code in the file "MySource.hpp" and can you kindly tell me how a binding can be done?
struct Point3D
{
public:
double x, y, z;
CPoint3D();
CPoint3D(double x, double y, double z);
inline double Len() const;
inline void Normalize();
};
Point3D VectorCross(const Point3D& pt1, const Point3D& pt2, const Point3D& pt3);
void VectorCross(const float* u, const float* v, float * n);
I was able to define a binding for Point3D as a class and certain of its member functions. But I don't have a clue about how to do the binding for the overloaded method "VectorCross". It has two methods with one accepting instances of Point3D, and another one accepting pointers to float arrays.
The binding I wrote so far is shown below
PYBIND11_MODULE(mymodule, m)
{
py::class_<Point3D> point3d(m, "Point3D");
point3d.def_readwrite("x", &CPoint3D::x);
point3d.def_readwrite("y", &CPoint3D::y);
point3d.def_readwrite("z", &CPoint3D::z);
point3d.def(py::init<>());
point3d.def(py::init<double , double , double >());
point3d.def("Len", &CPoint3D::Len);
point3d.def("Normalize", &CPoint3D::Normalize);
}
Can someone please guide me on how to do this?

It seems that you need to do overload cast as described here.
m.def("VectorCross", py::overload_cast<const Point3D&, const Point3D&, const Point3D&>(&VectorCross));
m.def("VectorCross", py::overload_cast<const float*, const float*, float*>(&VectorCross));

Roman,
I figured this out but still selecting your answer as the right one for it really is the answer. But still in the case of method signature where it expects the arguments to be float pointers (below line)
m.def("VectorCross", py::overload_cast<const float*, const float*, float*>(&VectorCross));
compiles fine in creating a python library. But when you are try calling the method from python after importing the same will result in an argument error.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: AngleBetween(): incompatible function arguments. The following argument types are supported:
1. (pt1: chenhancc.CPoint3D, pt2: chenhancc.CPoint3D) -> float
2. (pt1: chenhancc.CPoint3D, pt2: chenhancc.CPoint3D, pt3: chenhancc.CPoint3D) -> float
3. (u: float, v: float) -> float
Looks like the python looks at it as if they are plain float number arguments.
But still my sincere thanks and appreciate your time.
Regards,
0K

Related

Why does vector.push_back(System::Byte) not compile any more in VC++ 14.29 (C++/CLI)

I have the following code that used to compile and work fine:
std::vector<unsigned char> marshal_as(cli::array<System::Byte>^ const& from)
{
std::vector<unsigned char> result;
result.reserve(from->Length);
for (int i = 0; i < from->Length; i++)
{
result.push_back(from[i]);
}
return result;
}
After updating VisualStudio to version 16.10 - which updates the C++ compiler to version 14.29 - the code produces an error:
error C2664: 'void std::vector<unsigned
char,std::allocator<_Ty>>::push_back(const _Ty &)': cannot convert
argument 1 from 'unsigned char' to 'const _Ty &'
with
[
_Ty=unsigned char
]
message : An object from the gc heap (element of a managed array) cannot be converted to a native reference
message : see
declaration of 'std::vector<unsigned
char,std::allocator<_Ty>>::push_back'
with
[
_Ty=unsigned char
]
Changing the code in the loop body to
unsigned char b = from[i];
result.push_back(b);
fixes the problem.
I would like to understand the cause of this error. Is this somehow related to a change due to the C++ 20 standard?
Is this somehow related to a change due to the C++ 20 standard?
No. While std::vector<>::push() has subtly changed in C++20, it's not a change that materially affects what's going on here, the issue is definitely clr-specific.
I would like to understand the cause of this error.
This is almost certainly (see below) an error that was always present in your code, but was not being reported by previous versions of the C++/CLI compiler.
Consider the following function:
void foo(const int& v) {
int* ptr = &v;
// store ptr somewhere, long-term.
}
It's obvious that invoking foo() with a reference to a gc-backed int would be a recipe for disaster. Yet that's exactly what result.push_back(from[i]); does.
Your code "works" because push_back() happens to do nothing with its parameter that causes an issue. However, the compiler is not supposed to know that.
N.B. I say almost certainly because I'm having a heck of a time tracking down the call signature for cli::array<T>::operator[](std::size_t) const. It's not impossible that it used to return a T and now returns const T%.

How to use shared_ptr and make_shared with arrays?

I want to use a C++ shared_ptr as a replacement for raw C pointers. As a simple example the following code seems to work as intended:
from libcpp.memory cimport shared_ptr, allocator
cdef shared_ptr[double] spd
cdef allocator[double] allo
spd.reset(allo.allocate(13))
The size is chosen as 13 here, but in general is not know at compile time.
I'm not sure if this is correct, but I haven't had any errors (no memory leaks and segfaults yet). I'm curious if there is a more optimal solution with make_shared.
But I can't use C++11 arrays because Cython doesn't allow literals as templates, e.g. something like
cdef shared_ptr[array[double]] spd = make_shared[array[double,13]]()
and "normal" arrays which are supposed to work with C++20 compiler (e.g. gcc 10) are causing problems in one way or another:
# Cython error "Expected an identifier or literal"
cdef shared_ptr[double[]] spd = make_shared[double[]](3)
# Can't get ptr to work
ctypedef double[] darr
cdef shared_ptr[darr] spd = make_shared[darr](13)
cdef double* ptr = spd.get() # or spd.get()[0] or <double*> spd.get()[0] or ...
Is the allocator solution the correct and best one or is there another way how to do it?
Here is what I'm going with
cdef extern from *:
"""
template <typename T>
struct Ptr_deleter{
size_t nn;
void (*free_ptr)(T*, size_t);
Ptr_deleter(size_t nIn, void (*free_ptrIn)(T*, size_t)){
this->nn = nIn;
this->free_ptr = free_ptrIn;
};
void operator()(T* ptr){
free_ptr(ptr, nn);
};
};
template <typename T>
std::shared_ptr<T> ptr_to_sptr (T* ptr, size_t nn, void (*free_ptr)(T*, size_t)) {
Ptr_deleter dltr(nn, free_ptr);
std::shared_ptr<T> sp (ptr, dltr);
return sp;
};
"""
shared_ptr[double] d_ptr_to_sptr "ptr_to_sptr"(double* ptr, size_t nn, void (*free_ptr)(double*, size_t) nogil) nogil
cdef void free_d_ptr(double* ptr, size_t nn) nogil:
free(ptr)
cdef shared_ptr[double] sp_d_empty(size_t nn) nogil:
return d_ptr_to_sptr(<double*> nullCheckMalloc(nn*sizeof(double)), nn, &free_d_ptr)
My understanding is that the "right" way to handle malloced arrays is to use a custom deleter like I did. I personally prefer sticking with somewhat-raw C pointers (double* instead of double[] or something), since it's more natural in Cython and my projects.
I think it's reasonably easy to see how to change free_ptr for more complicated data types. For simple data types it could be done in less lines and less convoluted, but I wanted to have the same base.
I like my solution in the regard that I can just "wrap" existing Cython/C code raw pointers in a shared_ptr.
When working with C++ (especially newer standards like C++20) I think verbatim code is pretty often necessary. But I've intentionally defined free_d_ptr in Cython, so it's easy to use existing Cython code to handle the actual work done to free/clear/whatever the array.
I didn't get C++11 std::arrays to work, and it's apparently not "properly" possible in Cython in general (see Interfacing C++11 array with Cython).
I didn't get double[] or similar to work either (is possible in C++20), but with verbatim C++ code I think this should be doable in Cython. I prefer more C-like pointers/arrays anyway as I said.

Use nested functions as callbacks with Windows API functions?

I have the following code, this works.
import core.sys.windows.windows: EnumWindows;
import std.stdio: writeln;
void*[] hWndList;
extern (Windows) int callback(void* hWnd, long /* lParams */ ) nothrow {
hWndList ~= hWnd;
return true;
}
void main() {
EnumWindows(&callback, 0);
writeln(hWndList);
}
I was hoping I could use something more akin to JavaScript's syntax: (void* hWnd, long) => {}.
I tried this but I'm getting errors with the signature, it says the function is a delegate and apparently Windows API can't accept a delegate.
import core.sys.windows.windows: EnumWindows;
import std.stdio: writeln;
void main() {
void*[] hWndList;
EnumWindows((void* hWnd, long /* lParams */ ) nothrow {
hWndList ~= hWnd; return true;
}, 0);
writeln(hWndList);
}
I'm not going to even paste the compiler error because I am very clearly going about this the wrong way.
Of course there is nothing wrong with defining each callback as a separate function, but then comes the issue of naming them. I also don't like the way it makes my code look.
Thanks.
I figured out that I can cast the delegate (or lambda) to the correct signature specified by the MSDN documentation for EnumWindowsProc.
I also didn't realize that it was very bad practice to access the frame of the D program by implicitly using hWndList from the global scope.
This is what I used to create the correct signature.
alias EnumWindowsProc = extern (Windows) int function(HWND, LPARAM) nothrow;
Then I discovered that the alias already existed as ENUMWINDOWSPROC in the MinGW headers at core.sys.windows.windows at line 2483 (at the time of writing).
extern (Windows) nothrow {
...
alias BOOL function(HWND, LPARAM) ENUMWINDOWSPROC;
...
}
To solve the issue of implicitly passing D's frame, I used cast(LPARAM) &hWndList as the lParam in EnumWindows.
This became an issue of using the pointer now. I know this is probably botched, any suggestions welcome, but I casted it to a pointer.
*(cast(HWND[]*) lParam)
And the full code looks something like this. Obviously, this was just a minimal example to begin with, so you may want to assign the casted pointer to something so it's less confusing when using the variable from lParams.
import core.sys.windows.windows: EnumWindows, ENUMWINDOWSPROC;
import std.stdio: writeln;
void main() {
HWND[] hWndList;
EnumWindows(cast(ENUMWINDOWSPROC) (HWND hWnd, LPARAM lParam) {
*(cast(HWND[]*) lParam) ~= hWnd;
return true;
}, cast(LPARAM) &hWndList);
writeln(hWndList);
}
I hope this helps someone because this was confusing as hell for me (still not sure I understand the pointer logic).
Thanks to Boris-Barboris on the D Forums for giving me something to work from.
https://forum.dlang.org/post/xxklxaajptppockvazeo#forum.dlang.org

Cannot overload make_uint4 function

I'm trying to overload make_uint4 in the following manner:
namespace A {
namespace B {
inline __host__ __device__ uint4 make_uint4(uint2 a, uint2 b) {
return make_uint4(a.x, a.y, b.x, b.y);
}
}
}
But when I try to compile it, nvcc returns an error:
error: no suitable constructor exists to convert from "unsigned int" to "uint2"
error: no suitable constructor exists to convert from "unsigned int" to "uint2"
error: too many arguments in function call
All these errors point to the "return…" line.
I was able to get a partial repro on VS 2010 and CUDA 4.0 (the compiler built the code OK but Intellisense flagged the error you are seeing). Try the following:
#include "vector_functions.h"
inline __host__ __device__ uint4 make_uint4(uint2 a, uint2 b)
{
return ::make_uint4(a.x, a.y, b.x, b.y);
}
This fixed it for me.
I have no problem compiling it in Visual Studio+nvcc. What compiler are you using?
If that would be of any help: make_uint4 is defined in vector_functions.h, line 170 as
static __inline__ __host__ __device__ uint4 make_uint4(unsigned int x, unsigned int y, unsigned int z, unsigned int w)
{
uint4 t; t.x = x; t.y = y; t.z = z; t.w = w; return t;
}
Update:
I get similar error when I try to overload the function while being inside my custom namespace. Are you certain you are not inside one? If so, try putting :: in front of function call to refer to global scope, i.e:
return ::make_uint4(a.x, a.y, b.x, b.y);
I don't have the library code, but it seems like the compiler doesn't like overloaded device functions (as they are treated just like really fancy inline macros). What is does is shadow (hide) the old make_uint4(a,b,c,d) with your new make_uint4(va, vb) and try to call the latter with 4 uint parameters. That doesn't work because there is no conversion from uint to uint2 (as indicated by the first two error messages) and there are 4 instead of 2 arguments (the last error message).
Use a slightly different function name like make_uint4_from_uint2s and you'll be fine.

custom comparetor in stl

This has been driving me nuts for 3 hours. Anybody see a reason why this isn't working?
struct sortByPropRev
{
bool operator()(const cust_type &a, const cust_type &b) const
{
return a.prop > b.prop;
}
};
...
priority_queue<cust_type, vector<cust_type>, sortByPropRev> x;
I get compile errors:
Error C2664: 'bool (cust_type &,cust_type &)' : cannot convert parameter 1 from 'const cust_type' to 'cust_type &'
and 2 more just like it but on different lines of algorithm.h
You gave it b.pprop, vs a.prop. I think given the error that the compiler failed to parse the struct's definition properly- check for syntax errors in the code just above it.
Never mind. I found the problem. I t was in a different part of the code that was calling the same algorithm functions. Sorry to bother everybody and thanks for trying to help.