在Python和Python之间传递对象数组或数组数组C ++库

时间:2017-08-20 12:36:24

标签: python arrays ctypes

我有一个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变量。求救!

修改

怀疑结构是问题的一部分 - 并且最终还不仅仅是问题所需要的 - 我已将其简化为仅使用三维数组。 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]]))

所以现在我传递一个指向内存的指针,用于三维数组,数据用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中创建的相同 - 会发生相同的行为。

1 个答案:

答案 0 :(得分:0)

您必须在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