Skip to content

Commit 0d11cbb

Browse files
committed
First pass at CPP support, thanks to #99
1 parent 7c47f8f commit 0d11cbb

File tree

5 files changed

+69
-29
lines changed

5 files changed

+69
-29
lines changed

ctypeslib/codegen/cursorhandler.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ def INIT_LIST_EXPR(self, cursor):
9696
# now fixed by TranslationUnit.PARSE_SKIP_FUNCTION_BODIES
9797
COMPOUND_STMT = ClangHandler._do_nothing
9898

99+
@log_entity
100+
def NAMESPACE(self, cursor):
101+
for child in cursor.get_children():
102+
self.parse_cursor(child) # FIXME, where is the starElement
103+
99104
################################
100105
# TYPE REFERENCES handlers
101106

@@ -627,9 +632,12 @@ def _record_decl(self, cursor, _output_type, num=None):
627632
'_record_decl: %s is already registered with members',
628633
name)
629634
return self.get_registered(name)
630-
# FIXME: lets ignore bases for now.
631-
# bases = attrs.get("bases", "").split() # that for cpp ?
632-
bases = [] # FIXME: support CXX
635+
# CPP bases
636+
bases = []
637+
for c in cursor.get_children():
638+
if c.kind == CursorKind.CXX_BASE_SPECIFIER:
639+
bases.append(self.get_registered(self.get_unique_name(c)))
640+
log.debug("got base class %s", c.displayname)
633641
size = cursor.type.get_size()
634642
align = cursor.type.get_align()
635643
if size == -2: #

ctypeslib/codegen/handler.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,14 @@ def _make_unknown_name(self, cursor, field_name):
126126

127127
def get_unique_name(self, cursor, field_name=None):
128128
"""get the spelling or create a unique name for a cursor"""
129-
name = ''
130129
if cursor.kind in [CursorKind.UNEXPOSED_DECL]:
131130
return ''
132131
# covers most cases
133132
name = cursor.spelling
133+
if cursor.kind == CursorKind.CXX_BASE_SPECIFIER:
134+
name = cursor.type.spelling
134135
# if it's a record decl or field decl and its type is anonymous
135-
if cursor.spelling == '':
136+
if name == '':
136137
# if cursor.is_anonymous():
137138
# a unnamed object at the root TU
138139
if (cursor.semantic_parent
@@ -149,11 +150,13 @@ def get_unique_name(self, cursor, field_name=None):
149150
#code.interact(local=locals())
150151
return ''
151152
if cursor.kind in [CursorKind.STRUCT_DECL,CursorKind.UNION_DECL,
152-
CursorKind.CLASS_DECL]:
153+
CursorKind.CLASS_DECL, CursorKind.CXX_BASE_SPECIFIER]:
153154
names= {CursorKind.STRUCT_DECL: 'struct',
154155
CursorKind.UNION_DECL: 'union',
155156
CursorKind.CLASS_DECL: 'class',
156-
CursorKind.TYPE_REF: ''}
157+
CursorKind.TYPE_REF: '',
158+
CursorKind.CXX_BASE_SPECIFIER: 'class'
159+
}
157160
name = '%s_%s'%(names[cursor.kind],name)
158161
log.debug('get_unique_name: name "%s"',name)
159162
return name

test/data/test-cpp.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// sample from Stackoverflow
2+
// #include "test-cpp-inherit.h"
3+
4+
namespace MySpace {
5+
6+
struct Simple {
7+
int i;
8+
};
9+
10+
class Classy {
11+
public:
12+
float f;
13+
void myMethod() { // Method/function defined inside the class
14+
cout << "Hello World!";
15+
}
16+
private:
17+
long line;
18+
};
19+
20+
}
21+
22+
class Base {
23+
24+
private:
25+
int MyPrivateInt;
26+
protected:
27+
int MyProtectedInt;
28+
public:
29+
int MyPublicInt;
30+
};
31+
32+
class Extended : Base
33+
{
34+
public:
35+
Extended();
36+
int MyInt;
37+
virtual void method() const;
38+
};

test/test_record.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -286,26 +286,23 @@ def test_record_named_union(self):
286286
''')
287287
# print(self.text_output)
288288
self.assertIn('struct_s', self.namespace)
289-
self.assertHasFieldNamed('i', self.namespace.struct_s)
289+
self.assertHasFieldNamed(self.namespace.struct_s, 'i')
290290
# we named the unamed anonymous union. .. maybe not the best idea
291-
self.assertHasFieldNamed('_0', self.namespace.struct_s)
291+
self.assertHasFieldNamed(self.namespace.struct_s, '_0')
292292
self.assertIn('union_s_0', self.namespace)
293293
# python ctypes properly merges anonymous union and exposes members
294-
self.assertTrue(hasattr(self.namespace.struct_s, 'f1'))
295-
self.assertTrue(hasattr(self.namespace.struct_s, 'l1'))
296-
# but not as a field
297-
self.assertNotHasFieldNamed('f1', self.namespace.struct_s)
298-
self.assertNotHasFieldNamed('l1', self.namespace.struct_s)
294+
self.assertHasFieldNamed(self.namespace.struct_s, 'f1')
295+
self.assertHasFieldNamed(self.namespace.struct_s, 'l1')
299296
# and not for unamed anonymous union
300-
self.assertNotHasFieldNamed('l2', self.namespace.struct_s)
301-
self.assertNotHasFieldNamed('f2', self.namespace.struct_s)
297+
self.assertNotHasFieldNamed(self.namespace.struct_s, 'l2')
298+
self.assertNotHasFieldNamed(self.namespace.struct_s, 'f2')
302299
# and we have a named union
303-
self.assertHasFieldNamed('u', self.namespace.struct_s)
300+
self.assertHasFieldNamed(self.namespace.struct_s, 'u')
304301
# Issue #117
305302
self.assertNotIn('union_s_1', self.namespace)
306303
self.assertIn('union_s_u', self.namespace)
307-
self.assertHasFieldNamed('l2', self.namespace.union_s_u)
308-
self.assertHasFieldNamed('f2', self.namespace.union_s_u)
304+
self.assertHasFieldNamed(self.namespace.union_s_u, 'l2')
305+
self.assertHasFieldNamed(self.namespace.union_s_u, 'f2')
309306

310307

311308
if __name__ == "__main__":

test/util.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,23 +161,17 @@ def assertOffsets(self, name):
161161
name, membername, _c_offset, _p_offset, self.parser.flags))
162162
return
163163

164-
def assertHasFieldNamed(self, name, py_record):
164+
def assertHasFieldNamed(self, py_record, name):
165165
""" Check that a Py record has a field named name.
166166
"""
167167
self.assertTrue(hasattr(py_record, '_fields_'))
168-
for field_desc in py_record._fields_:
169-
if name == field_desc[0]:
170-
self.assertTrue(name, field_desc[0])
171-
return
172-
self.fail(f"no such field name {name}")
168+
self.assertTrue(hasattr(py_record, name), f"no such field name {name} on {py_record}")
173169

174-
def assertNotHasFieldNamed(self, name, py_record):
170+
def assertNotHasFieldNamed(self, py_record, name):
175171
""" Check that a Py record does not have a field named name.
176172
"""
177173
self.assertTrue(hasattr(py_record, '_fields_'))
178-
for field_desc in py_record._fields_:
179-
if name == field_desc[0]:
180-
self.fail(f"Field named {name} found in record")
174+
self.assertFalse(hasattr(py_record, name), f"Field named {name} found in record")
181175

182176

183177
def clang2py(args):

0 commit comments

Comments
 (0)