如何使用 cppyy 从指针索引类数组

Posted

技术标签:

【中文标题】如何使用 cppyy 从指针索引类数组【英文标题】:How do I index array of classes from a pointer with cppyy 【发布时间】:2020-03-11 16:03:25 【问题描述】:

我已经看过关于 cppyy 中基本类型的数组索引的文档。我还没弄清楚如何索引自定义类型的数组。

拍摄:

import cppyy


cppyy.cppdef("""
struct Foo

    float var;


struct Bar

    Foo* foo;    

""")

如果Foo 对象的数组存储在Bar 的实例中,我如何索引该数组以访问Foo 元素?

如果我尝试:

bar.foo[0]

我明白了:

'Foo' 对象不支持索引

【问题讨论】:

【参考方案1】:

编辑 2:按照您在下面 cmets 中的建议,原始代码现在可以在 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++(通过带有 cppdef 的 JIT)和 Python 助手(有关 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

编辑:根据您的问题提出更多想法。首先,一个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)

还有一个在 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

【讨论】:

感谢您的回复!不幸的是,这是 C 代码(甚至不是 C++),并且数组大小是动态分配的,因此不能将 foo 声明为数组。我做的解决方法看起来和你的很相似:``` cppyy.cppdef(""" template T* GetArrayElement(T* array, int index) return &array[index]; ```我不知道不喜欢这个解决方案,但它有效。我愿意做一些指针运算来获取元素而不是数组索引。你知道我会怎么做吗? 我在最后添加了一个基于指针算法的转换示例,以及另一个使用 python 化的 C++ 端转换。有关算术指针转换等低级内容的更多文档在这里:cppyy.readthedocs.io/en/latest/lowlevel.html 感谢您的更新。你能告诉我这一般是怎么做的吗?我正在考虑您提供的第二个数组索引选项(使用 FooGetter),但我想要更像我在评论中提供的模板版本。另外,我希望吸气剂适用于任何Foo,而不仅仅是Bar 中的那些。例如,如果我的 C 代码实例化了一个全局 Foo* global_foo,那么对它进行数组索引的 helper 和 pythonization 是什么? ...(续上)我的 python 是初级的。如果有一个 JIT 助手和 python 化来使全局 foo 的模板 getter 工作,在我看来,这可以添加到 cppyy 而不需要我用 getter 覆盖global_foo。如果 cppyy 知道基础类型是 T* 并且我尝试数组索引,我希望 cppyy 可以为我做指针数学,知道 T 的大小。 这是个好主意!从技术上讲,这样做并不是 100% 明确的,但我很确定在实际实践中会正确解决。

以上是关于如何使用 cppyy 从指针索引类数组的主要内容,如果未能解决你的问题,请参考以下文章

在 python 中使用 cppyy 时,指针变量引发错误为未知类型

cppyy - 如何调用接受集合的 c++ 函数?

使用cppyy时如何在python中创建子类?

如何在cppyy中加载库?

如何通过 C++ 中指向该数组的指针访问另一个类中的数组?

如何从基类动态创建和使用派生类?