-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
passing containers of char * (e.g. std::vector<char *>) does not work #2245
Comments
You probably need to make the container type opaque. The manual describes how to do so |
Thanks for the pointer, @molpopgen. In my case I can modify the wrapped class ( I can think of three behaviors that would be preferable to the current behavior:
As a reminder, the current behavior is that the bound code receives a pointer to seemingly random data. Silently corrupt data is never fun. |
I gave you bad advice! Sorry. Pybind11 converts python strings to c++ strings for good reason, to avoid memory leaks. Passing in pointer as you are doing makes ownership unclear, and therefore up to you. You can write a custom init to handle these cases, taking care to avoid memory leaks. Failing to compile wouldn't be correct because it makes assumptions about ownership. Auto conversion may not be correct because the char * may not be null-terminated. Etc.. It is up to you to avoid UB and leaks here. |
Hmmm, maybe I'm not stating the problem clearly enough. pybind11 does the expected thing (with some caveats) when a function's parameter is a
I think it's a bug that it doesn't do that when the parameter is a container of I assume that for bare Sorry for the delay in response—I'm in the middle of buying and selling houses/renovation. |
I see what you are saying. I just see a vector of char * to be a red flag in this case. If you define a c++ function taking such a vector, and want pybind11 to convert python data for you, then there may be no destructors for the pointers? I've not looked at the internal code to check, though. |
Why would pybind11 do something different for a bare Regardless, the current behavior is just plain wrong: a vector of |
You'd have to look at the code, but it is likely working in a loop and so the temporary is going out of scope. The current behavior is wrong, and probably either shouldn't compile or should warn and say that it isn't doing what you want. |
For a class that cannot change from vector of pointers to strings, etc., this is a workaround, although we're not correctly addressing ownership here: #include <vector>
#include <stdio.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
class Demo
{
public:
Demo(std::array<const char *, 2> strs)
{
printf("Got %s %s\n", strs[0], strs[1]);
}
Demo(const char *str1, const char *str2)
{
printf("Got %s %s\n", str1, str2);
}
};
PYBIND11_MODULE(demo, m)
{
// the real deal here
py::class_<Demo>(m, "Demo")
.def(py::init([](std::array<std::string, 2> a) {
// NOTE: the behavior in here depends on what the class
// is actually doing, as we're in memory leak territoy
// in general. This works for this specific example, though.
return Demo(std::array<const char *, 2>{a[0].c_str(), a[1].c_str()});
}))
.def(py::init<const char *, const char *>());
} |
Thanks for the feedback, @molpopgen. That's a nice clean workaround. I'm not trying to be dense, but I don't see where a memory leak could come from in the |
it all depends on what the fate of that container is, and on how the pointers were originally create ( |
Gotchya. I probably reached for it because of my relative familiarity with C instead of C++... I'm afraid my code may have a distinctly C-ish smell to it :-. Hopefully getting better every day though. |
Pybind doesn't try to work with C style arrays by design. You need a bit of cleverness to make it work, but it's certainly possible. Is there still an issue here or should we close this? |
The issue is that a bare |
The issue here is that the type caster for lists (to pybind11/include/pybind11/stl.h Lines 152 to 157 in e248869
In the case of This is definitely a bug, but I'm not immediately sure what's the best way of fixing it. |
m_text will changed by gc, how to fix this? Since below code works will.
|
Issue description
pybind11 does not seem to correctly pass containers of
char *
to Python. I'm using the example ofstd::array<const char *, N>
, butstd::vector
s have the same behavior. Thechar *
received by C++ seems to be pointing to a random memory location. The memory is still within the process, as there are no segfaults (that I have seen, anyway).Reproducible example code
This demo class has two separate constructors. One takes the type
std::array<const char *, 2>
, and the other takes the typesconst char *, const char*
. Thestd::array
version does the wrong thing, and thechar *
, when printed, just prints out garbage. Thechar *, char *
version does the right thing, and prints the passed in string as expected.I'm using pybind v2.5.0. I went through the docs on strings but didn't notice anything. Just guessing, but I would guess the problem might be shallow copies of pointers somewhere, since this only happens with containers. But I really have no clue.
This problem does not surface whenever
std::string
is used, which is another reason I think it may be related to shallow copies of pointers (sincestd::string
will copy its data when copied).After building, the following Python code demonstrates the issue:
The text was updated successfully, but these errors were encountered: