generated from 42cursus-youkim/project-template
-
Notifications
You must be signed in to change notification settings - Fork 1
/
container_of.py
executable file
·136 lines (103 loc) · 3.24 KB
/
container_of.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env python3
"""
container_of.py:
creates boilerplate for variable argument function "container_of()".
container_of generates anonymous container, with maximum <size> of arguments.
default writes stdout, provide <path> to write to file.
example usage - "container_of<vector<string> >("a", "b", "c)"
usage:
container_of.py [--help] [options] [ <path> ]
options:
-h, --help show this help message and exit
-n <N>, --size <N> number of maximum argument size. [default: 10]
"""
import shutil
from dataclasses import dataclass
from functools import cached_property
from pathlib import Path
from textwrap import dedent
from cpputil import clang_format
from create import wrap_header
template = dedent(
"""\
// do not try to directly edit this file.
// generate using container_of.py instead
#define MAP_OF(K, V, param) container_of<std::map<K, V> > param
#define VEC_OF(T, param) container_of<std::vector<T> > param
#define V(param) VEC_OF(std::string, param)
#define T typename C::value_type
namespace util {{
template <typename K, typename V>
std::pair<K, V> p(K k, V v) {{
return std::make_pair(k, v);
}}
}} // namespace util
// hardcoded size 0 case
template <typename C>
inline C container_of() {{
return C();
}}
// end of hardcoded size 0 cases
{text}
#undef T
"""
)
@dataclass
class Template:
"""
size must be greater than 0
typename C <- container type
typename T <- value type, expands to typename C::value_type
"""
size: int = 10
def create_args(self, *, with_type: bool = False) -> str:
arg = "T arg{n}" if with_type else "arg{n}"
return ", ".join(arg.format(n=n) for n in range(self.size))
def create_args_array(self) -> str:
return f"const T args[{self.size}] = {{{self.create_args()}}};"
def create_return(self) -> str:
return f"""return C({f"args, args + {self.size}"});"""
def __str__(self) -> str:
return dedent(
f"""\
template <typename C>
inline C container_of({self.create_args(with_type=True)}) {{
{self.create_args_array()}
{self.create_return()}
}}
"""
)
@dataclass
class ContainerOfArgs:
size: int = 10
@cached_property
def as_text(self) -> str:
text = "\n".join(str(Template(n)) for n in range(1, self.size + 1))
return template.format(
text=text,
)
def __repr__(self) -> str:
return self.as_text
def create_result(size: int, path: Path | None) -> str:
text = ContainerOfArgs(size).as_text
if path:
text = wrap_header(text, path)
if size < 100 and shutil.which("clang-format"):
text = clang_format(text)
else:
text = f"// clang-format: off\n{text}"
return text + "\n"
def main():
from docopt import docopt
assert __doc__ is not None
args: dict[str, str] = docopt(__doc__)
# print(args)
size = int(args["--size"])
path = Path(args["<path>"]) if args["<path>"] else None
text = create_result(size, path)
if path:
path.write_text(text)
else:
print(text)
if __name__ == "__main__":
main()