Add C++ API
[vpp.git] / src / vpp-api / vapi / vapi_cpp_gen.py
1 #!/usr/bin/env python3
2
3 import argparse
4 import os
5 import sys
6 import logging
7 from vapi_c_gen import CField, CStruct, CSimpleType, CStructType, CMessage, \
8     json_to_c_header_name
9 from vapi_json_parser import JsonParser
10
11
12 class CppField(CField):
13     def __init__(
14             self,
15             field_name,
16             field_type,
17             array_len=None,
18             nelem_field=None):
19         super().__init__(field_name, field_type, array_len, nelem_field)
20
21
22 class CppStruct(CStruct):
23     def __init__(self, name, fields):
24         super().__init__(name, fields)
25
26
27 class CppSimpleType (CSimpleType):
28
29     def __init__(self, name):
30         super().__init__(name)
31
32
33 class CppStructType (CStructType, CppStruct):
34     def __init__(self, definition, typedict, field_class):
35         super().__init__(definition, typedict, field_class)
36
37
38 class CppMessage (CMessage):
39     def __init__(self, logger, definition, typedict,
40                  struct_type_class, simple_type_class, field_class):
41         super().__init__(logger, definition, typedict, struct_type_class,
42                          simple_type_class, field_class)
43
44     def get_swap_to_be_template_instantiation(self):
45         return "\n".join([
46             "template <> inline void vapi_swap_to_be<%s>(%s *msg)" %
47             (self.get_c_name(), self.get_c_name()),
48             "{",
49             "  %s(msg);" % self.get_swap_to_be_func_name(),
50             "}",
51         ])
52
53     def get_swap_to_host_template_instantiation(self):
54         return "\n".join([
55             "template <> inline void vapi_swap_to_host<%s>(%s *msg)" %
56             (self.get_c_name(), self.get_c_name()),
57             "{",
58             "  %s(msg);" % self.get_swap_to_host_func_name(),
59             "}",
60         ])
61
62     def get_alloc_template_instantiation(self):
63         return "\n".join([
64             "template <> inline %s* vapi_alloc<%s%s>"
65             "(Connection &con%s)" %
66             (self.get_c_name(), self.get_c_name(),
67                 ", size_t" * len(self.get_alloc_vla_param_names()),
68                 "".join([", size_t %s" % n for n in
69                          self.get_alloc_vla_param_names()])
70              ),
71             "{",
72             "  %s* result = %s(con.vapi_ctx%s);" %
73             (self.get_c_name(), self.get_alloc_func_name(),
74                 "".join([", %s" % n
75                          for n in self.get_alloc_vla_param_names()])),
76             "#if VAPI_CPP_DEBUG_LEAKS",
77             "  con.on_shm_data_alloc(result);",
78             "#endif",
79             "  return result;",
80             "}",
81         ])
82
83     def get_cpp_name(self):
84         return "%s%s" % (self.name[0].upper(), self.name[1:])
85
86     def get_req_template_name(self):
87         if self.is_dump():
88             template = "Dump"
89         else:
90             template = "Request"
91
92         return "%s<%s, %s%s>" % (
93             template,
94             self.get_c_name(),
95             self.reply.get_c_name(),
96             "".join([", size_t"] * len(self.get_alloc_vla_param_names()))
97         )
98
99     def get_req_template_instantiation(self):
100         return "template class %s;" % self.get_req_template_name()
101
102     def get_type_alias(self):
103         return "using %s = %s;" % (
104             self.get_cpp_name(), self.get_req_template_name())
105
106     def get_reply_template_name(self):
107         return "Msg<%s>" % (self.get_c_name())
108
109     def get_reply_type_alias(self):
110         return "using %s = %s;" % (
111             self.get_cpp_name(), self.get_reply_template_name())
112
113     def get_msg_class_instantiation(self):
114         return "template class Msg<%s>;" % self.get_c_name()
115
116     def get_get_msg_id_t_instantiation(self):
117         return "\n".join([
118             ("template <> inline vapi_msg_id_t vapi_get_msg_id_t<%s>()"
119                 % self.get_c_name()),
120             "{",
121             "  return ::%s; " % self.get_msg_id_name(),
122             "}",
123             "",
124             ("template <> inline vapi_msg_id_t "
125              "vapi_get_msg_id_t<Msg<%s>>()" % self.get_c_name()),
126             "{",
127             "  return ::%s; " % self.get_msg_id_name(),
128             "}",
129         ])
130
131     def get_cpp_constructor(self):
132         return '\n'.join([
133             ('static void __attribute__((constructor)) '
134              '__vapi_cpp_constructor_%s()'
135              % self.name),
136             '{',
137             ('  vapi::vapi_msg_set_msg_id<%s>(%s);' % (
138                 self.get_c_name(), self.get_msg_id_name())),
139             '}',
140         ])
141
142
143 def gen_json_header(parser, logger, j, io, gen_h_prefix, add_debug_comments):
144     logger.info("Generating header `%s'" % io.name)
145     orig_stdout = sys.stdout
146     sys.stdout = io
147     include_guard = "__included_hpp_%s" % (
148         j.replace(".", "_").replace("/", "_").replace("-", "_"))
149     print("#ifndef %s" % include_guard)
150     print("#define %s" % include_guard)
151     print("")
152     print("#include <vapi/vapi.hpp>")
153     print("#include <%s%s>" % (gen_h_prefix, json_to_c_header_name(j)))
154     print("")
155     print("namespace vapi {")
156     print("")
157     for m in parser.messages_by_json[j].values():
158         # utility functions need to go first, otherwise internal instantiation
159         # causes headaches ...
160         if add_debug_comments:
161             print("/* m.get_swap_to_be_template_instantiation() */")
162         print("%s" % m.get_swap_to_be_template_instantiation())
163         print("")
164         if add_debug_comments:
165             print("/* m.get_swap_to_host_template_instantiation() */")
166         print("%s" % m.get_swap_to_host_template_instantiation())
167         print("")
168         if add_debug_comments:
169             print("/* m.get_get_msg_id_t_instantiation() */")
170         print("%s" % m.get_get_msg_id_t_instantiation())
171         print("")
172         if add_debug_comments:
173             print("/* m.get_cpp_constructor() */")
174         print("%s" % m.get_cpp_constructor())
175         print("")
176         if not m.is_reply():
177             if add_debug_comments:
178                 print("/* m.get_alloc_template_instantiation() */")
179             print("%s" % m.get_alloc_template_instantiation())
180             print("")
181         if add_debug_comments:
182             print("/* m.get_msg_class_instantiation() */")
183         print("%s" % m.get_msg_class_instantiation())
184         print("")
185         if m.is_reply():
186             if add_debug_comments:
187                 print("/* m.get_reply_type_alias() */")
188             print("%s" % m.get_reply_type_alias())
189             continue
190         if add_debug_comments:
191             print("/* m.get_req_template_instantiation() */")
192         print("%s" % m.get_req_template_instantiation())
193         print("")
194         if add_debug_comments:
195             print("/* m.get_type_alias() */")
196         print("%s" % m.get_type_alias())
197         print("")
198     print("}")  # namespace vapi
199
200     print("#endif")
201     sys.stdout = orig_stdout
202
203
204 def json_to_cpp_header_name(json_name):
205     if json_name.endswith(".json"):
206         return "%s.vapi.hpp" % os.path.splitext(json_name)[0]
207     raise Exception("Unexpected json name `%s'!" % json_name)
208
209
210 def gen_cpp_headers(parser, logger, prefix, gen_h_prefix,
211                     add_debug_comments=False):
212     if prefix == "" or prefix is None:
213         prefix = ""
214     else:
215         prefix = "%s/" % prefix
216     if gen_h_prefix is None:
217         gen_h_prefix = ""
218     else:
219         gen_h_prefix = "%s/" % gen_h_prefix
220     for j in parser.json_files:
221         with open('%s%s' % (prefix, json_to_cpp_header_name(j)), "w") as io:
222             gen_json_header(parser, logger, j, io,
223                             gen_h_prefix, add_debug_comments)
224
225
226 if __name__ == '__main__':
227     try:
228         verbose = int(os.getenv("V", 0))
229     except:
230         verbose = 0
231
232     if verbose >= 2:
233         log_level = 10
234     elif verbose == 1:
235         log_level = 20
236     else:
237         log_level = 40
238
239     logging.basicConfig(stream=sys.stdout, level=log_level)
240     logger = logging.getLogger("VAPI CPP GEN")
241     logger.setLevel(log_level)
242
243     argparser = argparse.ArgumentParser(description="VPP C++ API generator")
244     argparser.add_argument('files', metavar='api-file', action='append',
245                            type=str, help='json api file'
246                            '(may be specified multiple times)')
247     argparser.add_argument('--prefix', action='store', default=None,
248                            help='path prefix')
249     argparser.add_argument('--gen-h-prefix', action='store', default=None,
250                            help='generated C header prefix')
251     args = argparser.parse_args()
252
253     jsonparser = JsonParser(logger, args.files,
254                             simple_type_class=CppSimpleType,
255                             struct_type_class=CppStructType,
256                             field_class=CppField,
257                             message_class=CppMessage)
258
259     gen_cpp_headers(jsonparser, logger, args.prefix, args.gen_h_prefix)
260
261     for e in jsonparser.exceptions:
262         logger.error(e)