3

C code: uses fractal_create() to assign a 2D array to 'values' array in the struct fractal_t

//2D.c

#include <stdlib.h>
#include <stdio.h>

typedef struct 
{
    size_t height;
    size_t width;
    double values[];
} fractal_t;

void fractal_destroy (fractal_t* f)
{
    free(f);
}

void fractal_fill (fractal_t* f)
{
    double (*array_2D)[f->width] = (double(*)[f->width]) f->values;

    for (size_t height=0; height < f->height; height++)
    {
        for (size_t width=0; width < f->width; width++)
        {
            array_2D[height][width] = width; // whatever value that makes sense
        }
    }
}

void fractal_print (const fractal_t* f)
{
    double (*array_2D)[f->width] = (double(*)[f->width]) f->values;

    for(size_t height=0; height < f->height; height++)
    {
        for(size_t width=0; width < f->width; width++)
        {
            printf("%.5f ", array_2D[height][width]); 
        }
        printf("\n");
    }
}

fractal_t* fractal_create (size_t height, size_t width)
{
    // using calloc since it conveniently fills everything with zeroes
    fractal_t* f = calloc(1, sizeof *f + sizeof(double[height][width]) );
    f->height = height;
    f->width = width;
    // ...
    fractal_fill(f); // fill with some garbage value
    fractal_print(f);
    return f;
}

int main (void)
{
    int h = 3;
    int w = 4;

    fractal_t* fractal = fractal_create(h, w);
    fractal_destroy(fractal);
}

I'm trying to access this 'values' 2D array from python using ctypes

python code:

from ctypes import *

h = 3
w = 4

class fractal_t(Structure):
    _fields_ = [("a", c_int),
                ("values", (c_double * h) * w)]

slib = cdll.LoadLibrary('/2D.so')
t = fractal_t
fun = slib.fractal_create
t  = fun(c_int(h), 
    c_int(w))

p1 = fractal_t.from_address(t)

print(p1.values[0][0])
print(p1.values[0][1])
print(p1.values[0][2])

for i in p1.values: 
    print(i, end=" \n")
    for j in i:
        print(j, end=" \n")

output:

0.00000 1.00000 2.00000 3.00000 
0.00000 1.00000 2.00000 3.00000 
0.00000 1.00000 2.00000 3.00000 
2e-323
0.0
1.0
<__main__.c_double_Array_3 object at 0x7f5c7836f8c8> 
2e-323 
0.0 
1.0 
<__main__.c_double_Array_3 object at 0x7f5c7836fb70> 
2.0 
3.0 
0.0 
<__main__.c_double_Array_3 object at 0x7f5c7836f8c8> 
1.0 
2.0 
3.0 
<__main__.c_double_Array_3 object at 0x7f5c7836fb70> 
0.0 
1.0 
2.0 

The first 3 lines from the output is printed from fractal_print()in C. When i tried to access the 2D array using p1.values[0][0], I'm not getting the correct value. Not sure what is wrong here.

y_i
  • 75
  • 6
  • What happens if you replace `(c_double * h) * w` with `(c_double * w) * h`? – Joseph Sible-Reinstate Monica May 26 '20 at 21:25
  • It changes rows to columns <__main__.c_double_Array_4 object at 0x7fba3d4d68c8> 2e-323 0.0 1.0 2.0 <__main__.c_double_Array_4 object at 0x7fba3d4d6b70> 3.0 0.0 1.0 2.0 <__main__.c_double_Array_4 object at 0x7fba3d4d68c8> 3.0 0.0 1.0 2.0 – y_i May 26 '20 at 21:31

1 Answers1

1

I resolved this

Python code:

import numpy
from ctypes import *

col = 2
row = 3

lib = cdll.LoadLibrary('./2d_array.so')

class Array_2d(Structure):
    _fields_ = [('col', c_int), 
                ('row', c_int),
                ("data", (c_double * row) * col)]


fun = lib.initialize_Array_2d
point_ptr = fun(c_int(col), c_int(row))

fun = lib.cfun
fun(c_void_p(point_ptr))

p1 = Array_2d.from_address(point_ptr)

print("from python")

print(p1.data[0][0], p1.data[0][1],p1.data[0][2])
print(p1.data[1][0], p1.data[1][1],p1.data[1][2])

fun = lib.free_Array_2d
point_ptr = fun()

C code:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int col;
    int row;
    double data[];
} Array_2d;

Array_2d* f;

Array_2d* initialize_Array_2d(int col, int row)
{
    f = calloc(1, sizeof *f + sizeof(double[col][row]));
    f->col = col;
    f->row = row;
    return f;
}

void free_Array_2d()
{
    free(f);
}

void cfun(void * stv) 
{
    Array_2d * f = (Array_2d *) stv;

    double (*array_2D)[f->row] = (double(*)[f->row]) f->data;

    for (size_t i=0; i < f->col; i++)
    {
        for (size_t j=0; j < f->row; j++)
        {
            array_2D[i][j] = rand() % 10 + 1; // some random values
        }
    }

    printf("from C : \n");
    for(size_t i=0; i < f->col; i++)
    {
        for(size_t j=0; j < f->row; j++)
        {
            printf("%.1f ", array_2D[i][j]); 
        }
        printf("\n");
    }
    printf("\n");
}

Output:

from C : 
4.0 7.0 8.0 
6.0 4.0 6.0 

from python
4.0 7.0 8.0
6.0 4.0 6.0
y_i
  • 75
  • 6
  • I think you mixed up C and Python. – user35915 May 27 '20 at 15:38
  • is there any drawback in that? – y_i May 27 '20 at 16:01
  • It's still ***U**ndefined **B**ehavior*: [\[SO\]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)](https://stackoverflow.com/questions/58610333/c-function-called-from-python-via-ctypes-returns-incorrect-value/58611011#58611011). – CristiFati May 27 '20 at 22:06
  • Thank you for the comment. Could you point out what in the code is causing Undefined Behaviour. (I believe there are no inconsistencies between arguments (and / or return) in the above code as you have explained in the link provided) – y_i May 28 '20 at 04:14
  • There are no inconsistencies as in there are no specifications whatsoever. – CristiFati May 28 '20 at 05:49