1

I've been fooling with ctypes and have come across two problems:

Problem 1. I would like to build a cellComplex using double* arrays, but I want new_cellComplex to accept an array of double*'s (along with a size_t argument) rather than a fixed number of double*'s. With a fixed number the code looks like this (and it runs fine):

extern "C" {
  void * new_cellComplex(double* p_x, double* p_y, double* p_z) {

    std::vector< std::pair<double,double> > point;
    point.push_back( std::make_pair(p_x[0],p_x[1])); 
    point.push_back( std::make_pair(p_x[0],p_x[1])); 
    point.push_back( std::make_pair(p_x[0],p_x[1])); 

    cellComplex<double>* cmplx = new cellComplex<double>(point);
    return cmplx;
  }

with Python code:

import ctypes

cellComplex_lib = ctypes.cdll.LoadLibrary('./cellComplex_lib.so')
cellComplex_lib.new_cellComplex.restype  = ctypes.c_void_p
cellComplex_lib.new_cellComplex.argtypes = [ctypes.c_double*2,
                                            ctypes.c_double*2,
                                            ctypes.c_double*2]

p_x = (ctypes.c_double*2)(0.0,1.0)
p_y = (ctypes.c_double*2)(0.0,1.0)
p_z = (ctypes.c_double*2)(0.0,1.0)

cmplx = cellComplex_lib.new_cellComplex(p_x,p_y,p_z)

I would rather have the following (which segfaults):

extern "C" {
  void * new_cellComplex(double** p, size_t dim) {

    std::vector< std::pair<double,double> > point;
    for (size_t i=0; i<dim; ++i) {
      point.push_back( std::make_pair(p[i][0],p[i][1])); 
    } 
    cellComplex<double>* cmplx = new cellComplex<double>(point);
    return cmplx;
  }
}

With Python code:

import ctypes

dim = 3
cellComplex_lib = ctypes.cdll.LoadLibrary('./cellComplex_lib.so')
cellComplex_lib.new_cellComplex.restype  = ctypes.c_void_p
cellComplex_lib.new_cellComplex.argtypes = [(ctypes.c_double*2)*dim,
                                            ctypes.c_size_t]

p_x = (ctypes.c_double*2)(0.0,1.0)
p_y = (ctypes.c_double*2)(0.0,1.0)
p_z = (ctypes.c_double*2)(0.0,1.0)

p = ((ctypes.c_double*2)*dim)(p_x,p_y,p_z)

cmplx = cellComplex_lib.new_cellComplex(p,dim)

^This doesn't work and I don't know why.

Problem 2. (Included here because it's glaring in Problem 1) I am returning an essentially anonymous pointer from my C code! This just feels, well, dirty, and there must be a better way to return a custom data type and deal with it back in Python. For the record, I am extremely grateful for this stackoverflow answer where I learned such sorcery - but I'm not going to be able to sleep at night as long as it's in my code...

Community
  • 1
  • 1
Tom Stephens
  • 475
  • 4
  • 15

1 Answers1

2

Instead of double **, use double [][2]. You're passing a contiguous C array that you want to access as a pointer to a row of 2 items. The first index is the row index.

Declaring the array as a double ** is a pointer to a double pointer, so p[i] is a pointer, and p[i][0] dereferences it again. But p[i] is a NULL pointer by chance according to your data.

Refer to the comp.lang.c FAQ, question 6.18: My compiler complained when I passed a two-dimensional array to a function expecting a pointer to a pointer.

For the return type, you can subclass c_void_p, or use the hooks from_param and _as_parameter_ per the last paragraph of section 15.17.1.7 in the ctypes docs.

Eryk Sun
  • 33,190
  • 5
  • 92
  • 111