-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Problem with constructor for a class with custom operator new #948
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
Comments
Does it work if you also add dummy placement new operator, i.e. void *operator new(size_t, void *ptr) { return ptr; }
void *operator new[](size_t, void *ptr) { return ptr; } |
Thank you for the answer, Wenzel! static void* operator new(size_t, void *ptr) { return ptr; }
static void* operator new[](size_t, void *ptr) { return ptr; } And it works even without dummy operator, just the first line. The difference with code in the question is the presence of |
Edit for future readers: #805 also includes changes that makes the workaround described below unnecessary. PR #805 can help with a class that doesn't support placement new: it can handle a pointer, so you could write: cl.def(py::init([](Arg a, int b) { return new Class(a, b); })) We could, in theory, automatically map a |
Thank you, Jason! .def(py::init([]() { return new Example(); })) It compiles, but when I try to import this module into python (v.2.7), I get the next error |
Added a test for a factory init workaround for a type without placement new support (issue pybind#948).
I think that error is happening because your I've added a test to the PR #805 branch where this is working. |
Thank you, Jason! So the example looks like this: class Example {
public:
static void* operator new(std::size_t size);
static void operator delete(void* p, std::size_t size);
Example() {}
private:
static MyHeapBook heapBook;
};
#define PAGE_SIZE 4096
MyHeapBook Example::heapBook(sizeof(Example), PAGE_SIZE);
void *Example::operator new(std::size_t size) {
py::print("operator new called");
return heapBook.allocate();
}
void Example::operator delete(void *p, std::size_t size) {
py::print("operator delete called");
heapBook.free(p);
}
PYBIND11_MODULE(test, m) {
m.doc() = "test";
py::class_<Example>(m, "Example")
.def(py::init([]() { return new Example(); }))
;
} where MyHeapBook is defined like this (I am omitting a lot of stuff, because I do not have an access to the source code): class MyHeapPage;
class MyHeapBook {
public:
// Constructors and destructors
MyHeapBook(std::size_t objectSize, unsigned int numberOfObjectsInPage);
virtual ~MyHeapBook();
// Memory management
void* allocate(); // Allocates memory for one object
void free(void* objectPointer); // Frees the memory at address objectPointer
/*...*/
private:
MyHeapPage* firstPage; // The first page
void* firstFreeObject; // The address of the first free object
std::size_t objectSize; // The number of bytes per object
unsigned int numberOfObjectsInPage; // The number of objects in the page
std::mutex *mutex;
/*...*/
};
void* MyHeapBook::allocate() {
mutex->lock();
if (firstFreeObject == 0) {
firstPage = new MyHeapPage(objectSize,
numberOfObjectsInPage, firstPage); // add a new memory page
firstFreeObject = firstPage->objectArray;
}
void* objectPointer = (char*)firstFreeObject + sizeof(void*); // skip the header
firstFreeObject = (void*)(*((std::size_t*)firstFreeObject)); // move to the next free object
mutex->unlock();
return objectPointer;
}
void MyHeapBook::free(void* objectPointer) {
mutex->lock();
// the location of the header of the freed object
char* headerLocation = (char*)objectPointer - sizeof(void*);
// make the previous "first free object" the next object of the freed object
*((std::size_t*)headerLocation) = (std::size_t)(firstFreeObject);
// the freed object becomes the first free object
firstFreeObject = (void*)headerLocation;
mutex->unlock();
}
class MyHeapPage {
public:
// Constructors and destructors
MyHeapPage(std::size_t objectSize,
unsigned int numberOfObjectsInPage, MyHeapPage* nextPage);
virtual ~MyHeapPage();
/*...*/
private:
friend class MyHeapBook;
void* objectArray; // The object array
MyHeapPage* nextPage; // The next page in the heap book
/*...*/
}; And the error in Python is:
Any ideas on the source of the error or on a workaround are appreciated! |
What pybind with the factory PR is essentially:
Ah, I see the issue: your class doesn't have |
Thank you, Jason! |
#952 should solve the delete problem. |
Thank you very much, Jason! |
Thanks for the report. (I'm reopening for bookkeeping just until the PRs get merged). |
This allows you to use: cls.def(py::init(&factory_function)); where `factory_function` returns a pointer, holder, or value of the class type (or a derived type). Various compile-time checks (static_asserts) are performed to ensure the function is valid, and various run-time type checks where necessary. The feature is optional, and requires including the <pybind11/factory.h> header. Some other details of this feature: - The `py::init` name doesn't conflict with the templated no-argument `py::init<...>()`, but keeps the naming consistent (especially if we decide to roll this into the core, documented approach replacing raw placement-new `__init__`s). - If returning a CppClass (whether by value or pointer) when an CppAlias is required (i.e. python-side inheritance and a declared alias), a dynamic_cast to the alias is attempted (for the pointer version); if it fails, or if returned by value, an Alias(Class &&) constructor is invoked. If this constructor doesn't exist, a runtime error occurs. - for holder returns when an alias is required, we try a dynamic_cast of the wrapped pointer to the alias to see if it is already an alias instance; if it isn't, we raise an error. - `py::init(class_factory, alias_factory)` is also available that takes two factories: the first is called when an alias is not needed, the second when it is. - It also allows using a factory init to workaround binding a type without placement new support (issue pybind#948), which is basically impossible with this approach.
This allows you to use: cls.def(py::init(&factory_function)); where `factory_function` returns a pointer, holder, or value of the class type (or a derived type). Various compile-time checks (static_asserts) are performed to ensure the function is valid, and various run-time type checks where necessary. The feature is optional, and requires including the <pybind11/factory.h> header. Some other details of this feature: - The `py::init` name doesn't conflict with the templated no-argument `py::init<...>()`, but keeps the naming consistent (especially if we decide to roll this into the core, documented approach replacing raw placement-new `__init__`s). - If returning a CppClass (whether by value or pointer) when an CppAlias is required (i.e. python-side inheritance and a declared alias), a dynamic_cast to the alias is attempted (for the pointer version); if it fails, or if returned by value, an Alias(Class &&) constructor is invoked. If this constructor doesn't exist, a runtime error occurs. - for holder returns when an alias is required, we try a dynamic_cast of the wrapped pointer to the alias to see if it is already an alias instance; if it isn't, we raise an error. - `py::init(class_factory, alias_factory)` is also available that takes two factories: the first is called when an alias is not needed, the second when it is. - It also allows using a factory init to workaround binding a type without placement new support (issue pybind#948), which is basically impossible with this approach.
This allows you to use: cls.def(py::init(&factory_function)); where `factory_function` returns a pointer, holder, or value of the class type (or a derived type). Various compile-time checks (static_asserts) are performed to ensure the function is valid, and various run-time type checks where necessary. The feature is optional, and requires including the <pybind11/factory.h> header. Some other details of this feature: - The `py::init` name doesn't conflict with the templated no-argument `py::init<...>()`, but keeps the naming consistent (especially if we decide to roll this into the core, documented approach replacing raw placement-new `__init__`s). - If returning a CppClass (whether by value or pointer) when an CppAlias is required (i.e. python-side inheritance and a declared alias), a dynamic_cast to the alias is attempted (for the pointer version); if it fails, or if returned by value, an Alias(Class &&) constructor is invoked. If this constructor doesn't exist, a runtime error occurs. - for holder returns when an alias is required, we try a dynamic_cast of the wrapped pointer to the alias to see if it is already an alias instance; if it isn't, we raise an error. - `py::init(class_factory, alias_factory)` is also available that takes two factories: the first is called when an alias is not needed, the second when it is. - It also allows using a factory init to workaround binding a type without placement new support (issue pybind#948), which is basically impossible with this approach.
This allows you to use: cls.def(py::init(&factory_function)); where `factory_function` returns a pointer, holder, or value of the class type (or a derived type). Various compile-time checks (static_asserts) are performed to ensure the function is valid, and various run-time type checks where necessary. The feature is optional, and requires including the <pybind11/factory.h> header. Some other details of this feature: - The `py::init` name doesn't conflict with the templated no-argument `py::init<...>()`, but keeps the naming consistent (especially if we decide to roll this into the core, documented approach replacing raw placement-new `__init__`s). - If returning a CppClass (whether by value or pointer) when an CppAlias is required (i.e. python-side inheritance and a declared alias), a dynamic_cast to the alias is attempted (for the pointer version); if it fails, or if returned by value, an Alias(Class &&) constructor is invoked. If this constructor doesn't exist, a runtime error occurs. - for holder returns when an alias is required, we try a dynamic_cast of the wrapped pointer to the alias to see if it is already an alias instance; if it isn't, we raise an error. - `py::init(class_factory, alias_factory)` is also available that takes two factories: the first is called when an alias is not needed, the second when it is. - It also allows using a factory init to workaround binding a type without placement new support (issue pybind#948), which is basically impossible with this approach.
I've updated #805 with some performance optimizations that make the factory constructors just as efficient as preallocation + placement new, so switched the default |
Great! Thank you, Jason! |
This allows you to use: cls.def(py::init(&factory_function)); where `factory_function` returns a pointer, holder, or value of the class type (or a derived type). Various compile-time checks (static_asserts) are performed to ensure the function is valid, and various run-time type checks where necessary. The feature is optional, and requires including the <pybind11/factory.h> header. Some other details of this feature: - The `py::init` name doesn't conflict with the templated no-argument `py::init<...>()`, but keeps the naming consistent (especially if we decide to roll this into the core, documented approach replacing raw placement-new `__init__`s). - If returning a CppClass (whether by value or pointer) when an CppAlias is required (i.e. python-side inheritance and a declared alias), a dynamic_cast to the alias is attempted (for the pointer version); if it fails, or if returned by value, an Alias(Class &&) constructor is invoked. If this constructor doesn't exist, a runtime error occurs. - for holder returns when an alias is required, we try a dynamic_cast of the wrapped pointer to the alias to see if it is already an alias instance; if it isn't, we raise an error. - `py::init(class_factory, alias_factory)` is also available that takes two factories: the first is called when an alias is not needed, the second when it is. - It also allows using a factory init to workaround binding a type without placement new support (issue pybind#948), which is basically impossible with this approach.
This allows you to use: cls.def(py::init(&factory_function)); where `factory_function` returns a pointer, holder, or value of the class type (or a derived type). Various compile-time checks (static_asserts) are performed to ensure the function is valid, and various run-time type checks where necessary. The feature is optional, and requires including the <pybind11/factory.h> header. Some other details of this feature: - The `py::init` name doesn't conflict with the templated no-argument `py::init<...>()`, but keeps the naming consistent (especially if we decide to roll this into the core, documented approach replacing raw placement-new `__init__`s). - If returning a CppClass (whether by value or pointer) when an CppAlias is required (i.e. python-side inheritance and a declared alias), a dynamic_cast to the alias is attempted (for the pointer version); if it fails, or if returned by value, an Alias(Class &&) constructor is invoked. If this constructor doesn't exist, a runtime error occurs. - for holder returns when an alias is required, we try a dynamic_cast of the wrapped pointer to the alias to see if it is already an alias instance; if it isn't, we raise an error. - `py::init(class_factory, alias_factory)` is also available that takes two factories: the first is called when an alias is not needed, the second when it is. - It also allows using a factory init to workaround binding a type without placement new support (issue pybind#948), which is basically impossible with this approach.
This allows you to use: cls.def(py::init(&factory_function)); where `factory_function` returns a pointer, holder, or value of the class type (or a derived type). Various compile-time checks (static_asserts) are performed to ensure the function is valid, and various run-time type checks where necessary. The feature is optional, and requires including the <pybind11/factory.h> header. Some other details of this feature: - The `py::init` name doesn't conflict with the templated no-argument `py::init<...>()`, but keeps the naming consistent (especially if we decide to roll this into the core, documented approach replacing raw placement-new `__init__`s). - If returning a CppClass (whether by value or pointer) when an CppAlias is required (i.e. python-side inheritance and a declared alias), a dynamic_cast to the alias is attempted (for the pointer version); if it fails, or if returned by value, an Alias(Class &&) constructor is invoked. If this constructor doesn't exist, a runtime error occurs. - for holder returns when an alias is required, we try a dynamic_cast of the wrapped pointer to the alias to see if it is already an alias instance; if it isn't, we raise an error. - `py::init(class_factory, alias_factory)` is also available that takes two factories: the first is called when an alias is not needed, the second when it is. - It also allows using a factory init to workaround binding a type without placement new support (issue pybind#948), which is basically impossible with this approach.
This allows you to use: cls.def(py::init(&factory_function)); where `factory_function` returns a pointer, holder, or value of the class type (or a derived type). Various compile-time checks (static_asserts) are performed to ensure the function is valid, and various run-time type checks where necessary. The feature is optional, and requires including the <pybind11/factory.h> header. Some other details of this feature: - The `py::init` name doesn't conflict with the templated no-argument `py::init<...>()`, but keeps the naming consistent (especially if we decide to roll this into the core, documented approach replacing raw placement-new `__init__`s). - If returning a CppClass (whether by value or pointer) when an CppAlias is required (i.e. python-side inheritance and a declared alias), a dynamic_cast to the alias is attempted (for the pointer version); if it fails, or if returned by value, an Alias(Class &&) constructor is invoked. If this constructor doesn't exist, a runtime error occurs. - for holder returns when an alias is required, we try a dynamic_cast of the wrapped pointer to the alias to see if it is already an alias instance; if it isn't, we raise an error. - `py::init(class_factory, alias_factory)` is also available that takes two factories: the first is called when an alias is not needed, the second when it is. - It also allows using a factory init to workaround binding a type without placement new support (issue pybind#948), which is basically impossible with this approach.
Fixed now (via #805). |
Hello, all!
I have a problem exposing a class with custom operator
new
(custom allocation in a heap book).Here is an example:
The problem arises due to the fact that this statement
is a shorthand for
which results in the next error:
pybind11/pybind11.h:1320:60: error: no matching function for call to ‘Example::operator new(sizetype, Base*&)’ cl.def("__init__", [](Base *self_, Args... args) { new (self_) Base(args...); }, extra...);
It worked in Boost.Python out-of-the-box, because they have, apparently, different way of creating a constructor's bindings.
How it is possible to resolve this problem?
The text was updated successfully, but these errors were encountered: