【问题标题】:Passing Array of Objects OR Array of Arrays Between Python & C++ Library在 Python 和 C++ 库之间传递对象数组或数组数组
【发布时间】:2017-08-20 12:36:24
【问题描述】:

我有一个 C++ 函数的 extern "C" 声明,编译为 Ubuntu 上共享库的一部分,getVPHistory

void getVPHistory(actorPos allPostions[], uint numAct, uint numDim, uint numState) {
uint actorDimPairCount = numAct * numDim;
uint actorDimPairIndex = 0;
float val = 1.0;
for (uint actor = 0; actor < numAct; ++actor) {
  for (uint dim = 0; dim < numDim; ++dim) {
    actorPos *ap = &allPostions[actorDimPairIndex];
    ++actorDimPairIndex;
    ap->actor = actor;
    ap->dim = dim;
    ap->nState = numState;
    for (uint state = 0; state < numState; ++state) {
      ap->pos[state] = val;
      val += 0.5; // incrementing is just for testing
    }
  }
}

}

actorPos 对象定义为:

using actorPos = struct singleActorPositions {
  unsigned int actor;
  unsigned int dim;
  unsigned int nState;
  float pos[1000]; // possible upper limit of nState

};

我在python中使用ctypes来尝试使用这个函数,代码如下:

import ctypes as c

actorCnt = 2 dimensionCnt = 2; stateCnt = 2

smpLib = c.cdll.LoadLibrary('libsmpDyn.so')
class PosHist(c.Structure):
    _fields_ = [("actorID",c.c_uint),\
    ("dimensionID",c.c_uint),\
    ("stateCnt",c.c_uint),\
    ('positions',c.c_float*stateCnt)]
posHistArrType = PosHist*(actorCnt*dimensionCnt)

posHists = posHistArrType()

proto_PS = c.CFUNCTYPE(c.c_voidp,c.POINTER(posHistArrType),c.c_uint,c.c_uint,c.c_uint)
getVPHistory = proto_PS(('getVPHistory',smpLib))

getVPHistory(posHists,actorCnt,dimensionCnt,stateCnt)

这运行没有错误,但是当我尝试访问posHists 中的返回数据时,我看到只有第一个对象已被填充。运行这个:

for p in posHists:
  print('Actor: %d, Dimension: %d'%(p.actorID,p.dimensionID))
  for o in p.positions:
    print('\t%0.2f'%o)

给我这个:

Actor: 0, Dimension: 0
    1.00
    1.50
Actor: 0, Dimension: 0
    0.00
    0.00
Actor: 0, Dimension: 0
    0.00
    0.00
Actor: 0, Dimension: 0
    0.00
    0.00

即,在数组中的第一个 PosHist 对象之后,所有其他对象仍然只显示初始值。我可以确认getVPHistory 函数完全填充了数组,所以在我看来,整个数组没有传回我的posHists 变量的问题。请帮忙!

编辑

怀疑结构是问题的一部分——最终超出了问题的需要——我已将其简化为仅使用 3-d 数组。 C getVPHistory 函数现在是:

  void getVPHistory(float hist[100][100][100], uint numAct, uint numDim, uint numState)
  {
    // talk a little
    cout << "Actors: " << numAct << "Dimensions: " << numDim << "States: " << numState << endl;
    
    // loop through all actors, dims, and states and store the positions
    float val = 1.0;
    for (uint actor = 0; actor < numAct; ++actor)
    {
      //cout << "Actor " << actor << endl;
      for (uint dim = 0; dim < numDim; ++dim)
      {
        //cout << "Dimension " << dim << endl;
        for (uint state = 0; state < numState; ++state)
        {
          hist[actor][dim][state] = val;
          cout << "A: " << actor << " D: " << dim << " S: " << state << "\tAdded position: " << val << endl;
          val += 0.5;
        }
      }
    }       
  }

python 代码现在是:

actorCnt = 3; dimensionCnt = 2; stateCnt = 5
posHistType = ((c.c_float * stateCnt) * dimensionCnt)*actorCnt

proto_PS = c.CFUNCTYPE(c.c_voidp,c.POINTER(posHistType),c.c_uint,c.c_uint,c.c_uint)
getVPHistory = proto_PS(('getVPHistory',smpLib))

posHists = posHistType()
getVPHistory(posHists,actorCnt,dimensionCnt,stateCnt)

for a in range(actorCnt):
  for d in range(dimensionCnt):
    print('Pos Hist for Actor %d, Dimension %d:'%(a,d))
    print('\t[%s]'%', '.join(['%0.2f'%p for p in posHists[a][d]]))

所以现在我将一个指向内存的指针传递给一个 3-d 数组,数据用 C 填充,然后我在 python 中读取数据。 C 的控制台输出验证数据是否被存储:

Actors: 3 Dimensions: 2 States: 5
A: 0 D: 0 S: 0  Added position: 1
A: 0 D: 0 S: 1  Added position: 1.5
A: 0 D: 0 S: 2  Added position: 2
A: 0 D: 0 S: 3  Added position: 2.5
...
A: 2 D: 1 S: 2  Added position: 14.5
A: 2 D: 1 S: 3  Added position: 15
A: 2 D: 1 S: 4  Added position: 15.5

然而,即使没有结构的复杂性,我也有同样的问题,即只有第一个最里面的数组返回数据:

Pos Hist for Actor 0, Dimension 0:
    [1.00, 1.50, 2.00, 2.50, 3.00]
Pos Hist for Actor 0, Dimension 1:
    [0.00, 0.00, 0.00, 0.00, 0.00]
Pos Hist for Actor 1, Dimension 0:
    [0.00, 0.00, 0.00, 0.00, 0.00]
Pos Hist for Actor 1, Dimension 1:
    [0.00, 0.00, 0.00, 0.00, 0.00]
Pos Hist for Actor 2, Dimension 0:
    [0.00, 0.00, 0.00, 0.00, 0.00]
Pos Hist for Actor 2, Dimension 1:
    [0.00, 0.00, 0.00, 0.00, 0.00]

如果我将 C 函数定义中的数组硬编码为 hist[3][2][5] - 与在 python 中创建的相同 - 会发生相同的行为。

【问题讨论】:

  • 这个stateCnt = 2 ... ('positions',c.c_float*stateCnt) 似乎与float pos[1000]有些不匹配
  • 如果你指定一个数组长度within一个结构,你必须始终使用相同的长度。您将它们放在一个 array 中。或者,使用 pointer-to-structure 代替。
  • 谢谢,但pos[1000] 不是问题,因为getVPHistory() 函数在从 C 中以相同方式调用时可以正常工作。无论如何,我看不到如何分配足够的内存对于 1000 个条目,则仅填充 2 的数据会导致传回的内存仅显示 1 中的数据!
  • 假设使用的 C 编译器支持 VLA(C++ 编译器不会,顺便说一句)试试这个:void getVPHistory(uint numAct, uint numDim, uint numState, float hist[numAct][numDim][numSate]);(这里的顺序很重要)
  • 试过@alk,没有运气(使用 gcc)。

标签: python arrays ctypes


【解决方案1】:

您必须在 C++ 和 Python 之间声明具有相同尺寸的结构:

工作(Windows)C++ DLL:

typedef unsigned int uint;

struct actorPos
{
    unsigned int actor;
    unsigned int dim;
    unsigned int nState;
    float pos[1000]; // possible upper limit of nState
};

extern "C" __declspec(dllexport)
void getVPHistory(actorPos allPostions[], uint numAct, uint numDim, uint numState)
{
    uint actorDimPairIndex = 0;
    float val = 1.0;
    for(uint actor = 0; actor < numAct; ++actor)
    {
        for(uint dim = 0; dim < numDim; ++dim)
        {
            actorPos* ap = &allPostions[actorDimPairIndex];
            ++actorDimPairIndex;
            ap->actor = actor;
            ap->dim = dim;
            ap->nState = numState;
            for(uint state = 0; state < numState; ++state)
            {
                ap->pos[state] = val;
                val += 0.5; // incrementing is just for testing
            }
        }
    }
}

Python 代码:

import ctypes as c

actorCnt = 2; dimensionCnt = 2; stateCnt = 2

smpLib = c.CDLL('test')
class PosHist(c.Structure):
    _fields_ = [("actorID",c.c_uint),\
    ("dimensionID",c.c_uint),\
    ("stateCnt",c.c_uint),\
    ('positions',c.c_float*1000)] # declare same size as struct in C++
posHistArrType = PosHist*(actorCnt*dimensionCnt)

posHists = posHistArrType()

proto_PS = c.CFUNCTYPE(c.c_voidp,c.POINTER(posHistArrType),c.c_uint,c.c_uint,c.c_uint)
getVPHistory = proto_PS(('getVPHistory',smpLib))

getVPHistory(posHists,actorCnt,dimensionCnt,stateCnt)

for p in posHists:
    print('Actor: %d, Dimension: %d'%(p.actorID,p.dimensionID))

    # Here, iterate only the number of states returned...
    for i in range(p.stateCnt):
        print('\t%0.2f'%p.positions[i])

输出:

Actor: 0, Dimension: 0
    1.00
    1.50
Actor: 0, Dimension: 1
    2.00
    2.50
Actor: 1, Dimension: 0
    3.00
    3.50
Actor: 1, Dimension: 1
    4.00
    4.50

【讨论】:

  • 感谢@Mark-Tolonen。由于我很久没有得到有用的答案,我已经转向了一个不同的解决方案,它只使用了一个常规的平面数组,然后我将它重新塑造成我在 Python 中想要的结构。我会尽快测试您的解决方案,以便标记您的答案。
猜你喜欢
  • 2014-05-16
  • 2016-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-02
  • 1970-01-01
  • 2011-06-18
相关资源
最近更新 更多