From 75d8a974a8ca473f03019d5dcfcfa2a8d055576e Mon Sep 17 00:00:00 2001 From: gen740 Date: Mon, 12 Jun 2023 10:17:00 +0900 Subject: [PATCH] Update --- cppygen/component.py | 270 ----------------------------- cppygen/cppclass.py | 142 +++++++++++++++ cppygen/cppygen_parser.py | 71 +++++--- cppygen/function.py | 97 +++++++++++ cppygen/submodule.py | 47 +++++ cppygen/template_class.py | 41 +++++ example/header_mode/CMakeLists.txt | 6 + example/header_mode/Taskfile.yml | 2 +- example/header_mode/python/test.py | 32 ++++ example/header_mode/shell/hoge.cpp | 38 +--- example/header_mode/shell/hoge.hpp | 65 +++++-- example/header_mode/shell/piyo.cpp | 14 -- example/header_mode/shell/piyo.hpp | 15 -- example/header_mode/stubgen.sh | 6 +- tests/test_components.py | 3 +- 15 files changed, 479 insertions(+), 370 deletions(-) delete mode 100644 cppygen/component.py create mode 100644 cppygen/cppclass.py create mode 100644 cppygen/function.py create mode 100644 cppygen/submodule.py create mode 100644 cppygen/template_class.py create mode 100644 example/header_mode/python/test.py delete mode 100644 example/header_mode/shell/piyo.cpp delete mode 100644 example/header_mode/shell/piyo.hpp diff --git a/cppygen/component.py b/cppygen/component.py deleted file mode 100644 index 00717c5..0000000 --- a/cppygen/component.py +++ /dev/null @@ -1,270 +0,0 @@ -from typing import Dict, List, Tuple, TypedDict, cast - - -class Function(object): - """ - Function を表すクラス。 - 必要な情報を詰め込み、 to_pybind_string で生成する。 - """ - - def __init__(self): - self._return_type: str = "" - self._arguments: List[Tuple[str, str]] = [] - self._name: str | None = None - self._full_name: str | None = None - self._namespace: List[str] = [] - self._description = "" - self._module: str | None = None - self._pyname: str | None = None - self._call_guards: List[str] = [] - - def set_name(self, name: str, namespace: List[str]): - self._name = name - self._namespace = namespace - self._full_name = f"{'::'.join(namespace)}::{name}" - - def set_return_type(self, type: str): - self._return_type = type - - def add_call_guard(self, call_guard: str): - self._call_guards.append(call_guard) - - def set_argument_types(self, types: List[Tuple[str, str]]): - """ - parameter: [(name, type),] - """ - self._arguments = types - - @property - def pyname(self): - self._pyname - - @pyname.setter - def pyname(self, python_name: str): - self._pyname = python_name - - def add_argument_type(self, type: Tuple[str, str]): - """ - parameter: (name, type) - """ - self._arguments.append(type) - - def set_description(self, description: str): - self._description = description - - def set_module(self, module: str): - self._module = module - - def to_pybind_string(self, overloaded=False): - if self._name == None or self._full_name == None or self._module == None: - print("Parse Error Skipping ...") - return "" - args = [f', pybind11::arg("{i[0]}")' for i in self._arguments] - self._pyname = self._pyname or self._name - if overloaded: - return ( - f'{self._module}.def("{self._pyname}", ' - f'static_cast<{self._return_type} (*)({", ".join([i[1] for i in self._arguments])})>' - f'(&{self._full_name}), "{self._description}"' - f"""{"".join(args)}{f", pybind11::call_guard<{', '.join(self._call_guards)}>()" if len(self._call_guards) > 0 else ""});""" - ) - else: - return ( - f'{self._module}.def("{self._pyname}", &{self._full_name}, "{self._description}"' - f"""{"".join(args)}{f", pybind11::call_guard<{', '.join(self._call_guards)}>()" if len(self._call_guards) > 0 else ""});""" - ) - - def to_decl_string(self): - if self._name == None or self._full_name == None or self._module == None: - print("Parse Error Skipping ...") - return "" - args = [f"{i[1]}" for i in self._arguments] - return ( - f'namespace {"::".join(self._namespace)} ' - f'{{ {self._return_type} {self._name}({", ".join(args)}); }}' - ) - - def signature(self, with_return_type=True) -> str: - args = [f"{i[1]}" for i in self._arguments] - if with_return_type: - return f'{"::".join(self._namespace)}::{self._name}({", ".join(args)}) -> {self._return_type}' - else: - return f'{"::".join(self._namespace)}::{self._name}({", ".join(args)})' - - def __eq__(self, obj): - if isinstance(obj, Function): - return self.signature(with_return_type=False) == obj.signature( - with_return_type=False - ) - else: - return False - - -class StructOrClass: - """ - Represent Struct or Class. - """ - - class MemberFunctionSignature(TypedDict): - name: str - pyname: str - return_type: str - args: List[Tuple[str, str]] - description: str - call_guards: List[str] - - def __init__(self): - self._name: str | None = None - self._base_classes: List[str] = [] - self._namespace: List[str] = [] - self._members: list[Dict[str, str]] = [] - self._member_funcs: list[StructOrClass.MemberFunctionSignature] = [] - self._module: str | None = None - self._description = "" - - def set_name(self, name: str, namespace: List[str]): - self._name = name - self._namespace = namespace - self._full_name = f"{'::'.join(namespace)}::{name}" - - def add_member( - self, - name: str, - type: str, - description: str = "", - private: bool = False, - ): - if not private: - self._members.append( - { - "name": name, - "type": type, - "description": description, - } - ) - - def add_base_class(self, name: str): - self._base_classes.append(name) - - def add_member_func( - self, - name: str, - pyname: str | None, - return_type: str, - args: List[Tuple[str, str]], # list[(name, type)] - description: str = "", - call_guards: List[str] = [], - private: bool = False, - ): - pyname = pyname or name - if not private: - self._member_funcs.append( - { - "name": name, - "pyname": pyname, - "return_type": return_type, - "args": args, - "description": description, - "call_guards": call_guards, - } - ) - - def set_module(self, module: str): - self._module = module - - def set_description(self, description: str): - self._description = description - - def get_members(self): - return self._members - - def get_member_funcs(self): - return self._member_funcs - - def to_pybind_string(self): - if self._name == None or self._module == None: - print("Parse Error Skipping ...") - return "" - return ( - f"pybind11::class_<" - + ", ".join([f"::{self._full_name}", *self._base_classes]) - + ">" - + f'({self._module}, "{self._name}")\n' - "\t\t.def(pybind11::init())" - # Declare members. - + "\n".join( - [""] - + [ - f'\t\t.def_readwrite("{i["name"]}",' - f' &{self._full_name}::{i["name"]}, "{i["description"]}")' - for i in self._members - ] - ) - # Declare member functions. - + "\n".join( - [""] - + [ - # overloaded funciton - f'\t\t.def("{i["pyname"]}", ' - f'static_cast<{i["return_type"]} ({self._full_name}::*)({", ".join([j[1] for j in i["args"]])})>' - f'(&{self._full_name}::{i["name"]}), "{i["description"]}"' - f"""{f", pybind11::call_guard<{', '.join(i['call_guards'])}>()" if len(i['call_guards']) > 0 else ""})""" - if [j["name"] for j in self._member_funcs].count(i["name"]) > 1 - # Non overloaded funciton - else f'\t\t.def("{i["pyname"]}",' - f' &{self._full_name}::{i["name"]}, "{i["description"]}"' - f"""{f", pybind11::call_guard<{', '.join(i['call_guards'])}>()" if len(i['call_guards']) > 0 else ""})""" - for i in self._member_funcs - ] - ) - + ";" - ) - - def signature(self) -> str: - return f"{self._full_name}" - - -class Submodule: - """ - Represent Submodule. - """ - - def __init__(self): - self._name: str | None = None - self._description = "" - self._parents: List[str] = [] - - @property - def cpp_name(self) -> str: - if self._name == None: - print("Parse Error Skipping ...") - return "" - return "_".join(self._parents) + "_" + self._name - - @property - def cpp_parent_name(self) -> str: - if self._name == None: - print("Parse Error Skipping ...") - return "" - return "_".join(self._parents) - - def set_name(self, name: str): - self._name = name - - def set_description(self, description: str): - self._description = description - - def set_parent(self, parents: List[str]): - self._parents = parents - - def to_pybind_string(self): - if self._name == None: - print("Parse Error Skipping ...") - return "" - return f'auto {self.cpp_name} = {self.cpp_parent_name}.def_submodule("{self._name}", "{self._description}");' - - def __eq__(self, obj): - if isinstance(obj, Submodule): - return self.cpp_name == obj.cpp_name - else: - return False diff --git a/cppygen/cppclass.py b/cppygen/cppclass.py new file mode 100644 index 0000000..b4b7a34 --- /dev/null +++ b/cppygen/cppclass.py @@ -0,0 +1,142 @@ +import copy +import re +from typing import TypedDict + + +class CppClass: + """ + Represent Struct or Class. + """ + + class MemberFunctionSignature(TypedDict): + name: str + pyname: str + return_type: str + args: list[tuple[str, str]] + description: str + call_guards: list[str] + + def __init__(self, is_template=False, defined_template_classes=[]): + self._name: str | None = None + self._sanitized_name: str | None = None + self._base_classes: list[CppClass] = [] + self._namespace: list[str] = [] + self._members: list[dict[str, str]] = [] + self._member_funcs: list[CppClass.MemberFunctionSignature] = [] + self._module: str | None = None + self._description = "" + self._is_template = is_template + self._defined_template_classes: list[CppClass] = defined_template_classes + self._template_parameter: list[tuple[str, str | None]] = [] + + def set_name(self, name: str, namespace: list[str] | None = None): + self._name = name + self._sanitized_name = "_".join(filter(None, re.findall(r"\w*", self._name))) + if namespace is not None: + self._namespace = namespace + self._full_name = f"{'::'.join(self._namespace)}::{name}" + + def add_member( + self, + name: str, + type: str, + description: str = "", + private: bool = False, + ): + if not private: + self._members.append( + { + "name": name, + "type": type, + "description": description, + } + ) + + def add_base_class(self, name: str): + for i in self._defined_template_classes: + if i._full_name in name: + base_class = copy.deepcopy(i) + base_class.set_name(name.split("::")[-1]) + self._base_classes.append(base_class) + + def add_member_func( + self, + name: str, + pyname: str | None, + return_type: str, + args: list[tuple[str, str]], # list[(name, type)] + description: str = "", + call_guards: list[str] = [], + private: bool = False, + ): + pyname = pyname or name + if not private: + self._member_funcs.append( + { + "name": name, + "pyname": pyname, + "return_type": return_type, + "args": args, + "description": description, + "call_guards": call_guards, + } + ) + + def set_module(self, module: str): + self._module = module + + def set_description(self, description: str): + self._description = description + + def get_members(self): + return self._members + + def get_member_funcs(self): + return self._member_funcs + + def to_pybind_string(self): + if self._name == None or self._module == None: + print("Parse Error Skipping ...") + return "" + return ( + # BaseClass + "\n".join([i.to_pybind_string() for i in self._base_classes]) + "\n" + # Class + + f"pybind11::class_<" + + ", ".join( + [f"::{self._full_name}", *[i._full_name for i in self._base_classes]] + ) + + ">" + + f'({self._module}, "{self._sanitized_name}")\n' + "\t\t.def(pybind11::init())" + # Declare members. + + "\n".join( + [""] + + [ + f'\t\t.def_readwrite("{i["name"]}",' + f' &{self._full_name}::{i["name"]}, "{i["description"]}")' + for i in self._members + ] + ) + # Declare member functions. + + "\n".join( + [""] + + [ + # overloaded funciton + f'\t\t.def("{i["pyname"]}", ' + f'static_cast<{i["return_type"]} ({self._full_name}::*)({", ".join([j[1] for j in i["args"]])})>' + f'(&{self._full_name}::{i["name"]}), "{i["description"]}"' + f"""{f", pybind11::call_guard<{', '.join(i['call_guards'])}>()" if len(i['call_guards']) > 0 else ""})""" + if [j["name"] for j in self._member_funcs].count(i["name"]) > 1 + # Non overloaded funciton + else f'\t\t.def("{i["pyname"]}",' + f' &{self._full_name}::{i["name"]}, "{i["description"]}"' + f"""{f", pybind11::call_guard<{', '.join(i['call_guards'])}>()" if len(i['call_guards']) > 0 else ""})""" + for i in self._member_funcs + ] + ) + + ";" + ) + + def signature(self) -> str: + return f"{self._full_name}" diff --git a/cppygen/cppygen_parser.py b/cppygen/cppygen_parser.py index 21c39e4..7d8eee1 100644 --- a/cppygen/cppygen_parser.py +++ b/cppygen/cppygen_parser.py @@ -1,7 +1,7 @@ import copy import os import re -from typing import List, Literal +from typing import Literal from cppygen._clang.cindex import ( AccessSpecifier, @@ -11,8 +11,10 @@ TranslationUnit, ) -from .component import Function, StructOrClass, Submodule +from .cppclass import CppClass +from .function import Function from .logging import get_logger +from .submodule import Submodule logger = get_logger("parser") @@ -30,13 +32,13 @@ def __init__( library_file: str | None = None, verbose: bool = False, ): - self._functions: List[Function] = [] - self._submodules: List[Submodule] = [] - self._structs_and_classes: List[StructOrClass] = [] - self._hpp_includes: List[str] = [] + self._functions: list[Function] = [] + self._submodules: list[Submodule] = [] + self._cpp_classes: list[CppClass] = [] + self._hpp_includes: list[str] = [] self._namespace = namespace or "cppygen" self._verbose = verbose - self._call_guards: List[str] = [] + self._call_guards: list[str] = [] if library_file != None and library_path != None: raise ValueError(f"Both library_path and library_file cannot be set.") @@ -72,7 +74,7 @@ def _get_tu(self, source: str, filename: str, flags=[]) -> TranslationUnit: def _extract_functions( self, cu: Cursor, - namespace: List[str], + namespace: list[str], module_name: str, mode: Literal["source"] | Literal["header"] = "source", ): @@ -88,8 +90,10 @@ def _extract_functions( # extract comment string raw_comment = i.raw_comment or "" + raw_comment = raw_comment.replace('"', '\\"') + # print("raw_comment", raw_comment) - pyname, description = _extract_comment_string(raw_comment) + pyname, description = _extract_comment_string(str(raw_comment)) if pyname is not None: func.pyname = pyname @@ -105,24 +109,36 @@ def _extract_functions( self._functions.append(func) def _extract_struct_and_class( - self, cu: Cursor, namespace: List[str], module_name: str + self, cu: Cursor, namespace: list[str], module_name: str ): - def visit(i: Cursor, namespace: List[str]): - struct_or_class = StructOrClass() - struct_or_class.set_name(i.spelling, namespace) - struct_or_class.set_module(module_name) - struct_or_class.set_description(i.brief_comment or "") + def visit(i: Cursor, namespace: list[str], is_template): + print(i.kind) + cpp_class = CppClass( + is_template, + copy.deepcopy([j for j in self._cpp_classes if j._is_template]), + ) + cpp_class.set_name(i.spelling, namespace) + cpp_class.set_module(module_name) + cpp_class.set_description(i.brief_comment or "") if self._verbose: - print("\t| Class | " + struct_or_class.signature()) + print("\t| Class | " + cpp_class.signature()) for j in list(i.get_children()): j: Cursor if j.kind == CursorKind.CXX_BASE_SPECIFIER: # type: ignore - struct_or_class.add_base_class(j.spelling) + if self._verbose: + print( + "\t| BaseClass | " + + "::".join([*namespace, i.spelling]) + + j.spelling + ) + cpp_class.add_base_class(j.spelling) if j.kind == CursorKind.STRUCT_DECL or j.kind == CursorKind.CLASS_DECL: # type: ignore - visit(j, [*namespace, i.spelling]) + visit(j, [*namespace, i.spelling], False) + if j.kind == CursorKind.CLASS_TEMPLATE: # type: ignore + visit(j, [*namespace, i.spelling], True) if j.kind == CursorKind.FIELD_DECL: # type: ignore # メンバー変数の抽出 - struct_or_class.add_member( + cpp_class.add_member( j.spelling, j.type.spelling, j.brief_comment or "", @@ -144,7 +160,7 @@ def visit(i: Cursor, namespace: List[str]): if k.kind == CursorKind.PARM_DECL: # type: ignore args.append((k.spelling, k.type.spelling)) (pyname, description) = _extract_comment_string(j.raw_comment or "") - struct_or_class.add_member_func( + cpp_class.add_member_func( name=j.spelling, pyname=pyname, return_type=j.result_type.spelling, @@ -164,12 +180,14 @@ def visit(i: Cursor, namespace: List[str]): + ") -> " + j.result_type.spelling ) - self._structs_and_classes.append(struct_or_class) + self._cpp_classes.append(cpp_class) i: Cursor for i in list(cu.get_children()): + if i.kind == CursorKind.CLASS_TEMPLATE: # type: ignore + visit(i, namespace, True) if i.kind == CursorKind.STRUCT_DECL or i.kind == CursorKind.CLASS_DECL: # type: ignore - visit(i, namespace) + visit(i, namespace, False) def add_hpp_includes(self, hpp: str): self._hpp_includes.append(hpp) @@ -202,7 +220,7 @@ def parse( i: Cursor # Recursive Function - def visit(x: Cursor, namespace: List[str], module_name: str): + def visit(x: Cursor, namespace: list[str], module_name: str): if mode == "source": if lang == "cpp": self._extract_functions(x, namespace, module_name) @@ -275,7 +293,12 @@ def to_export_string(self): + "\t/* Function Export End */\n\n" + "\t/* Structs and Classes Export Start */\n" + "\n".join( - ["\t" + i.to_pybind_string() for i in self._structs_and_classes] + [""] + [ + "\t" + i.to_pybind_string() + for i in self._cpp_classes + if not i._is_template + ] + + [""] ) + "\t/* Structs and Classes Export End */\n\n" ) diff --git a/cppygen/function.py b/cppygen/function.py new file mode 100644 index 0000000..1cfd4ee --- /dev/null +++ b/cppygen/function.py @@ -0,0 +1,97 @@ +class Function(object): + """ + Function を表すクラス。 + 必要な情報を詰め込み、 to_pybind_string で生成する。 + """ + + def __init__(self): + self._return_type: str = "" + self._arguments: list[tuple[str, str]] = [] + self._name: str | None = None + self._full_name: str | None = None + self._namespace: list[str] = [] + self._description = "" + self._module: str | None = None + self._pyname: str | None = None + self._call_guards: list[str] = [] + + def set_name(self, name: str, namespace: list[str]): + self._name = name + self._namespace = namespace + self._full_name = f"{'::'.join(namespace)}::{name}" + + def set_return_type(self, type: str): + self._return_type = type + + def add_call_guard(self, call_guard: str): + self._call_guards.append(call_guard) + + def set_argument_types(self, types: list[tuple[str, str]]): + """ + parameter: [(name, type),] + """ + self._arguments = types + + @property + def pyname(self): + self._pyname + + @pyname.setter + def pyname(self, python_name: str): + self._pyname = python_name + + def add_argument_type(self, type: tuple[str, str]): + """ + parameter: (name, type) + """ + self._arguments.append(type) + + def set_description(self, description: str): + self._description = description + + def set_module(self, module: str): + self._module = module + + def to_pybind_string(self, overloaded=False): + if self._name == None or self._full_name == None or self._module == None: + print("Parse Error Skipping ...") + return "" + args = [f', pybind11::arg("{i[0]}")' for i in self._arguments] + self._pyname = self._pyname or self._name + if overloaded: + return ( + f'{self._module}.def("{self._pyname}", ' + f'static_cast<{self._return_type} (*)({", ".join([i[1] for i in self._arguments])})>' + f'(&{self._full_name}), "{self._description}"' + f"""{"".join(args)}{f", pybind11::call_guard<{', '.join(self._call_guards)}>()" if len(self._call_guards) > 0 else ""});""" + ) + else: + return ( + f'{self._module}.def("{self._pyname}", &{self._full_name}, "{self._description}"' + f"""{"".join(args)}{f", pybind11::call_guard<{', '.join(self._call_guards)}>()" if len(self._call_guards) > 0 else ""});""" + ) + + def to_decl_string(self): + if self._name == None or self._full_name == None or self._module == None: + print("Parse Error Skipping ...") + return "" + args = [f"{i[1]}" for i in self._arguments] + return ( + f'namespace {"::".join(self._namespace)} ' + f'{{ {self._return_type} {self._name}({", ".join(args)}); }}' + ) + + def signature(self, with_return_type=True) -> str: + args = [f"{i[1]}" for i in self._arguments] + if with_return_type: + return f'{"::".join(self._namespace)}::{self._name}({", ".join(args)}) -> {self._return_type}' + else: + return f'{"::".join(self._namespace)}::{self._name}({", ".join(args)})' + + def __eq__(self, obj): + if isinstance(obj, Function): + return self.signature(with_return_type=False) == obj.signature( + with_return_type=False + ) + else: + return False diff --git a/cppygen/submodule.py b/cppygen/submodule.py new file mode 100644 index 0000000..18155a3 --- /dev/null +++ b/cppygen/submodule.py @@ -0,0 +1,47 @@ +from typing import Dict, List, Tuple, TypedDict + + +class Submodule: + """ + Represent Submodule. + """ + + def __init__(self): + self._name: str | None = None + self._description = "" + self._parents: List[str] = [] + + @property + def cpp_name(self) -> str: + if self._name == None: + print("Parse Error Skipping ...") + return "" + return "_".join(self._parents) + "_" + self._name + + @property + def cpp_parent_name(self) -> str: + if self._name == None: + print("Parse Error Skipping ...") + return "" + return "_".join(self._parents) + + def set_name(self, name: str): + self._name = name + + def set_description(self, description: str): + self._description = description + + def set_parent(self, parents: List[str]): + self._parents = parents + + def to_pybind_string(self): + if self._name == None: + print("Parse Error Skipping ...") + return "" + return f'auto {self.cpp_name} = {self.cpp_parent_name}.def_submodule("{self._name}", "{self._description}");' + + def __eq__(self, obj): + if isinstance(obj, Submodule): + return self.cpp_name == obj.cpp_name + else: + return False diff --git a/cppygen/template_class.py b/cppygen/template_class.py new file mode 100644 index 0000000..e4687a4 --- /dev/null +++ b/cppygen/template_class.py @@ -0,0 +1,41 @@ +from typing import TypedDict + + +class TemplateClass: + class MemberFunctionSignature(TypedDict): + name: str + pyname: str + return_type: str + args: list[tuple[str, str]] + description: str + call_guards: list[str] + + def __init__(self): + self._name: str | None = None + self._base_classes: list[tuple[str, bool]] = [] + self._namespace: list[str] = [] + self._members: list[dict[str, str]] = [] + self._member_funcs: list[TemplateClass.MemberFunctionSignature] = [] + self._module: str | None = None + self._description = "" + + def set_name(self, name: str, namespace: list[str]): + self._name = name + self._namespace = namespace + self._full_name = f"{'::'.join(namespace)}::{name}" + + def add_member( + self, + name: str, + type: str, + description: str = "", + private: bool = False, + ): + if not private: + self._members.append( + { + "name": name, + "type": type, + "description": description, + } + ) diff --git a/example/header_mode/CMakeLists.txt b/example/header_mode/CMakeLists.txt index 033dcc0..165b553 100644 --- a/example/header_mode/CMakeLists.txt +++ b/example/header_mode/CMakeLists.txt @@ -47,6 +47,12 @@ set_target_properties( CXX_VISIBILITY_PRESET "hidden" VISIBILITY_INLINES_HIDDEN ON) +add_custom_command( + TARGET pyshell + POST_BUILD + COMMAND ${CMAKE_CURRENT_LIST_DIR}/stubgen.sh + COMMENT "Generating pyshell stub file") + add_custom_command( OUTPUT ${cppygen_generated_hpp} ${cppygen_generated_cpp} COMMAND diff --git a/example/header_mode/Taskfile.yml b/example/header_mode/Taskfile.yml index 6f17cf8..ec73524 100644 --- a/example/header_mode/Taskfile.yml +++ b/example/header_mode/Taskfile.yml @@ -14,4 +14,4 @@ tasks: - env "CXX=/usr/local/opt/llvm/bin/clang++" cmake -B build -S . -GNinja compile: cmds: - - cmake --build build + - rm -rf ./build/cppygen_generated.cpp && cmake --build build diff --git a/example/header_mode/python/test.py b/example/header_mode/python/test.py new file mode 100644 index 0000000..1fbf896 --- /dev/null +++ b/example/header_mode/python/test.py @@ -0,0 +1,32 @@ +import sys + +sys.path.append("../build") + +import pyshell +import pyshell.impl + + +def test(): + pyshell.start() + + print(f"# Simple Function {pyshell.add(3, 5) = }") + + pyshell.impl.hey() + + ####################################################################### + # Simple Class + ####################################################################### + person = pyshell.Person() + person.hey() + person.say() + person.name = "John" + person.age = 36 + person.say() + + john = pyshell.John() + john.hey() + john.say() + + +if __name__ == "__main__": + test() diff --git a/example/header_mode/shell/hoge.cpp b/example/header_mode/shell/hoge.cpp index 586adfb..1e9281f 100644 --- a/example/header_mode/shell/hoge.cpp +++ b/example/header_mode/shell/hoge.cpp @@ -4,42 +4,14 @@ namespace Shell { -void start(std::string arg = "", std::string tty = "none") { - std::cout << "This is dummy start function with arg = " << arg << std::endl; - std::cout << "tty = " << tty << std::endl; -} +void start() { std::cout << "Example Start" << std::endl; } -// このようなユーザー定義のクラスを用いている場合は -// PyGen はデフォルトでは Header -// の場所を探しきれず、ただしく、これがユーザー定義 -// のクラスを返す変数であることを認識できないので、 flag を渡して -// Header の位置を教えてあげる必要がある。 -Foo make_foo() { return Foo(1, 2); } +double add(int a, int b) { return a + b; } -void hoge() { std::cout << "Hello" << std::endl; } +namespace impl { -void hoge(char) { std::cout << "Hello" << std::endl; } +void hello() { std::cout << "Hello" << std::endl; } -void fuga() { std::cout << "Hello Fuga" << std::endl; } - -int add(int a, int b) { return a + b; } - -int sub(int a, int b) { return a - b; } - -namespace hogehoge { - -/// Piyopiyo!!! -namespace piyo { - -/// Comment!!! -int add_piyo(int x, int y) { return x + y; } - -int add_(int x, int y) { return x + y; } - -} // namespace piyo - -} // namespace hogehoge - -std::vector return_vector(std::vector a) { return a; } +} // namespace Impl } // namespace Shell diff --git a/example/header_mode/shell/hoge.hpp b/example/header_mode/shell/hoge.hpp index ccc68a1..144aefe 100644 --- a/example/header_mode/shell/hoge.hpp +++ b/example/header_mode/shell/hoge.hpp @@ -1,29 +1,72 @@ #pragma once #include +#include #include namespace Shell { -void hoge(); +/****************************************************************************** + * Simple Test + ******************************************************************************/ +void start(); -void hoge(char); +// define simple function +double add(int a, int b); -void fuga(); +// submodule +namespace impl { +/** + * pyname: hey + * description: this function was declared "hello" in C++. + */ +void hello(); -int add(int a, int b); +} // namespace impl -int sub(int a, int b); +/****************************************************************************** + * Simple Class and Inheritence + ******************************************************************************/ +class Person { +public: + std::string name{"no_name"}; + int age{0}; + + void hey() { std::cout << "Hello" << std::endl; } + virtual void say() { + std::cout << "I am " << name << " age is " << age << std::endl; + } -template class FooBase0 { + // Virtual Class should declare virtual destructor + virtual ~Person() = default; +}; + +// Base class must be global namespace +class John : public ::Shell::Person { public: - int some_val = 0; + John() { + this->name = "John"; + this->age = 23; + } + + void say() { + std::cout << "Hello! I am " << name << " age is " << age << std::endl; + } }; -class Foo : public ::Shell::FooBase0 { +template struct FooBase { public: - Foo() = default; // pybind11 から見えるのは この default コンストラクターのみ - Foo(int a, int b) : a(a), b(b) {} + foo th() { + std::cout << "Foo" << std::endl; + return foo(); + } + int some_val = 0; +}; +// You shold declare BaseClass as global namespace +class Foo : public ::Shell::FooBase { +public: + Foo() = default; // pybind11 から見えるのは この defaultコンストラクターのみ + // Foo(int a, int b) : a(a), b(b) {} struct InnerBar { void barbar() { std::cout << "Hoge" << std::endl; } @@ -36,4 +79,6 @@ class Foo : public ::Shell::FooBase0 { int b = 42; }; +class Foo2 : public ::Shell::FooBase {}; + } // namespace Shell diff --git a/example/header_mode/shell/piyo.cpp b/example/header_mode/shell/piyo.cpp deleted file mode 100644 index ef0d9f8..0000000 --- a/example/header_mode/shell/piyo.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "piyo.hpp" -#include "hoge.hpp" -#include - -namespace Shell::piyo { - -Piyoyo make_piyoyo() { - Piyoyo tmp; - tmp.setValue(-5); - return tmp; -} -std::vector fugafuga() { return {3, 4, 5, 6}; } - -} // namespace Shell::piyo diff --git a/example/header_mode/shell/piyo.hpp b/example/header_mode/shell/piyo.hpp deleted file mode 100644 index a0ddb86..0000000 --- a/example/header_mode/shell/piyo.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -namespace Shell { -struct Piyoyo { - int value; - int getValue() { return value; } - void setValue(int val) { value = val; } -}; - -std::vector fugafuga(); - - -} // namespace Shell diff --git a/example/header_mode/stubgen.sh b/example/header_mode/stubgen.sh index a52f3fd..3be6824 100755 --- a/example/header_mode/stubgen.sh +++ b/example/header_mode/stubgen.sh @@ -1,4 +1,6 @@ #!/bin/bash -# このコマンドで stub を作る -cd ./build && stubgen -p pyshell -o ../python/stubs +SCRIPT_DIR=$(dirname "$(realpath "$0")") + +# Generate python stubs +cd $SCRIPT_DIR/build && stubgen -p pyshell -o $SCRIPT_DIR/python/stubs diff --git a/tests/test_components.py b/tests/test_components.py index b7b64f9..dfdd242 100644 --- a/tests/test_components.py +++ b/tests/test_components.py @@ -1,4 +1,5 @@ -from cppygen.component import Function, StructOrClass, Submodule +from cppygen.function import Function +from cppygen.submodule import Submodule def test_function():