Skip to content

Commit e99385a

Browse files
committed
Add "call_guards" option
1 parent 9d221c6 commit e99385a

File tree

5 files changed

+64
-23
lines changed

5 files changed

+64
-23
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ Parser compile options.
8989
**libclang_path** [path, optional]
9090
Path to `libclang` shared library.
9191

92+
**call_guards** [array of string, optional]
93+
Add call_guard to exported function and member function, this option is
94+
useful when you want to add some call_guard to all C++ function.
95+
9296
**include_headers** [array of filename, optional]
9397
**deprecated** `cppygen` does not resolve include paths, thus if you want to export C++
9498
classes you should specify include filenames.

cppygen/__main__.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -70,20 +70,23 @@ def run():
7070
verbose=args.verbose,
7171
)
7272

73-
flags = configs.get("flags") or []
73+
for i in configs.get("call_guards", []):
74+
cppygen.add_call_guard(i)
7475

75-
for i in configs.get("include_directories") or []:
76+
flags = configs.get("flags", [])
77+
78+
for i in configs.get("include_directories", []):
7679
flags.append(f"-I{str(cwd.joinpath(i).absolute())}")
7780

7881
flags.extend([i for i in (args.flags or "").split(";")])
7982
flags.extend([f"-I{i}" for i in (args.include_directories or "").split(";")])
8083

8184
if mode == "source":
8285
for i in sources:
83-
cppygen.parse_from_file(i, lang="cpp", flags=configs.get("flags") or [])
86+
cppygen.parse_from_file(i, lang="cpp", flags=configs.get("flags", []))
8487

8588
for i in headers:
86-
cppygen.parse_from_file(i, lang="hpp", flags=configs.get("flags") or [])
89+
cppygen.parse_from_file(i, lang="hpp", flags=configs.get("flags", []))
8790
else:
8891
cppygen.parse(
8992
source="\n".join([f"#include<{i}>" for i in headers]),

cppygen/component.py

+17-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def __init__(self):
1616
self._description = ""
1717
self._module: str | None = None
1818
self._pyname: str | None = None
19+
self._call_guards: List[str] = []
1920

2021
def set_name(self, name: str, namespace: List[str]):
2122
self._name = name
@@ -25,6 +26,9 @@ def set_name(self, name: str, namespace: List[str]):
2526
def set_return_type(self, type: str):
2627
self._return_type = type
2728

29+
def add_call_guard(self, call_guard: str):
30+
self._call_guards.append(call_guard)
31+
2832
def set_argument_types(self, types: List[Tuple[str, str]]):
2933
"""
3034
parameter: [(name, type),]
@@ -62,12 +66,12 @@ def to_pybind_string(self, overloaded=False):
6266
f'{self._module}.def("{self._pyname}", '
6367
f'static_cast<{self._return_type} (*)({", ".join([i[1] for i in self._arguments])})>'
6468
f'(&{self._full_name}), "{self._description}"'
65-
f'{"".join(args)});'
69+
f"""{"".join(args)}{f", pybind11::call_guard<{', '.join(self._call_guards)}>()" if len(self._call_guards) > 0 else ""});"""
6670
)
6771
else:
6872
return (
6973
f'{self._module}.def("{self._pyname}", &{self._full_name}, "{self._description}"'
70-
f'{"".join(args)});'
74+
f"""{"".join(args)}{f", pybind11::call_guard<{', '.join(self._call_guards)}>()" if len(self._call_guards) > 0 else ""});"""
7175
)
7276

7377
def to_decl_string(self):
@@ -105,8 +109,9 @@ class MemberFunctionSignature(TypedDict):
105109
name: str
106110
pyname: str
107111
return_type: str
108-
description: str
109112
args: List[Tuple[str, str]]
113+
description: str
114+
call_guards: List[str]
110115

111116
def __init__(self):
112117
self._name: str | None = None
@@ -141,9 +146,10 @@ def add_member_func(
141146
self,
142147
name: str,
143148
pyname: str | None,
144-
type: str,
149+
return_type: str,
145150
args: List[Tuple[str, str]], # list[(name, type)]
146151
description: str = "",
152+
call_guards: List[str] = [],
147153
private: bool = False,
148154
):
149155
pyname = pyname or name
@@ -152,9 +158,10 @@ def add_member_func(
152158
{
153159
"name": name,
154160
"pyname": pyname,
155-
"return_type": type,
156-
"description": description,
161+
"return_type": return_type,
157162
"args": args,
163+
"description": description,
164+
"call_guards": call_guards,
158165
}
159166
)
160167

@@ -193,11 +200,13 @@ def to_pybind_string(self):
193200
# overloaded funciton
194201
f'\t\t.def("{i["pyname"]}", '
195202
f'static_cast<{i["return_type"]} ({self._full_name}::*)({", ".join([j[1] for j in i["args"]])})>'
196-
f'(&{self._full_name}::{i["name"]}), "{i["description"]}")'
203+
f'(&{self._full_name}::{i["name"]}), "{i["description"]}"'
204+
f"""{f", pybind11::call_guard<{', '.join(i['call_guards'])}>()" if len(i['call_guards']) > 0 else ""})"""
197205
if [j["name"] for j in self._member_funcs].count(i["name"]) > 1
198206
# Non overloaded funciton
199207
else f'\t\t.def("{i["pyname"]}",'
200-
f' &{self._full_name}::{i["name"]}, "{i["description"]}")'
208+
f' &{self._full_name}::{i["name"]}, "{i["description"]}"'
209+
f"""{f", pybind11::call_guard<{', '.join(i['call_guards'])}>()" if len(i['call_guards']) > 0 else ""})"""
201210
for i in self._member_funcs
202211
]
203212
)

cppygen/cppygen_parser.py

+17-10
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ def __init__(
2929
self._structs_and_classes: List[StructOrClass] = []
3030
self._hpp_includes: List[str] = []
3131
self._namespace = namespace or "cppygen"
32-
self.verbose = verbose
32+
self._verbose = verbose
33+
self._call_guards: List[str] = []
3334

3435
if library_file != None and library_path != None:
3536
raise ValueError(f"Both library_path and library_file cannot be set.")
@@ -76,6 +77,8 @@ def _extract_functions(
7677
func.set_return_type(i.result_type.spelling)
7778
func.set_name(i.spelling, namespace)
7879
func.set_module(module_name)
80+
for call_guard in self._call_guards:
81+
func.add_call_guard(call_guard)
7982

8083
# extract comment string
8184
raw_comment = i.raw_comment or ""
@@ -90,7 +93,7 @@ def _extract_functions(
9093
j: Cursor
9194
if j.kind == CursorKind.PARM_DECL: # type: ignore
9295
func.add_argument_type((j.spelling, j.type.spelling))
93-
if self.verbose:
96+
if self._verbose:
9497
print("\t| Function | " + func.signature())
9598
if not func in self._functions:
9699
self._functions.append(func)
@@ -125,20 +128,24 @@ def _extract_struct_and_class(
125128
j.raw_comment or ""
126129
)
127130
struct_or_class.add_member_func(
128-
j.spelling,
129-
pyname,
130-
j.result_type.spelling,
131-
args,
132-
description or "",
133-
j.access_specifier == AccessSpecifier.PRIVATE, # type: ignore
131+
name=j.spelling,
132+
pyname=pyname,
133+
return_type=j.result_type.spelling,
134+
args=args,
135+
description=description or "",
136+
call_guards=self._call_guards,
137+
private=j.access_specifier == AccessSpecifier.PRIVATE, # type: ignore
134138
)
135-
if self.verbose:
139+
if self._verbose:
136140
print("\t| Class | " + struct_or_class.signature())
137141
self._structs_and_classes.append(struct_or_class)
138142

139143
def add_hpp_includes(self, hpp: str):
140144
self._hpp_includes.append(hpp)
141145

146+
def add_call_guard(self, call_guard: str):
147+
self._call_guards.append(call_guard)
148+
142149
def parse(
143150
self,
144151
source: str,
@@ -184,7 +191,7 @@ def visit(x: Cursor, namespace: List[str], module_name: str):
184191
submod.set_description(i.brief_comment or "")
185192
submod.set_parent(copy.deepcopy(namespace_in))
186193
if not submod in self._submodules:
187-
if self.verbose:
194+
if self._verbose:
188195
print(f"\t| Submodule | {submod.cpp_name}")
189196
self._submodules.append(submod)
190197
namespace_in.append(i.spelling)

tests/test_components.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ def test_function():
3737
"""(&Shell::foo::test_function1), "this is test function1", pybind11::arg("arg1"), pybind11::arg("arg2"));"""
3838
)
3939

40+
fun.add_call_guard("int")
41+
42+
assert fun.to_pybind_string(overloaded=True) == (
43+
"""Shell_foo.def("__str__", static_cast<TestClass (*)(int, std::string)>"""
44+
"""(&Shell::foo::test_function1), "this is test function1", pybind11::arg("arg1"), pybind11::arg("arg2"), pybind11::call_guard<int>());"""
45+
)
46+
47+
fun.add_call_guard("hoge")
48+
49+
assert fun.to_pybind_string(overloaded=True) == (
50+
"""Shell_foo.def("__str__", static_cast<TestClass (*)(int, std::string)>"""
51+
"""(&Shell::foo::test_function1), "this is test function1", pybind11::arg("arg1"), pybind11::arg("arg2"), pybind11::call_guard<int, hoge>());"""
52+
)
53+
4054
assert (
4155
fun.signature()
4256
== """Shell::foo::test_function1(int, std::string) -> TestClass"""
@@ -103,6 +117,7 @@ def test_struct_or_class():
103117
"char",
104118
[("arg1", "bool")],
105119
"this is the member2(overloaded)",
120+
["int", "bool"],
106121
)
107122

108123
expect_members = [
@@ -125,20 +140,23 @@ def test_struct_or_class():
125140
"pyname": "mfun1",
126141
"return_type": "int",
127142
"description": "this is the member1",
143+
"call_guards": [],
128144
"args": [("arg1", "int"), ("arg2", "std::string")],
129145
},
130146
{
131147
"name": "mfun2",
132148
"pyname": "mfun2",
133149
"return_type": "char",
134150
"description": "this is the member2",
151+
"call_guards": [],
135152
"args": [("arg1", "std::string"), ("arg2", "std::vector<int>")],
136153
},
137154
{
138155
"name": "mfun2",
139156
"pyname": "mfun2",
140157
"return_type": "char",
141158
"description": "this is the member2(overloaded)",
159+
"call_guards": ["int", "bool"],
142160
"args": [("arg1", "bool")],
143161
},
144162
]
@@ -151,6 +169,6 @@ def test_struct_or_class():
151169
"""\t\t.def_readwrite("member2", &mod1::mod2::class1::member2, "this is the member2")\n"""
152170
"""\t\t.def("mfun1", &mod1::mod2::class1::mfun1, "this is the member1")\n"""
153171
"""\t\t.def("mfun2", static_cast<char (mod1::mod2::class1::*)(std::string, std::vector<int>)>(&mod1::mod2::class1::mfun2), "this is the member2")\n"""
154-
"""\t\t.def("mfun2", static_cast<char (mod1::mod2::class1::*)(bool)>(&mod1::mod2::class1::mfun2), "this is the member2(overloaded)");"""
172+
"""\t\t.def("mfun2", static_cast<char (mod1::mod2::class1::*)(bool)>(&mod1::mod2::class1::mfun2), "this is the member2(overloaded)", pybind11::call_guard<int, bool>());"""
155173
)
156174
assert soc.to_pybind_string() == expect_pybind_string

0 commit comments

Comments
 (0)