1

I have seen docs about array indexing for basic types in cppyy. I haven't figured out how to index an array of custom types.

take:

import cppyy


cppyy.cppdef("""
struct Foo
{
    float var;
}

struct Bar
{
    Foo* foo;    
}
""")

If an array of Foo objects is stored in an instance of Bar, how do I index that array to access the Foo element?

If I try:

bar.foo[0]

I get:

'Foo' object does not support indexing

Ken
  • 693
  • 1
  • 7
  • 15

1 Answers1

1

EDIT 2: Following your suggestion in the comments below, the original code now works out-of-the-box with cppyy release 1.6.2. That is, if the Python proxy contains a pointer type, then indexing is taken to mean it represents an array.

As-is Foo* is taken as a pointer to Foo, not to an array of Foo. Yes, it could point to an array, but low-level C++ (C, really) is ambiguous and it being a pointer to an object is by far the common case. If something is an array, array syntax does work, as it becomes unambiguous again:

import cppyy

cppyy.cppdef("""
struct Foo
{
    float var;
};

struct Bar
{
    Foo foo[1];
};
""")

bar = cppyy.gbl.Bar()
print(len(bar.foo))
print(bar.foo[0])

which prints:

1
<cppyy.gbl.Foo object at 0x7f85ace370f0>

as expected.

If you don't know the length of the array, is there any option that you can use modern C++ constructs such as std::unique_ptr instead of heritage C? Those are completely unambiguous and thus easily automatically bound (they also make memory management a lot easier):

import cppyy

cppyy.cppdef("""
struct Foo
{
    float var;
};

struct Bar
{
    Bar(int num_foo) : foo(std::unique_ptr<Foo[]>{new Foo[num_foo]}) {
       for (int i = 0; i < num_foo; ++i)
           foo[i].var = (float)2.*i;
    }
    std::unique_ptr<Foo[]> foo;
};
""")

num_foo = 4
bar = cppyy.gbl.Bar(num_foo)
for i in range(num_foo):
    print(bar.foo[i].var)

which prints the expected:

0.0
2.0
4.0
6.0

If you really are in the unenviable position of maintaining legacy code, I recommend patching things up a little with C++ (through the JIT with cppdef) and Python helpers (more details on pythonization here: https://cppyy.readthedocs.io/en/latest/pythonizations.html). For example:

import cppyy

cppyy.cppdef("""
struct Foo
{
    float var;
};

struct Bar
{
    Bar() : foo(new Foo{}) { foo[0].var = 42.f; }
    // memory mgmt here ...
    Foo* foo;
};

Foo* Bar_get_foo_indexed(Bar* b, int idx) {
   return &b->foo[idx];
}
""")

# pythonize the getter
class FooGetter(object):
    def __get__(self, obj, kls=None):
        self.obj = obj
        return self
    def __getitem__(self, idx):
        return cppyy.gbl.Bar_get_foo_indexed(self.obj, idx)

cppyy.gbl.Bar.foo = FooGetter()

bar = cppyy.gbl.Bar()
print(bar.foo[0].var)

which prints the expected:

42.0

EDIT: some more ideas based on your question. First, a casting example on the C++ side:

cppyy.cppdef("""
struct Foo
{
    float var;
};

struct Bar
{
    Bar() : foo(new Foo[2]) { foo[0].var = 42.f; foo[1].var = 13.f; }
    // memory mgmt here ...
    Foo* foo;
};

template<int N>
struct FooArrayWrapper {
    Foo foo[N];
};

template<int N>
FooArrayWrapper<N>* cast_foo_array(Foo*& f) {
    return reinterpret_cast<FooArrayWrapper<N>*>(f);
}

""")

def make_foo_wrapper_init(func):
    def wrapper_init(self, foo_size, *args, **kwargs):
        func(self)
        self.__dict__['foo_array'] = cppyy.gbl.cast_foo_array[foo_size](self.foo).foo
    return wrapper_init

cppyy.gbl.Bar.__init__ = make_foo_wrapper_init(cppyy.gbl.Bar.__init__)

bar = cppyy.gbl.Bar(2)
print(bar.foo_array[0].var)
print(bar.foo_array[1].var)

and a casting example using pointer arithmetic on the Python side:

cppyy.cppdef("""
struct Foo
{
    float var;
};

struct Bar
{
    Bar() : foo(new Foo[2]) { foo[0].var = 42.f; foo[1].var = 13.f; }
    // memory mgmt here ...
    Foo* foo;
};

""")

bar = cppyy.gbl.Bar()
for i in range(2):
    print(cppyy.bind_object(cppyy.addressof(bar.foo)+i*cppyy.sizeof(cppyy.gbl.Foo), cppyy.gbl.Foo).var)

both print the expected:

42.0
13.0
Wim Lavrijsen
  • 3,453
  • 1
  • 9
  • 21
  • Thanks for the reply! Unfortunately this is C code (not even C++) and the array size is dynamically allocated, so `foo` can't be declared as an array. The workaround I made looks very similar to yours: ``` cppyy.cppdef(""" template T* GetArrayElement(T* array, int index) { return &array[index]; } ``` I don't love this solution, but it works. I'd be willing to do some pointer arithmetic to get to the elements instead of array indexing. Do you know how I would do that? – Ken Mar 12 '20 at 18:29
  • I added a casting example based on pointer arithmetic at the end, as well as another casting C++-side with pythonization. More documentation on low-level stuff such as pointer casting for arithmetic is here: https://cppyy.readthedocs.io/en/latest/lowlevel.html – Wim Lavrijsen Mar 13 '20 at 06:01
  • Thanks for those updates. Can you tell me how this can be done generically? I'm coming around to the second, array indexing option you gave (with the FooGetter), but I'd want something more like the template version I provided in my comment. Also, I'd want the getter to work for any `Foo`, not just those inside `Bar`. For example, if my C code instantiated a global `Foo* global_foo`, what is the helper and pythonization to array index it? – Ken Mar 13 '20 at 13:21
  • ... (continued from above) My python is rudimentary. If there is a JIT helper and pythonization to make the template getter of the global foo work, it would seem to me that this could be added to cppyy and not require me to overwrite `global_foo` with the getter. If cppyy knows the underlying type is `T*` and I attempt array indexing, I would expect cppyy could do the pointer math for me, knowing the size of `T`. – Ken Mar 13 '20 at 13:22
  • That's a great idea! It's not technically 100% unambiguous to do it that way, but I'm pretty sure that in actual practice that will resolve correctly. – Wim Lavrijsen Mar 13 '20 at 21:34
  • See edit: now works out-of-the-box with cppyy release 1.6.2. – Wim Lavrijsen Mar 16 '20 at 06:18
  • Wow thanks! I have updated and it (mostly) works as expected. I have found one failure with it, but need to reduce to small example. I'll submit a bug on bitbucket. – Ken Mar 16 '20 at 20:37