__init__ with ctypes Structure - python

I don't know much about what is actually going on with _fields_ and __init__, so I'm not sure if it's possible or not, but I'm trying to do the something like the following:
class Matrix(Structure):
def __init__(self, dtype, nrow, ncol, data = []):
self._fields_ = [("nrow", c_int), ("ncol", c_int)]
self.dtype = dtype
self.nrow = nrow
self.ncol = ncol
if dtype == float:
self._fields_.append(("data", POINTER(c_double)))
arr_type = c_double * (nrow * ncol)
arr_inst = arr_type(*data)
self.data = arr_inst
The goal is to call functions from a shared library I wrote in C that has the structure:
typedef struct DoubleMatrix DoubleMatrix;
struct DoubleMatrix {
int nrow;
int ncol;
double *data;
};
void func1(DoubleMatrix *mat) {
// stuff
}
but it's not working. When I do:
m = DoubleMatrix(float, 2, 2, [1, 2, 3, 4])
I get
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-28-4e74b99b208f> in <module>
----> 1 m = DoubleMatrix(float, 2, 2, [1, 2, 3, 4])
TypeError: __init__() takes from 3 to 4 positional arguments but 5 were given
The reason I want to do this is because I will have matrices whose entries are of type other than double
Edit
I want to share what has worked but does not have my desired functionality:
from ctypes import *
lib = CDLL("/usr/local/lib/ccalc/libccalc.so")
class DoubleMatrix(Structure):
_fields_ = [("nrow", c_int),
("ncol", c_int),
("data", POINTER(c_double))]
def __init__(self, nrow, ncol, data=[]):
self.nrow = nrow
self.ncol = ncol
arr_type = c_double * (nrow * ncol)
arr_inst = arr_type(*data)
self.data = arr_inst
return
Second Edit
I am sorry everyone, I think I didn't restart kernel of my jupyter notebook properly when probing this. Now the case is that python crashes with the following code:
class DoubleMatrix(Structure):
_fields_ = [("nrow", c_int), ("ncol", c_int)]
def __init__(self, dtype, nrow, ncol, data=[]):
self.dtype = dtype
self.nrow = nrow
self.ncol = ncol
if dtype == float:
self._fields_.append(("data", POINTER(c_double)))
arr_type = c_double * (nrow * ncol)
arr_inst = arr_type(*data)
self.data = arr_inst
return
Maybe This question should be closed/deleted so I can ask it properly? The heart of my question is still the same, how do I take my working code in the first edit, and add the functionality I mentioned. Again I am very sorry.

Related

glCreateShader stoped workig after irrelevant change

When I am running a pyopengl program, I get an error.
I searhed the web but all it says is that it is a pyopengl version problem, but I am using the latest update.
Traceback (most recent call last):
File "C:/Users/TheUser/Desktop/MyPytonDen/ThinMatrixOpenGl/engineTester/MainGameLoop.py", line 10, in
from ThinMatrixOpenGl.renderEngine.MasterRenderer import MasterRendererClass
File "C:\Users\TheUser\Desktop\MyPytonDen\ThinMatrixOpenGl\renderEngine\MasterRenderer.py", line 10, in
class MasterRendererClass:
File "C:\Users\TheUser\Desktop\MyPytonDen\ThinMatrixOpenGl\renderEngine\MasterRenderer.py", line 11, in MasterRendererClass
shader = StaticShaderClass()
File "C:\Users\TheUser\Desktop\MyPytonDen\ThinMatrixOpenGl\shaders\staticShader.py", line 22, in init
super().init(self.VERTEX_FILE, self.FRAGMENT_FILE)
File "C:\Users\TheUser\Desktop\MyPytonDen\ThinMatrixOpenGl\shaders\shaderProgram.py", line 13, in init
self.Vertex_Shader_Id = Load_Shader(vertex_file, GL_VERTEX_SHADER)
File "C:\Users\TheUser\Desktop\MyPytonDen\ThinMatrixOpenGl\shaders\shaderProgram.py", line 84, in Load_Shader
Shader_Id = glCreateShader(type_of_shader)
File "C:\Users\TheUser\AppData\Local\Programs\Python\Python38-32\lib\site-packages\OpenGL\platform\baseplatform.py", line 423, in call
raise error.NullFunctionError(
OpenGL.error.NullFunctionError: Attempt to call an undefined function glCreateShader, check for bool(glCreateShader) before calling
Process finished with exit code 1
I checked the OpenGL source code. Not that I meddle with it in the first place but its fine.
For some reason, StaticShader refuses to initialize now.
In my program, before doing some change, it was working just fine and it is still working in some other project.
Despite I didn't even get close to shader codes it gave me this.
What exactly is this and how can I handle it.
Btw while this poped up I was trying to update the render algorithm although it did not change much.
class StaticShaderClass(ShaderProgramClass):
VERTEX_FILE = "../shaders/vertexShader.txt"
FRAGMENT_FILE = "../shaders/fragmentShader.txt"
location_transformation_matrix: int
location_projection_matrix: int
location_view_matrix: int
location_light_position: int
location_light_color: int
location_shine_damper: int
location_reflectivity: int
def __init__(self):
super().__init__(self.VERTEX_FILE, self.FRAGMENT_FILE)
def Bind_Attributes(self):
super().Bind_Attribute(0, "position")
super().Bind_Attribute(1, "texture_coord")
super().Bind_Attribute(2, "normal")
def GetAllUniformLocation(self):
self.location_transformation_matrix = super().GetUniformLocation("transformation_matrix")
self.location_projection_matrix = super().GetUniformLocation("projection_matrix")
self.location_view_matrix = super().GetUniformLocation("view_matrix")
self.location_light_position = super().GetUniformLocation("light_position")
self.location_light_color = super().GetUniformLocation("light_color")
self.location_shine_damper = super().GetUniformLocation("shine_damper")
self.location_reflectivity = super().GetUniformLocation("reflectivity")
def Load_Shine_Variables(self, damper, reflectivity):
Load_Float(self.location_shine_damper, damper)
Load_Float(self.location_reflectivity, reflectivity)
def Load_Transformation_Matrix(self, matrix: Matrix44):
super().Load_Matrix(self.location_transformation_matrix, matrix)
def Load_Projection_Matrix(self, projection: Matrix44):
super().Load_Matrix(self.location_projection_matrix, projection)
def Load_view_Matrix(self, camera: CameraClass):
view_matrix = Maths.Create_view_Matrix(camera)
super().Load_Matrix(self.location_view_matrix, view_matrix)
def Load_Light(self, light: Light):
Load_Vector(self.location_light_position, light.position)
Load_Vector(self.location_light_color, light.color)
class ShaderProgramClass(ABC):
Program_Id: int
Vertex_Shader_Id: int
Fragment_Shader_Id: int
def __init__(self, vertex_file: str, fragment_file: str):
self.Vertex_Shader_Id = Load_Shader(vertex_file, GL_VERTEX_SHADER)
self.Fragment_Shader_Id = Load_Shader(fragment_file, GL_FRAGMENT_SHADER)
self.Program_Id = glCreateProgram()
glAttachShader(self.Program_Id, self.Vertex_Shader_Id)
glAttachShader(self.Program_Id, self.Fragment_Shader_Id)
self.Bind_Attributes()
glLinkProgram(self.Program_Id)
# glGetProgramInfoLog(self.Program_Id)
glValidateProgram(self.Program_Id)
self.GetAllUniformLocation()
def Start(self):
glUseProgram(self.Program_Id)
def Clean_up(self):
self.Stop()
glDetachShader(self.Program_Id, self.Vertex_Shader_Id)
glDetachShader(self.Program_Id, self.Fragment_Shader_Id)
glDeleteShader(self.Vertex_Shader_Id)
glDeleteShader(self.Fragment_Shader_Id)
glDeleteProgram(self.Program_Id)
#abstractmethod
def Bind_Attributes(self):
pass
def Bind_Attribute(self, attribute: int, variable_name: str):
glBindAttribLocation(self.Program_Id, attribute, variable_name)
#staticmethod
def Stop():
glUseProgram(0)
#abstractmethod
def GetAllUniformLocation(self):
pass
def GetUniformLocation(self, uniform_name: str):
return glGetUniformLocation(self.Program_Id, uniform_name)
#staticmethod
def Load_Matrix(location, matrix):
matrix = np.array(matrix, dtype=np.float32)
# it may require matrix s data type to change float later
glUniformMatrix4fv(location, 1, False, matrix)
def Load_Float(location: int, value: float):
glUniform1f(location, value)
def Load_Vector(location: int, vector: Vector3):
glUniform3f(location, vector.x, vector.y, vector.z)
def Load_Boolean(location: int, value: bool):
to_load = 0
if value:
to_load = 1
glUniform1f(location, to_load)
def Load_Shader(file: str, type_of_shader: int):
try:
src = ""
with open(file, "r") as f:
text = f.readlines()
for i in text:
src += str(i)
except ():
raise Exception(FileNotFoundError, "file is not exist or could not be readied for some reason")
Shader_Id = glCreateShader(type_of_shader)
print(Shader_Id)
glShaderSource(Shader_Id, src)
glCompileShader(Shader_Id)
if glGetShaderiv(Shader_Id, GL_COMPILE_STATUS) == GL_FALSE:
print(glGetShaderInfoLog(Shader_Id))
print("could not compile shader!")
return Shader_Id
#version 400 core
in vec3 position;
in vec2 texture_coord;
in vec3 normal;
out vec2 pass_texture_coord;
out vec3 surface_normal;
out vec3 to_light_vector;
out vec3 to_camera_vector;
uniform mat4 transformation_matrix;
uniform mat4 projection_matrix;
uniform mat4 view_matrix;
uniform vec3 light_position;
void main(){
vec4 world_position = transformation_matrix * vec4(position, 1.0f);
gl_Position = projection_matrix * view_matrix * world_position;
pass_texture_coord = texture_coord;
surface_normal = (transformation_matrix * vec4(normal,0.0)).xyz;
to_light_vector = light_position - world_position.xyz;
to_camera_vector = (inverse(view_matrix) * vec4(0.0,0.0,0.0,1.0)).xyz - world_position.xyz;
}
Let me quote Python class attributes are evaluated on declaration:
In Python, class attributes are evaluated and put into memory when the class is defined (or imported).
A valid and current OpenGL context is required for each OpenGL instruction, such as for creating the shader program. Therefore, if the shader program is stored in a class attribute and the class is defined or imported before the OpenGL window and context are created, the shader program cannot be generated.

ctypes: c_ulong type magically changed to long

I am writing the deepcopy function using python2.7 for my class. I encountered a weird problem
My code is the following
import copy
from ctypes import *
class Graph (Structure):
_fields_ = [("numVertices", c_ulong),
("numEdges", c_ulong)]
def __init__(self):
self.numVertices = c_ulong(0)
self.numEdges = c_ulong(0)
def __deepcopy__(self,memo={}):
newInstance = Graph()
newInstance.numVertices = c_ulong(self.numVertices.value)
newInstance.numEdges = c_ulong(self.numEdges.value)
return newInstance
graph = Graph()
anotherGraph = copy.deepcopy(graph)
I get the following error:
<ipython-input-46-a0cdaa4ef3f7> in __deepcopy__(self, memo)
9 def __deepcopy__(self,memo={}):
10 newInstance = Graph()
---> 11 newInstance.numVertices = c_ulong(self.numVertices.value)
12 newInstance.numEdges = c_ulong(self.numEdges.value)
13 return newInstance
AttributeError: 'long' object has no attribute 'value'
If you try:
type(graph.numVertices)
The result is long
I declared the numVertices as c_ulong(). Why does it become long?
The type of the fields in the structure is still maintained, but ctypes has some "helpful" conversions when reading the values:
from ctypes import *
class Test(Structure):
_fields_ = [('a',c_ulong),
('b',c_char_p)]
t = Test(1,b'hello')
print(type(t.a),type(t.b))
print(t._fields_)
Output:
<class 'int'> <class 'bytes'>
[('a', <class 'ctypes.c_ulong'>), ('b', <class 'ctypes.c_char_p'>)]
So you can write your code as the following and it will work correctly:
import copy
from ctypes import *
class Graph (Structure):
_fields_ = [("numVertices", c_ulong),
("numEdges", c_ulong)]
def __init__(self):
self.numVertices = 0
self.numEdges = 0
def __deepcopy__(self,memo={}):
newInstance = Graph()
newInstance.numVertices = self.numVertices
newInstance.numEdges = self.numEdges
return newInstance
graph = Graph()
anotherGraph = copy.deepcopy(graph)
You can suppress the conversions by deriving from the classes, but it is usually unnecessary. One use case is when using ctypes to call a function that returns an allocated string. You need to suppress the c_char_p to Python byte string conversion so you can later free the c_char_p.
from ctypes import *
class ulong(c_ulong): pass
class char_p(c_char_p): pass
class Test(Structure):
_fields_ = [('a',ulong),
('b',char_p)]
t = Test(1,b'hello')
print(type(t.a),type(t.b))
print(t.a,t.b)
Output:
<class '__main__.ulong'> <class '__main__.char_p'>
<ulong object at 0x0000000006263748> char_p(b'hello')

Trying to pass a structure by pointer into C library from Python

Can't figure out how to pass a structure by pointer to a C function from Python.
Here is what I have (it's a part of bigger effort of implementing nn_recvmsg for nanomsg-python project):
...
msgHdr = NN_MSGHDR(iovecList)
pointer_type = ctypes.POINTER(NN_MSGHDR)
pp = pointer_type.from_address(ctypes.addressof(msgHdr))
print("argument type: "+str(pp))
print("function arguments: "+str(_nn_recvmsg.argtypes))
rtn = _nn_recvmsg(socket, pp, 0)
...
Which gives me:
argument type: <_nanomsg_ctypes.LP_NN_MSGHDR object at 0x10b6d8d40>
function arguments: (<class 'ctypes.c_int'>, <class '_nanomsg_ctypes.LP_NN_MSGHDR'>, <class 'ctypes.c_int'>)
Traceback (most recent call last):
File "./test.py", line 11, in <module>
nnc.nn_recvmsg(s, [4, frameSize])
File "/Users/peetonn/Documents/Work/ptn-nanomsg-python/_nanomsg_ctypes/__init__.py", line 311, in nn_recvmsg
rtn = _nn_recvmsg(socket, pp, 0)
ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: wrong type
from the output, I see that argument type is the same as the function would expect it to be. However, it still fails.
Below are structure definitions and full code for the function I'm implementing:
class NN_IOVEC(ctypes.Structure):
_fields_ = [("iov_base", ctypes.c_void_p),
("iov_len", ctypes.c_size_t)]
class NN_MSGHDR(ctypes.Structure):
_fields_ = [("msg_iov", ctypes.POINTER(NN_IOVEC)), # ctypes.c_void_p),
("msg_iovlen", ctypes.c_int),
("msg_control", ctypes.c_void_p),
("msg_controllen", ctypes.c_size_t)]
def __init__(self, iovecList):
elems = (NN_IOVEC * len(iovecList))()
self.msg_iovlen = len(iovecList)
self.msg_iov = ctypes.cast(elems, ctypes.POINTER(NN_IOVEC))
for i in range(0, len(iovecList)):
self.msg_iov[i].iov_base = iovecList[i].iov_base
self.msg_iov[i].iov_len = iovecList[i].iov_len
self.msg_controllen = 0
self.msg_control = 0
def nn_recvmsg(socket, sizes = None):
"receive message/messages"
if sizes:
iovecList = []
for sz in sizes:
iovec = NN_IOVEC()
iovec.iov_len = sz
buf = (ctypes.c_char * sz)()
iovec.iov_base = ctypes.cast(buf, ctypes.c_void_p)
iovecList.append(iovec)
msgHdr = NN_MSGHDR(iovecList)
pointer_type = ctypes.POINTER(NN_MSGHDR)
pp = pointer_type.from_address(ctypes.addressof(msgHdr))
print("argument type: "+str(pp))
print("function arguments: "+str(_nn_recvmsg.argtypes))
rtn = _nn_recvmsg(socket, ctypes.byref(pp), 0)
print("here's the result: "+str(rtn))
if rtn < 0 :
print(nn_strerror(nn_errno()))
else:
pass # tbd

Dereference void pointer to numpy array or list

I've received a void pointer from a foreign function via ctypes, containing an array of c_double arrays:
[[12.0, 13.0], [14.0, 15.0], …]
I'm accessing it via the restype parameter:
from ctypes import Structure, POINTER, c_void_p, c_size_t, c_double, c_uint32, c_char_p, cast, cdll
class _CoordResult(Structure):
""" Container for returned FFI coordinate data """
_fields_ = [("coords", _FFIArray)]
class _FFIArray(Structure):
"""
Convert sequence of float lists to a C-compatible void array
example: [[1.0, 2.0], [3.0, 4.0]]
"""
_fields_ = [("data", c_void_p),
("len", c_size_t)]
def _void_array_to_nested_list(res, _func, _args):
""" Dereference the FFI result to a list of coordinates """
shape = (res.coords.len, 2)
array_size = np.prod(shape)
mem_size = 8 * array_size
array_str = string_at(res.coords.data, mem_size)
array = [list(pair) for pair in ((POINTER(c_double * 2).from_buffer_copy(res.coords)[:res.coords.len]))]
drop_array(res.coords)
return array
decode_polyline = lib.decode_polyline_ffi
decode_polyline.argtypes = (c_char_p, c_uint32)
decode_polyline.restype = _CoordResult
decode_polyline.errcheck = _void_array_to_nested_list
However, this gives me back nonsense values, because the pointer dereference in _void_array_to_nested_list is wrong.
The solution doesn't have to use NumPy, but that seems like the best approach.
I can't test this right now, but this is what I would try:
import numpy as np
result = ...
shape = (10, 2)
array_size = np.prod(shape)
mem_size = 8 * array_size
array_str = ctypes.string_at(result, mem_size)
array = np.frombuffer(array_str, float, array_size).reshape(shape)
array will be read only, copy it if you need a writable array.
Here is a solution that uses ctypes.cast or numpy.ctypeslib.as_array, and no ctypes.string_at just in case if it makes an extra copy of memory region.
class _FFIArray(Structure):
_fields_ = [("data", c_void_p), ("len", c_size_t)]
class Coordinate(Structure):
_fields_ = [("latitude", c_double), ("longitude", c_double)]
class Coordinates(Structure):
_fields_ = [("data", POINTER(Coordinate)), ("len", c_size_t)]
decode_polyline = lib.decode_polyline_ffi
decode_polyline.argtypes = (c_char_p, c_uint32)
decode_polyline.restype = _FFIArray
# assuming that the second argument is the length of polyline,
# although it should not be needed for `\0` terminated string
res = decode_polyline(polyline, len(polyline))
nres = Coordinates(cast(res.data, POINTER(Coordinate)), res.len)
for i in range(nres.len):
print(nres.data[i].latitude, nres.data[i].longitude)
# if just a numpy (np) array is needed
xs = np.ctypeslib.as_array((c_double * res.len * 2).from_address(res.data))
# "New view of array with the same data."
xs = xs.view(dtype=[('a', np.float64), ('b', np.float64)], type=np.ndarray)
xs.shape = res.len

which argtypes for NetShareAdd

The win32 function NetShareDel takes three arguments, LPCWSTR LPCWSTR and DWORD.
So I use the following list for argtypes:
import ctypes as C
C.windll.Netapi32.NetShareDel.argtypes = [LPCWSTR, LPCWSTR, c_int]
C.windll.Netapi32.NetShareDel.restype = c_int
C.windll.Netapi32.NetShareDel(server, shareName, 0)
That works fine, but I can't figure out what to use for NetShareAdd, especialle the byte array for NET_SHARE_INFO struct and the last byref(c_int) argument.
Here's the code:
def Share(server, shareName, dir):
info = SHARE_INFO_2()
STYPE_DISKTREE = 0
info.shi2_netname = shareName
info.shi2_path = dir
info.shi2_type = STYPE_DISKTREE
info.shi2_remark = "Shared: " + time.strftime("%Y%m%d-%H:%M")
info.shi2_max_uses = -1
info.shi2_passwd = ""
info.shi2_current_uses = 0
info.shi2_permissions = 0xFFFFFFFF
i = c_int()
bytearray = buffer(info)[:]
windll.Netapi32.NetShareAdd.argtypes = [LPCWSTR, c_int, ????, ????]
windll.Netapi32.NetShareAdd(server, 2, bytearray, C.byref(i))
What would be the correct argtypes list for NetShareAdd?
Got it working finally
First the line
bytearray = buffer(info)[:]
was changed into byte pointer type
byteptr = C.POINTER(C.wintypes.BYTE)(info)
and then the argtypes and call will become POINTER(BYTE) too of course:
C.windll.Netapi32.NetShareAdd.argtypes = [LPCWSTR, c_int, C.POINTER(C.wintypes.BYTE), C.POINTER(c_int)]
C.windll.Netapi32.NetShareAdd.restype = c_int
windll.Netapi32.NetShareAdd(server, 2, byteptr, C.byref(i))

Categories

Resources