Search code examples
c++python-3.xlinuxswig

SWIG: Access Array of Structs in Python


Say I have the following static constexpr array of c struct:

#include <cstdint>

namespace ns1::ns2 {
    struct Person {
        char name[32];
        uint8_t age;    
    };
    static constexpr Person PERSONS[] = {
        {"Ken", 8},
        {"Cat", 27}
    };
}

How can I access elements in ns1::ns2::PERSONS in python by using swig?

One way I can think of is to create a accessor like const Person& get(uint32_t index) in the swig interface file. Tho, I wonder whether there is a more elegant way that I don't have to create an accessor function for each array of c struct.

Thanks!


Solution

  • One way I can think of is to create a accessor like const Person& get(uint32_t index) in the swig interface file.

    According to 5.4.5 Arrays in the SWIG documentation, that's the way to do it:

    %module test
    
    %include <stdint.i>
    
    %inline %{
    #include <cstdint>
    
    namespace ns1::ns2 {
        struct Person {
            char name[32];
            uint8_t age;
        };
        static constexpr Person PERSONS[] = {
            {"Ken", 8},
            {"Cat", 27}
        };
    }
    
    // helper function
    const ns1::ns2::Person* Person_get(size_t index) {
        if(index < sizeof(ns1::ns2::PERSONS) / sizeof(ns1::ns2::Person))  // protection
            return ns1::ns2::PERSONS + index;
        else
            return nullptr;
    }
    %}
    

    Demo:

    >>> import test
    >>> test.PERSONS.name # can only access the first element
    'Ken'
    >>> test.Person_get(1).name
    'Cat'
    >>> test.Person_get(2).name
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'NoneType' object has no attribute 'name'
    

    SWIG can also generate array wrappers for you with 11.2.2 carrays.i, but there is no bounds checking:

    %module test
    
    %include <stdint.i>
    
    %inline %{
    #include <cstdint>
    
    namespace ns1::ns2 {
        struct Person {
            char name[32];
            uint8_t age;
        };
        static constexpr Person PERSONS[] = {
            {"Ken", 8},
            {"Cat", 27}
        };
    }
    %}
    
    %include <carrays.i>
    %array_functions(ns1::ns2::Person,arrayPerson);
    

    Demo:

    >>> import test
    >>> test.arrayPerson_getitem(test.PERSONS,1).name
    'Cat'