pthom / litgen

litgen: a pybind11 automatic generator for humans who like nice code and API documentation. Also a C++ transformer tool
https://pthom.github.io/litgen
GNU General Public License v3.0
47 stars 6 forks source link

Failure to bind vector of smart pointers #21

Open grayj2 opened 4 days ago

grayj2 commented 4 days ago

Starting to use litgen to make bindings and ran into an issue when using vectors of smart points. litgen does not seem to interpret vectors of smart pointers correctly.

example.h

#include <vector>

class ClassA {};

class ClassB {
  public:
    std::vector<std::shared_ptr<ClassA>> vec_shared_ptrs;
    std::vector<std::unique_ptr<ClassA>> vec_unique_ptrs;
    std::vector<ClassA> vec;
    std::shared_ptr<ClassA> shared_ptr;
    std::unique_ptr<ClassA> unique_ptr;
};

resulting stubs with no options specified which are obviously wrong and do not throw any errors when generating other than black formatting errors.

class ClassA:
    def __init__(self) -> None:
        """Autogenerated default constructor"""
        pass

class ClassB:
    vec_shared_ptrs: std.vector<ClassA>
    vec_unique_ptrs: std.vector<ClassA>
    vec: List[ClassA]
    shared_ptr: ClassA
    unique_ptr: ClassA
    def __init__(self) -> None:
        """Autogenerated default constructor"""
        pass

I was able to get around this and get something more expected using the following type replacement option and regex

    options = litgen.LitgenOptions()
    options.type_replacements.add_first_replacement(r"std::vector<std::(?:unique_ptr|shared_ptr)<(.*)>>", r"std::vector<\1>")

This results in stubs looking like this

class ClassA:
    def __init__(self) -> None:
        """Autogenerated default constructor"""
        pass

class ClassB:
    vec_shared_ptrs: List[ClassA]
    vec_unique_ptrs: List[ClassA]
    vec: List[ClassA]
    shared_ptr: ClassA
    unique_ptr: ClassA
    def __init__(self) -> None:
        """Autogenerated default constructor"""
        pass

Im not sure if this is best approach or just the naive solution. Considering how smart pointers of a class and a vector of classes gets bound, I would expect to get this result. Would appreciate your insight.

pthom commented 3 days ago

Binding smart pointers with pydind11 is complex.

Concerning the stubs, your solution was a step in the right direction. I fixed it inside cpp_to_python, by applying the changes to unique_ptr and shared_ptr before vector:

https://github.com/pthom/litgen/blob/5c0740da6bb6d72400727d1f9c5158fc035b1840/src/litgen/internal/cpp_to_python.py#L517-L520

Now, concerning your usage:

For an example, see this integration test:

https://github.com/pthom/litgen/blob/main/src/litgen/integration_tests/mylib/smart_ptr.h

And its bindings:

https://github.com/pthom/litgen/blob/main/src/litgen/integration_tests/mylib/smart_ptr.h.pyi

Where I had to specify the holder type for SmartElem

https://github.com/pthom/litgen/blob/5c0740da6bb6d72400727d1f9c5158fc035b1840/src/litgen/integration_tests/autogenerate_mylib.py#L53-L54

Read here the doc for class_held_as_shared__regex:

https://github.com/pthom/litgen/blob/5c0740da6bb6d72400727d1f9c5158fc035b1840/src/litgen/options.py#L405-L420

pthom commented 3 days ago

Note: you will need to update litgen to the latest commit

grayj2 commented 1 day ago

Thank you for the updates! This did the trick.

Understood about the caveats on smart pointers. Mainly just tinkering at this point to understand how to use litgen. So far it seems pretty straight forward.