Peano
Loading...
Searching...
No Matches
DataModel.py
Go to the documentation of this file.
1# This file is part of the DaStGen2 project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3import dastgen2
4import os
5
6
7
8class DataModel(object):
9 """! Represents on DaStGen2 object, i.e. one data model
10
11 A data model has attributes and it is tied to aspects which inject some
12 technical behaviour that is cross-cutting.
13
14 """
15
17 self,
18 full_qualified_name,
19 has_nondefault_constructor=True,
20 embed_attributes_into_data_store=False,
21 ):
22 """!
23
24 Construct the data model
25
26
27 ## Attributes
28
29 public_fields: C++ string
30 This will be used to add public fields to the class, and each
31 attribute can feed into this string. However, you can also
32 modify it yourself.
33
34 ## Arguments
35
36 @param full_qualified_name Full qualified name
37 Please pass in in C++ convention i.e. with :: separating the namespaces.
38
39 """
40 self._full_qualified_name = full_qualified_name
41 self._attributes = []
42 self._aspects = []
43 self.has_nondefault_constructor = has_nondefault_constructor
44 self._embed_attributes_into_data_store = embed_attributes_into_data_store
45 self.public_fields = ""
47
48 def set_full_qualified_name(self, full_qualified_name):
49 self._full_qualified_name = full_qualified_name
50
51 def add_attribute(self, attribute):
52 attribute.use_data_store = self._embed_attributes_into_data_store
53 self._attributes.append(attribute)
54
55 def add_or_replace_attribute(self, attribute):
56 """
57 Check first whether attribute already exists. If it does, replace it.
58 """
59
60 attribute.use_data_store = self._embed_attributes_into_data_store
61
62 for i, att in enumerate(self._attributes):
63 if att._name == attribute._name:
64 self._attributes[i] = attribute
65 return
66
67 # if we make it here, it's a new attribute.
68 self._attributes.append(attribute)
69 return
70
71 def add_aspect(self, aspect):
72 """!
73
74 Add a new aspect
75
76 This is a pretty simplistic routine:
77
78 1. Inform the aspect object about the model by invoking set_model()
79 2. Add the aspect to the list of used aspects
80
81 """
82 aspect.set_model(self)
83 self._aspects.append(aspect)
84
85 _Header_Template_Without_Data_Store = """
86//
87// Generated by DaStGen2 (C) 2020 Tobias Weinzierl
88//
89// For DaStGen's copyright, visit www.peano-framework.org. These generated files
90// however are not subject of copyright, i.e. feel free to add your copyright in
91// here
92//
93#pragma once
94
95#include <string>
96
97{ATTRIBUTE_INCLUDES}
98{ASPECT_INCLUDES}
99{ADDITIONAL_INCLUDES}
100
101{OPEN_NAMESPACES}
102 struct {UNQUALIFIED_CLASS_NAME};
103{CLOSE_NAMESPACES}
104
105
106struct {FULL_QUALIFIED_CLASS_NAME} {{
107 public:
108{PUBLIC_FIELDS}
109
110 {UNQUALIFIED_CLASS_NAME}() {{}}
111 {UNQUALIFIED_CLASS_NAME}{CONSTRUCTOR_ARGUMENTS};
112
113
114{METHOD_DECLARATIONS}
115
116{ASPECT_METHOD_DECLARATIONS}
117 std::string toString() const;
118
119 private:
120{ATTRIBUTE_DECLARATIONS}
121
122{ASPECT_ATTRIBUTES}
123
124}};
125
126 """
127
128 _Header_Template_With_Data_Store = """
129//
130// Generated by DaStGen2 (C) 2020 Tobias Weinzierl
131//
132// For DaStGen's copyright, visit www.peano-framework.org. These generated files
133// however are not subject of copyright, i.e. feel free to add your copyright in
134// here
135//
136#pragma once
137
138#include <string>
139
140{ASPECT_INCLUDES}
141
142{OPEN_NAMESPACES}
143 struct {UNQUALIFIED_CLASS_NAME};
144{CLOSE_NAMESPACES}
145
146
147struct {FULL_QUALIFIED_CLASS_NAME} {{
148 public:
149{PUBLIC_FIELDS}
150
151 {UNQUALIFIED_CLASS_NAME}() {{}}
152 {UNQUALIFIED_CLASS_NAME}{CONSTRUCTOR_ARGUMENTS};
153
154{METHOD_DECLARATIONS}
155
156{ASPECT_METHOD_DECLARATIONS}
157 std::string toString() const;
158
159 private:
160 struct DataStore {{
161{ATTRIBUTE_DECLARATIONS}
162 }};
163
164{ASPECT_ATTRIBUTES}
165
166 DataStore _dataStore;
167}};
168
169 """
170
172 """
173 Generates the list of constructor arguments needed to fully
174 instantiate an object of the class the datamodel generates.
175 """
176 result = "("
177 is_first = True
178 for attribute in self._attributes:
179 if (
180 attribute._is_static
181 or attribute._is_const_static
182 or attribute._is_constexpr
183 or attribute._is_const
184 ):
185 continue
186 for entry in attribute.get_constructor_arguments():
187 if not is_first:
188 result += ", "
189 is_first = False
190 result += entry[1] + " _" + entry[0]
191 result += ")"
192 return result
193
195 use_default_constructor = True
196 for attribute in self._attributes:
197 use_default_constructor = (
198 use_default_constructor and attribute.use_default_copy_constructor()
199 )
200 return use_default_constructor
201
202 def write_header_file(self, full_qualified_filename):
203 """!
204 writes the header file for your data model.
205 """
206 d = {}
207 d["ATTRIBUTE_INCLUDES"] = ""
208 for attribute in self._attributes:
209 d["ATTRIBUTE_INCLUDES"] += attribute.get_includes()
210
211 d["UNQUALIFIED_CLASS_NAME"] = dastgen2.get_unqualified_class_name(
213 )
214 d["FULL_QUALIFIED_CLASS_NAME"] = self._full_qualified_name
215 d["OPEN_NAMESPACES"] = ""
216 d["CLOSE_NAMESPACES"] = ""
217 d["ADDITIONAL_INCLUDES"] = self.additional_includes
218 d["SETTER_GETTER_DECLARATIONS"] = ""
219 for i in dastgen2.get_namespaces(self._full_qualified_name):
220 d["OPEN_NAMESPACES"] += "namespace " + i + "{\n"
221 d["CLOSE_NAMESPACES"] += "}\n"
222
223 d["PUBLIC_FIELDS"] = self.public_fields
224 for attribute in self._attributes:
225 d["PUBLIC_FIELDS"] += attribute.get_public_fields()
226
227 d["ATTRIBUTE_DECLARATIONS"] = ""
228 for attribute in self._attributes:
229 d["ATTRIBUTE_DECLARATIONS"] += attribute.get_attribute_declaration_string()
230
231 d["METHOD_DECLARATIONS"] = ""
232 for attribute in self._attributes:
233 if attribute.ifdefs != []:
234 d["METHOD_DECLARATIONS"] += dastgen2.construct_ifdef_string(
235 attribute.ifdefs
236 )
237 for method in attribute.get_methods(
238 self._full_qualified_name, for_declaration=True
239 ):
240 d["METHOD_DECLARATIONS"] += " "
241 if attribute.expose_in_header_file:
242 d["METHOD_DECLARATIONS"] += " inline "
243 d["METHOD_DECLARATIONS"] += method[1] + " " + method[0] + ";\n"
244 if attribute.ifdefs != []:
245 d["METHOD_DECLARATIONS"] += "#endif \n"
246
247 d["ASPECT_ATTRIBUTES"] = ""
248 d["ASPECT_METHOD_DECLARATIONS"] = ""
249 # Inlcudes to peano4 should not exist in tarch
250 # As tarch do not uses the definition Dimensions, but peano4 needs it (2 or 3 for example)
251 d["ASPECT_INCLUDES"] = ""
252 for i in self._aspects:
253
254 d["ASPECT_ATTRIBUTES"] += i.get_attributes() + "\n"
255 d["ASPECT_METHOD_DECLARATIONS"] += (
256 i.get_method_declarations(self._full_qualified_name) + "\n"
257 )
258 include = i.get_include()
259 # Do not include peano4 aspects in tarch files, due to the dimensions definitions
260 filtered_includes = ""
261 for line in include.split("\n"):
262 if not("tarch" in d["FULL_QUALIFIED_CLASS_NAME"] and line.startswith('#include "peano4')):
263 filtered_includes += line + "\n"
264
265 d["ASPECT_INCLUDES"] += filtered_includes+ "\n"
266 d["CONSTRUCTOR_ARGUMENTS"] = self._get_constructor_arguments()
268 d["METHOD_DECLARATIONS"] += (
269 " "
270 + dastgen2.get_unqualified_class_name(self._full_qualified_name)
271 + "(const "
272 + dastgen2.get_unqualified_class_name(self._full_qualified_name)
273 + "& copy) = default;"
274 )
275 else:
276 d["METHOD_DECLARATIONS"] += (
277 " "
278 + dastgen2.get_unqualified_class_name(self._full_qualified_name)
279 + "(const "
280 + dastgen2.get_unqualified_class_name(self._full_qualified_name)
281 + "& copy);"
282 )
283
284 if full_qualified_filename.find("/") != -1:
285 path = full_qualified_filename[0 : full_qualified_filename.rfind("/")]
286 if not os.path.exists(path):
287 os.mkdir(path)
288 with open(full_qualified_filename, "w") as output:
290 output.write(self._Header_Template_With_Data_Store.format(**d))
291 else:
292 output.write(self._Header_Template_Without_Data_Store.format(**d))
293
294 self.__generate_accessors(output, False)
295 output.write("\n\n\n")
296
298 """
299
300 In this generation business, I faced multiple issues with initialisation
301 lists: where are comma if some fields are not defined, which fields can
302 I copy directly, and which ones do I have to copy manually, and so forth.
303
304
305 """
306 output.write(
308 + "::"
309 + dastgen2.get_unqualified_class_name(self._full_qualified_name)
311 )
312
313 output.write("{\n")
314 for attribute in self._attributes:
315 if (
316 attribute._is_static
317 or attribute._is_const_static
318 or attribute._is_constexpr
319 or attribute._is_const
320 ):
321 # no need to copy static class fields.
322 continue
323
324 if attribute.ifdefs != []:
325 output.write(dastgen2.construct_ifdef_string(attribute.ifdefs))
326 output.write(
327 "set"
328 + attribute._name[0].title()
329 + attribute._name[1:].split("[")[0]
330 + "( __"
331 + attribute._name.split("[")[0]
332 + ");\n"
333 )
334 if attribute.ifdefs != []:
335 output.write("#endif \n")
336 output.write("}\n")
337
339 output.write(
340 "std::string " + self._full_qualified_name + "::toString() const {\n"
341 )
342 output.write(" std::ostringstream out;\n")
343 output.write(' out << "(";\n')
344 for attribute in self._attributes:
345 if attribute.ifdefs != []:
346 output.write(dastgen2.construct_ifdef_string(attribute.ifdefs))
347 if self._attributes.index(attribute) != 0:
348 output.write(""" out << ","; \n""")
349 output.write(
350 ' out << "'
351 + attribute._name
352 + '=" << '
353 + attribute.get_to_string()
354 + ";\n"
355 )
356 if attribute.ifdefs != []:
357 output.write("#endif \n")
358
359 output.write(' out << ")";\n')
360 output.write(" return out.str();\n")
361 output.write("}\n\n\n")
362
364 output.write(
366 + "::"
367 + dastgen2.get_unqualified_class_name(self._full_qualified_name)
368 + "( const "
369 + dastgen2.get_unqualified_class_name(self._full_qualified_name)
370 + "& copy ) {\n"
371 )
372 for attribute in self._attributes:
373 if (
374 attribute._is_static
375 or attribute._is_const_static
376 or attribute._is_constexpr
377 or attribute._is_const
378 ):
379 # no need to copy static class fields.
380 continue
381 if attribute.ifdefs != []:
382 output.write(dastgen2.construct_ifdef_string(attribute.ifdefs))
383 uppercase_name = attribute.name[0].upper() + attribute.name[1:]
384 output.write(
385 " set" + uppercase_name + "( copy.get" + uppercase_name + "() );\n"
386 )
387 if attribute.ifdefs != []:
388 output.write("#endif \n")
389 output.write("}\n\n\n")
390
391 def __generate_accessors(self, output, is_cpp_file):
392 """!
393
394 Generate getter/setter functions.
395
396 Generate all the setters and getters and use get_methods() to
397 construct them for the individual attributes.
398
399 output: File object
400 File object that will be written to.
401
402 is_cpp_file: Boolean.
403 True if the output is intended to be the .cpp file. False if it's
404 the header file.
405
406 """
407 for attribute in self._attributes:
408 if attribute.expose_in_header_file != is_cpp_file:
409 if attribute.ifdefs != []:
410 output.write(dastgen2.construct_ifdef_string(attribute.ifdefs))
411 for method in attribute.get_methods(
412 self._full_qualified_name, for_declaration=False
413 ):
414 output.write(
415 method[1]
416 + " "
418 + "::"
419 + method[0]
420 + " {\n"
421 )
422 output.write(attribute.get_method_body(method[0]))
423 output.write("}\n\n\n")
424 if attribute.ifdefs != []:
425 output.write("#endif \n\n\n")
426
427 def write_implementation_file(self, full_qualified_filename):
428 """!
429 writes the implementation (.cpp) file for your data model.
430 """
431 with open(full_qualified_filename, "w") as output:
432 output.write(
433 '#include "'
434 + dastgen2.get_unqualified_class_name(self._full_qualified_name)
435 + '.h"\n\n\n'
436 )
437
438 output.write(
439 """
440#include <sstream>
441#include <algorithm>
442
443
444
445"""
446 )
447
448 # inserting include statements that the compiler will not accept in the header file
449 for aspect in self._aspects:
450 if hasattr(aspect, "implementation_file_includes"):
451 output.write( aspect.implementation_file_includes )
452
455 output.write("\n\n\n")
456
457 if not self.__use_default_copy_constructor():
459 output.write("\n\n\n")
460
462 output.write("\n\n\n")
463
464 self.__generate_accessors(output, True)
465 output.write("\n\n\n")
466
467 for aspect in self._aspects:
468 output.write(aspect.get_implementation(self._full_qualified_name))
469
471 for i in self._attributes:
472 i.expose_in_header_file = True
473
474
475 @property
477 return self._full_qualified_name
478
479
480 @property
481 def name(self):
482 return self._full_qualified_name.split( "::" )[-1]
483
Represents on DaStGen2 object, i.e.
Definition DataModel.py:8
write_implementation_file(self, full_qualified_filename)
writes the implementation (.cpp) file for your data model.
Definition DataModel.py:427
add_aspect(self, aspect)
Add a new aspect.
Definition DataModel.py:71
_get_constructor_arguments(self)
Generates the list of constructor arguments needed to fully instantiate an object of the class the da...
Definition DataModel.py:171
__generate_constructor_definition(self, output)
In this generation business, I faced multiple issues with initialisation lists: where are comma if so...
Definition DataModel.py:297
__generate_accessors(self, output, is_cpp_file)
Generate getter/setter functions.
Definition DataModel.py:391
add_attribute(self, attribute)
Definition DataModel.py:51
write_header_file(self, full_qualified_filename)
writes the header file for your data model.
Definition DataModel.py:202
__generate_toString_definition(self, output)
Definition DataModel.py:338
__init__(self, full_qualified_name, has_nondefault_constructor=True, embed_attributes_into_data_store=False)
Construct the data model.
Definition DataModel.py:21
set_full_qualified_name(self, full_qualified_name)
Definition DataModel.py:48
add_or_replace_attribute(self, attribute)
Check first whether attribute already exists.
Definition DataModel.py:55
__generate_explicit_copy_constructor(self, output)
Definition DataModel.py:363