ctypes-结构包含垃圾/不正确的数据

时间:2018-08-22 23:04:03

标签: python c++ 64-bit ctypes

我有一个C ++ DLL(64位),它以指向结构的双指针(即,指向结构数组的指针)作为输入。该函数动态分配结构数组,然后填充每个结构元素(元素包括:整数,浮点数,双精度数,静态数组,另一个结构和动态分配的数组)。

我创建了一个python类来模仿结构(所有数据元素)。但是,当我从python调用C ++函数-CreateROIs(..)-时,对于结构中的某些元素,我得到了垃圾/错误值。

例如,我将获得ppMem [0] .spic = 24609435234

有趣的是,如果我将相同的数据结构传递回C ++ DLL并打印出C ++中的元素,它们仍然是正确的(下面的部分没有提供功能/代码)。

我假设在64位python和C ++ DLL之间移动数据时,数据类型(双精度/浮点型)存在问题。也许没有连续的存储结构?我似乎无法对其进行追踪...无论如何,我们非常感谢您的帮助!

这是我的代码,感谢您的帮助!

C ++ DLL代码:

// TriangleMesh struct where mesh is defined by vertices connected via triangles
// <summary>
extern "C"

struct MyDLL_API TriangleMesh
{
    int nbrVertices;
    int nbrTriangles;

    float* vertices;
    int* triangles;
};

extern "C"
struct MyDLL_API ROI 
{
    double xmm;
    double ymm;
    double zmm;
    double diameter;
    double level;
    double volume;
    double threshold;
    int isMesh;
    double mVol;
    double mass;
    double meanD;
    double dve;
    double dla;
    double dsa;
    double dla3D;
    double spike; 
    float sphere;

    float lais[6];  
    float sais[6];  
    float la3D[6];      

    TriangleMesh mesh;
};

extern "C" MyDLL_API
void CreateROIs(ROI** ppMem, int* NumROIs);

void CreateROIs(ROI** ppMem, int* NumROIs)
{
    *ppMem = (ROI *)malloc(sizeof(ROI) * (*NumROIs));

    for (int i = 0; i < *nbrNodules; i++)
    {
        (*ppMem)[i].xmm = double(i + 1.0);
        (*ppMem)[i].ymm = double(i + 1.0);
        (*ppMem)[i].zmm = double(i + 1.0);
        (*ppMem)[i].diameter = double(i + 1.0);
        (*ppMem)[i].level = double(i + 1.0);
        (*ppMem)[i].volume= double(i + 1.0);
        (*ppMem)[i].threshold = double(i + 1.0);
        (*ppMem)[i].mVol= double(i + 1.0);
        (*ppMem)[i].mass = double(i + 1.0);
        (*ppMem)[i].meanD= double(i + 1.0);
        (*ppMem)[i].dve = double(i + 1.0);
        (*ppMem)[i].dla = double(i + 1.0);
        (*ppMem)[i].dsa = double(i + 1.0);
        (*ppMem)[i].dla3D = double(i + 10.0);
        (*ppMem)[i].spike = double(i + 1.0);
        (*ppMem)[i].sphere = double(i + 1.0);

        int arr_size = sizeof((*ppMem)[i].longAxis3D) / sizeof((*ppMem)[i].longAxis3D[0]);

        for (int j = 0; j < arr_size; j++)
        {
            float val = arr_size * i + j;
            (*ppMem)[i].lais[j] = val;
            (*ppMem)[i].sais[j] = val;
            (*ppMem)[i].la3D[j] = val;
        }


        (*ppMem)[i].meshWasCreatedByDll = int(i+1);
        (*ppMem)[i].segMesh.nbrVertices = int(rand() % 10 + 1);
        (*ppMem)[i].segMesh.nbrTriangles = int(rand() % 10 + 1);

        (*ppMem)[i].segMesh.vertices = (float *)malloc(sizeof(float) * (*ppMem)[i].segMesh.nbrVertices);
        (*ppMem)[i].segMesh.triangles = (int *)malloc(sizeof(int) * (*ppMem)[i].segMesh.nbrTriangles);

        for (int j = 0; j < (*ppMem)[i].segMesh.nbrVertices; j++)
        {
            (*ppMem)[i].segMesh.vertices[j] = float(j + 1);
        }

        for (int j = 0; j < (*ppMem)[i].segMesh.nbrTriangles; j++)
        {
            (*ppMem)[i].segMesh.triangles[j] = int(j + 1);
        }

    }
}

}

Python代码:

from ctypes import *
import numpy as np
from numpy.ctypeslib import ndpointer

ndDLL = cdll.LoadLibrary("./MyDll.dll")

# Triangle Mesh Structure
class TriangleMesh(Structure):
    _fields_ = [
        ("nbrVertices", c_int),
        ("nbrTriangles", c_int),
        ("vertices", POINTER(c_float)),
        ("triangles",POINTER(c_int))]

# LCSNoduleEx Structure
class ROI(Structure):
    _fields_ = [
        ("xmm", c_double),
        ("ymm", c_double),
        ("zmm", c_double),
        ("diameter", c_double),
        ("level", c_double),
        ("volume", c_double),
        ("threshold", c_double),
        ("isMesh", c_int),
        ("mVol", c_double),
        ("mass", c_double),
        ("meanD", c_double),
        ("dve", c_double),
        ("dla", c_double),
        ("dsa", c_double),
        ("dla3D", c_double),
        ("spike", c_double), 
        ("sphere", c_float),

        ("lais", c_float*6),   
        ("said", c_float*6), 
        ("la3D", c_float*6),       

        ("mesh", TriangleMesh)
     ]


def PrintROI(pMem, NumROI):

    for i in range():):
        print("ROI %d"%(i), flush=True)
        print("\tlevel %f"%(pMem[i].level), flush=True)
        print("\tdiameter %f"%(pMem[i].diameter), flush=True)
        print("\tdla3D %f"%(pMem[i].dla3D), flush=True)
        print("\tddla %f"%(pMem[i].dla), flush=True)
        print("\tmeanD %f"%(pMem[i].meanD), flush=True)
        print("\tdai %f"%(pMem[i].dai), flush=True)
        print("\tdve %f"%(pMem[i].dve), flush=True)

        print("\tmass %f"%(pMem[i].mass), flush=True)

        print("\tthreshold %f"%(pMem[i].threshold), flush=True)

        print("\tspiculation %f"%(pMem[i].spike), flush=True)
        print("\tspiculation2 %f"%(pMem[i].sphere), flush=True)

        print("\tvolume %f"%(pMem[i].volume), flush=True)
        print("\tmVol %f"%(pMem[i].mVol), flush=True)
        print("\txmm %f"%(pMem[i].xmm), flush=True)
        print("\tymm %f"%(pMem[i].ymm), flush=True)
        print("\tzmm %f"%(pMem[i].zmm), flush=True)

        print("\tla3D %f, %f, %f, %f, %f, %f"%(pMem[i].la3D[0],
                                                    pMem[i].la3D[1],
                                                    pMem[i].la3D[2],
                                                    pMem[i].la3D[3],
                                                    pMem[i].la3D[4],
                                                    pMem[i].la3D[5]), flush=True)

        print("\tsais %f, %f, %f, %f, %f, %f"%(pMem[i].sais[0],
                                                           pMem[i].sais[1],
                                                           pMem[i].sais[2],
                                                           pMem[i].sais[3],
                                                           pMem[i].sais[4],
                                                           pMem[i].sais[5]), flush=True)

        print("\tLAIS %f, %f, %f, %f, %f, %f"%(pMem[i].lais[0],
                                                            pMem[i].lais[1],
                                                            pMem[i].lais[2],
                                                            pMem[i].lais[3],
                                                            pMem[i].lais[4],
                                                            pMem[i].lais[5]), flush=True)

        print("\tisMesh %d"%(pMem[i].isMesh), flush=True)


        print("\tnbrVertices %d"%(pMem[i].mesh.nbrVertices))
        print("\tnbrTriangles %d"%(pMem[i].mesh.nbrTriangles))


        for j in range(pMem[i].mesh.nbrVertices):
            print("\t\tVertices %d) %f"%(j,pMem[i].mesh.vertices[j]), flush=True)

        for j in range(pMem[i].mesh.nbrTriangles):
            print("\t\tTriangle %d) %d"%(j,pMem[i].mesh.triangles[j]), flush=True)

ndDLL.CreateROI.argtypes  = [POINTER(POINTER(ROI)), POINTER(c_int)]

pMem= POINTER(LCSNoduleEx)()
NumROI = c_int(1)

ndDLL.CreateROI(byref(pMem),byref(NumROI) )


PrintROI(pMem, NumROI.value)

1 个答案:

答案 0 :(得分:0)

您的两个结构至少有两个不匹配。

我不知道哪一个是相关的,因为您的垃圾似乎在名为spic的字段中,而该字段既没有出现在C ++结构中,也没有出现在ctypes中……


首先,您会得到两个乱序的字段:

double threshold;
int isMesh;
double mVol;

vs。

("threshold", c_double),
("mVol", c_double),
("isMesh", c_int),

这似乎没什么大不了,但是一旦将对齐驱动的填充放入混合中,可以想象这可能导致所有后续字段减少4个字节。


然后,对所有这些数组使用错误的类型:

float lais[6];  
float sais[6];  
float la3D[6];      

vs。

("lais", c_double*6),   
("said", c_double*6), 
("la3D", c_double*6),       

float是32位; c_double是64位。 lais[0]是由lais[0]lais[1]打包成一个双精度值的垃圾双精度型,lais[4]sais[2]sais[3]打包在一起的{{ 1}}是超出结构末尾的64个随机位。如果幸运的话,尝试阅读时会遇到段错误,但如果不行,则会遇到难以调试的垃圾。


对于像这样的非常复杂的结构,我通常不相信自己手动编写ctypes版本,而不会犯至少三倍于您的错误。相反,我编写了一个快速脚本来解析标头(有时是文档),并生成Python代码。对于诸如CoreFoundation和Win32标头之类的东西,它们具有成千上万的极其庞大的结构都以非常严格和可靠的样式编写,我有一遍又一遍地使用脚本。