11import ctypes
22from contextlib import suppress
3- from typing import Any , Optional , Type , TypeVar , Union , get_type_hints
3+ from typing import Any , List , Optional , Type , TypeVar , Union , get_type_hints
44
5- from ._utils import attempt_decode , get_mapped
6- from .c_pointer import BaseCPointer
5+ from _pointers import add_ref
6+
7+ from ._utils import attempt_decode , get_mapped , get_py
8+ from .base_pointers import BaseCPointer
9+ from .c_pointer import TypedCPointer , VoidPointer
710from .constants import RawType
811from .object_pointer import Pointer
912
1518)
1619
1720
18- def _get_type (
19- class_ : Type ["Struct" ],
20- ct : Type ["ctypes._CData" ],
21- name : str ,
22- ) -> Type ["ctypes._CData" ]:
23- attr = getattr (class_ , name , None )
21+ class Struct :
22+ """Abstract class representing a struct."""
2423
25- if isinstance (attr , RawType ):
26- return attr .tp
24+ def _convert_tc_ptr (self , typ : Any , name : str ):
25+ if typ is TypedCPointer :
26+ raise TypeError (
27+ "cannot instantiate: TypedCPointer has no type argument" ,
28+ )
2729
28- return ct
30+ if getattr (typ , "__origin__" , None ) is TypedCPointer :
31+ setattr (
32+ type (self ),
33+ name ,
34+ RawType (
35+ ctypes .POINTER (get_mapped (typ .__args__ [0 ])),
36+ ),
37+ )
2938
39+ return typ
3040
31- class Struct :
32- """Abstract class representing a struct."""
41+ def _get_type (
42+ self ,
43+ ct : Type ["ctypes._CData" ],
44+ name : str ,
45+ ) -> Type ["ctypes._CData" ]:
46+ attr = getattr (type (self ), name , None )
47+
48+ if isinstance (attr , RawType ):
49+ return attr .tp
50+
51+ if ct is ctypes .c_void_p :
52+ self ._void_p .append (name )
53+
54+ return ct
3355
3456 def __init__ (self , * args : Any , do_sync : bool = True ):
3557 class_typ : Type [Struct ] = type (self )
@@ -40,15 +62,16 @@ def __init__(self, *args: Any, do_sync: bool = True):
4062 )
4163
4264 hints = get_type_hints (class_typ )
43- self ._hints = hints
65+ self ._void_p : List [str ] = []
66+ self ._hints = {k : self ._convert_tc_ptr (v , k ) for k , v in hints .items ()}
4467
4568 class _InternalStruct (ctypes .Structure ):
4669 _fields_ = [
4770 (
4871 name ,
49- _get_type (class_typ , get_mapped (typ ), name ),
72+ self . _get_type (get_mapped (typ ), name ),
5073 )
51- for name , typ in hints .items () # fmt: off
74+ for name , typ in self . _hints .items () # fmt: off
5275 ]
5376
5477 self ._struct = _InternalStruct (
@@ -91,6 +114,29 @@ def __getattribute__(self, name: str):
91114 if (name in hints ) and (type (attr )) is bytes :
92115 attr = attempt_decode (attr )
93116
117+ if isinstance (attr , ctypes ._Pointer ): # type: ignore
118+ value = attr .contents
119+ ct = type (value )
120+
121+ if ct is ctypes .c_void_p :
122+ add_ref (ct )
123+ return VoidPointer (
124+ ctypes .addressof (value ),
125+ ctypes .sizeof (value ),
126+ )
127+
128+ py_type = get_py (ct )
129+ return TypedCPointer (
130+ ctypes .addressof (value ),
131+ py_type ,
132+ ctypes .sizeof (value ),
133+ False ,
134+ )
135+
136+ if name in super ().__getattribute__ ("_void_p" ):
137+ ct = ctypes .c_void_p (attr ) # type: ignore
138+ return VoidPointer (attr , ctypes .sizeof (ct )) # type: ignore
139+
94140 return attr
95141
96142 def __setattr__ (self , name : str , value : Any ):
0 commit comments