Skip to content

Commit 8aeb66e

Browse files
committed
generate-accessors: add char double-pointer accessor support
The next data structure to be moved out of the public API contains string arrays. Extend the generator to create getter/setters for this type. Signed-off-by: Daniel Wagner <[email protected]>
1 parent 82e3e0b commit 8aeb66e

2 files changed

Lines changed: 111 additions & 25 deletions

File tree

libnvme/tools/generator/generate-accessors.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -289,19 +289,20 @@ __public const char *person_get_role(const struct person *p)
289289
290290
- `typedef struct` is not supported.
291291
- Nested structs (a `struct` member whose type is also a `struct`) are skipped.
292-
- Only `char *` pointer members are supported; other pointer types are skipped.
292+
- Only `char *` and `char **` pointer members are supported; other pointer types are skipped.
293293
294294
------
295295
296296
## Notes
297297
298298
1. **Dynamic strings** (`char *`) — setters store a `strdup()` copy; passing `NULL` clears the field.
299-
2. **Fixed char arrays** (`char foo[N]`) — setters use `snprintf`, always NUL-terminated.
300-
3. **`const` members** — only a getter is generated, no setter (applies regardless of any annotation).
301-
4. **`//!accessors:readonly`** — same effect as `const`: getter only.
302-
5. **`//!accessors:writeonly`** — setter only; getter is suppressed.
303-
6. **`//!accessors:readwrite`** — both getter and setter; overrides a restrictive struct-level default.
304-
7. **`//!accessors:none`** — member is completely ignored by the generator.
305-
8. **Struct-level mode** — the qualifier on `generate-accessors` sets the default for every member in the struct; per-member annotations override the struct default.
306-
9. **`--prefix`** — prepended to every function name (e.g. `--prefix nvme_` turns `ctrl_set_name` into `nvme_ctrl_set_name`).
307-
10. **Line length** — generated code is automatically wrapped to stay within the 80-column limit required by `checkpatch.pl`.
299+
2. **String arrays** (`char **`) — setters deep-copy NULL-terminated arrays (each element and the container).
300+
3. **Fixed char arrays** (`char foo[N]`) — setters use `snprintf`, always NUL-terminated.
301+
4. **`const` members** — only a getter is generated, no setter (applies regardless of any annotation).
302+
5. **`//!accessors:readonly`** — same effect as `const`: getter only.
303+
6. **`//!accessors:writeonly`** — setter only; getter is suppressed.
304+
7. **`//!accessors:readwrite`** — both getter and setter; overrides a restrictive struct-level default.
305+
8. **`//!accessors:none`** — member is completely ignored by the generator.
306+
9. **Struct-level mode** — the qualifier on `generate-accessors` sets the default for every member in the struct; per-member annotations override the struct default.
307+
10. **`--prefix`** — prepended to every function name (e.g. `--prefix nvme_` turns `ctrl_set_name` into `nvme_ctrl_set_name`).
308+
11. **Line length** — generated code is automatically wrapped to stay within the 80-column limit required by `checkpatch.pl`.

libnvme/tools/generator/generate-accessors.py

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,17 @@ class Member:
203203
"""Represents one member of a parsed C struct."""
204204

205205
__slots__ = ('name', 'type', 'gen_getter', 'gen_setter',
206-
'is_char_array', 'array_size')
206+
'is_char_array', 'is_char_ptr_array', 'array_size')
207207

208208
def __init__(self, name, type_str, gen_getter, gen_setter,
209-
is_char_array, array_size):
209+
is_char_array, is_char_ptr_array, array_size):
210210
self.name = name
211211
self.type = type_str # e.g. "const char *", "int", "__u32"
212212
self.gen_getter = gen_getter # True → emit getter
213213
self.gen_setter = gen_setter # True → emit setter
214214
self.is_char_array = is_char_array
215-
self.array_size = array_size # only valid when is_char_array is True
215+
self.is_char_ptr_array = is_char_ptr_array
216+
self.array_size = array_size # only for fixed-size char arrays (char[N])
216217

217218

218219
# ---------------------------------------------------------------------------
@@ -274,6 +275,7 @@ def parse_members(struct_name, raw_body, struct_mode, verbose):
274275
gen_getter=gen_getter,
275276
gen_setter=gen_setter and not is_const_qual,
276277
is_char_array=True,
278+
is_char_ptr_array=False,
277279
array_size=m.group(3),
278280
))
279281
continue
@@ -286,11 +288,16 @@ def parse_members(struct_name, raw_body, struct_mode, verbose):
286288
ptr_part = m.group(3)
287289
name = m.group(4)
288290

289-
is_ptr = '*' in ptr_part
290-
if is_ptr:
291+
ptr_depth = ptr_part.count('*')
292+
if ptr_depth:
291293
if type_base != 'char':
292294
continue # only char* pointers are supported
293-
type_str = 'const char *'
295+
if ptr_depth == 1:
296+
type_str = 'const char *'
297+
elif ptr_depth == 2:
298+
type_str = 'const char *const *'
299+
else:
300+
continue
294301
else:
295302
type_str = type_base
296303

@@ -300,6 +307,7 @@ def parse_members(struct_name, raw_body, struct_mode, verbose):
300307
gen_getter=gen_getter,
301308
gen_setter=gen_setter and not is_const_qual,
302309
is_char_array=False,
310+
is_char_ptr_array=(ptr_depth == 2),
303311
array_size=None,
304312
))
305313

@@ -418,6 +426,28 @@ def emit_hdr_setter_str(f, prefix, sname, mname, is_dyn_str):
418426
)
419427

420428

429+
def emit_hdr_setter_str_array(f, prefix, sname, mname):
430+
"""Emit a header declaration for a string-array setter."""
431+
f.write(
432+
f'/**\n'
433+
f' * {_set_name(prefix, sname, mname)}() - Set {mname}.\n'
434+
f' * @p: The &struct {sname} instance to update.\n'
435+
f' * @{mname}: New NULL-terminated string array; deep-copied.\n'
436+
f' */\n'
437+
)
438+
439+
single = (f'void {_set_name(prefix, sname, mname)}'
440+
f'(struct {sname} *p, const char *const *{mname});')
441+
if fits_80(single):
442+
f.write(single + '\n\n')
443+
else:
444+
f.write(
445+
f'void {_set_name(prefix, sname, mname)}(\n'
446+
f'\t\tstruct {sname} *p,\n'
447+
f'\t\tconst char *const *{mname});\n\n'
448+
)
449+
450+
421451
def emit_hdr_setter_val(f, prefix, sname, mname, mtype):
422452
"""Emit a header declaration for a value setter."""
423453
f.write(
@@ -468,9 +498,12 @@ def generate_hdr(f, prefix, struct_name, members):
468498
"""Write header declarations for all members of one struct."""
469499
for member in members:
470500
is_dyn_str = (not member.is_char_array and
501+
not member.is_char_ptr_array and
471502
member.type == 'const char *')
472503
if member.gen_setter:
473-
if member.is_char_array or is_dyn_str:
504+
if member.is_char_ptr_array:
505+
emit_hdr_setter_str_array(f, prefix, struct_name, member.name)
506+
elif member.is_char_array or is_dyn_str:
474507
emit_hdr_setter_str(f, prefix, struct_name,
475508
member.name, is_dyn_str)
476509
else:
@@ -533,6 +566,49 @@ def emit_src_setter_chararray(f, prefix, sname, mname, array_size):
533566
)
534567

535568

569+
def emit_src_setter_str_array(f, prefix, sname, mname):
570+
"""Emit a NULL-terminated string-array setter (deep copy)."""
571+
sig = (f'{PUB}void {_set_name(prefix, sname, mname)}'
572+
f'(struct {sname} *p, const char *const *{mname})')
573+
if fits_80(sig):
574+
f.write(sig + '\n')
575+
else:
576+
f.write(
577+
f'{PUB}void {_set_name(prefix, sname, mname)}(\n'
578+
f'\t\tstruct {sname} *p,\n'
579+
f'\t\tconst char *const *{mname})\n'
580+
)
581+
582+
f.write(
583+
'{\n'
584+
'\tchar **new_array = NULL;\n'
585+
'\tsize_t i;\n\n'
586+
f'\tif ({mname}) {{\n'
587+
f'\t\tfor (i = 0; {mname}[i]; i++)\n'
588+
'\t\t\t;\n'
589+
'\n'
590+
'\t\tnew_array = calloc(i + 1, sizeof(char *));\n'
591+
'\t\tif (new_array != NULL) {\n'
592+
f'\t\t\tfor (i = 0; {mname}[i]; i++) {{\n'
593+
f'\t\t\t\tnew_array[i] = strdup({mname}[i]);\n'
594+
'\t\t\t\tif (!new_array[i]) {\n'
595+
'\t\t\t\t\twhile (i > 0)\n'
596+
'\t\t\t\t\t\tfree(new_array[--i]);\n'
597+
'\t\t\t\t\tfree(new_array);\n'
598+
'\t\t\t\t\tnew_array = NULL;\n'
599+
'\t\t\t\t\tbreak;\n'
600+
'\t\t\t\t}\n'
601+
'\t\t\t}\n'
602+
'\t\t}\n'
603+
'\t}\n\n'
604+
f'\tfor (i = 0; p->{mname} && p->{mname}[i]; i++)\n'
605+
f'\t\tfree(p->{mname}[i]);\n'
606+
f'\tfree(p->{mname});\n'
607+
f'\tp->{mname} = new_array;\n'
608+
'}\n\n'
609+
)
610+
611+
536612
def emit_src_setter_val(f, prefix, sname, mname, mtype):
537613
"""Emit a value setter (direct assignment)."""
538614
sig = (f'{PUB}void {_set_name(prefix, sname, mname)}'
@@ -551,40 +627,49 @@ def emit_src_setter_val(f, prefix, sname, mname, mtype):
551627
)
552628

553629

554-
def emit_src_getter(f, prefix, sname, mname, mtype):
555-
"""Emit a getter (return member value)."""
630+
def emit_src_getter(f, prefix, sname, mname, mtype, cast=None):
631+
"""Emit a getter (return member value).
632+
633+
*cast* is an optional C cast expression (e.g. ``'(const char *const *)'``)
634+
inserted before ``p->mname`` in the return statement. Required when the
635+
declared return type differs from the member's raw storage type (e.g. a
636+
``char **`` field exposed as ``const char *const *``).
637+
"""
556638
sep = type_sep(mtype)
557639
sig = (f'{PUB}{mtype}{sep}{_get_name(prefix, sname, mname)}'
558640
f'(const struct {sname} *p)')
641+
ret = f'\treturn {cast}p->{mname};\n' if cast else f'\treturn p->{mname};\n'
559642
if fits_80(sig):
560-
f.write(
561-
sig + '\n'
562-
f'{{\n\treturn p->{mname};\n}}\n\n'
563-
)
643+
f.write(sig + '\n' f'{{\n{ret}}}\n\n')
564644
else:
565645
f.write(
566646
f'{PUB}{mtype}{sep}{_get_name(prefix, sname, mname)}(\n'
567647
f'\t\tconst struct {sname} *p)\n'
568-
f'{{\n\treturn p->{mname};\n}}\n\n'
648+
f'{{\n{ret}}}\n\n'
569649
)
570650

571651

572652
def generate_src(f, prefix, struct_name, members):
573653
"""Write source implementations for all members of one struct."""
574654
for member in members:
575655
is_dyn_str = (not member.is_char_array and
656+
not member.is_char_ptr_array and
576657
member.type == 'const char *')
577658
if member.gen_setter:
578659
if is_dyn_str:
579660
emit_src_setter_dynstr(f, prefix, struct_name, member.name)
661+
elif member.is_char_ptr_array:
662+
emit_src_setter_str_array(f, prefix, struct_name, member.name)
580663
elif member.is_char_array:
581664
emit_src_setter_chararray(f, prefix, struct_name,
582665
member.name, member.array_size)
583666
else:
584667
emit_src_setter_val(f, prefix, struct_name,
585668
member.name, member.type)
586669
if member.gen_getter:
587-
emit_src_getter(f, prefix, struct_name, member.name, member.type)
670+
cast = '(const char *const *)' if member.is_char_ptr_array else None
671+
emit_src_getter(f, prefix, struct_name, member.name, member.type,
672+
cast=cast)
588673

589674

590675
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)