Как мне индексировать массив классов по указателю с помощью cppyy - PullRequest
1 голос
/ 11 марта 2020

Я видел документы по индексированию массивов для базовых c типов в cppyy. Я не понял, как индексировать массив пользовательских типов.

take:

import cppyy


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

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

Если массив Foo объектов хранится в экземпляре Bar, Как мне индексировать этот массив для доступа к элементу Foo?

Если я попытаюсь:

bar.foo[0]

Я получу:

Объект 'Foo' делает не поддерживается индексация

1 Ответ

1 голос
/ 11 марта 2020

РЕДАКТИРОВАТЬ 2 : Следуя вашему предложению в комментариях ниже, оригинальный код теперь работает "из коробки" с выпуском cppyy 1.6.2. То есть, если прокси Python содержит тип указателя, индексация означает, что он представляет массив.

Как есть Foo* принимается как указатель на Foo, а не на массив Foo. Да, он может указывать на массив, но низкоуровневый C ++ (на самом деле C) неоднозначен, и это указатель на объект является общепринятым случаем. Если что-то является массивом, синтаксис массива работает, так как он снова становится однозначным:

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])

, который печатает:

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

, как и ожидалось.

Если вы не Не знаете длину массива, есть ли вариант, который вы можете использовать современные конструкции C ++, такие как std :: unique_ptr вместо наследия C? Они абсолютно недвусмысленны и, следовательно, легко автоматически связываются (они также значительно упрощают управление памятью):

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)

, который печатает ожидаемое:

0.0
2.0
4.0
6.0

Если вы действительно находитесь в незавидном состоянии Что касается поддержки устаревшего кода, я рекомендую немного исправиться с помощью C ++ (через JIT с cppdef) и помощников Python (подробнее о питонизации здесь: https://cppyy.readthedocs.io/en/latest/pythonizations.html). Например:

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)

, который печатает ожидаемое:

42.0

EDIT : еще несколько идей, основанных на вашем вопросе. Во-первых, пример приведения на стороне C ++:

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)

и пример приведения с использованием указателя arithmeti c на стороне Python:

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)

оба выводят ожидаемое:

42.0
13.0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...