Skip to content

Commit dd68f7e

Browse files
authored
Merge pull request #84 from bigladder/add-json-object-write
Add to_json boilerplate functions, recursive references.
2 parents d8b1e43 + c810cf9 commit dd68f7e

File tree

5 files changed

+81
-9
lines changed

5 files changed

+81
-9
lines changed

lattice/cpp/cpp_entries.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
FunctionalHeaderEntry,
1515
HeaderEntry,
1616
InlineDependency,
17+
ObjectDeserializationDeclaration,
1718
ObjectSerializationDeclaration,
1819
Struct,
1920
VirtualDestructor,
@@ -136,6 +137,17 @@ def __post_init__(self):
136137
self.trace()
137138

138139

140+
@dataclass
141+
class StructDeserialization(FunctionDefinition):
142+
_header_entry: Struct
143+
144+
def __post_init__(self):
145+
super(FunctionDefinition, self).__post_init__()
146+
self._func = f"void to_json(nlohmann::json& j, const {self._name}& x)"
147+
148+
self.trace()
149+
150+
139151
@dataclass
140152
class MemberFunctionDefinition(FunctionDefinition):
141153
_header_entry: FunctionalHeaderEntry
@@ -193,6 +205,30 @@ def __post_init__(self):
193205
self.trace()
194206

195207

208+
@dataclass
209+
class OwnedElementDeserialization(ElementSerialization):
210+
def __post_init__(self):
211+
super().__post_init__()
212+
self._funclines = [f'json_set<{self._type}>(j, "{self._name}", x.{self._name}, x.{self._name}_is_set);']
213+
self.trace()
214+
215+
216+
@dataclass
217+
class OwnedElementDownload(ElementSerialization):
218+
def __post_init__(self):
219+
super().__post_init__()
220+
self._funclines = []
221+
assert len(self._header_entry.selector) == 1 # only one switchable data element per entry
222+
data_element = next(iter(self._header_entry.selector))
223+
for enum in self._header_entry.selector[data_element]:
224+
self._funclines += [
225+
f"if (x.{data_element} == {enum}) {{",
226+
f'\tjson_set<{self._header_entry.selector[data_element][enum]}>(j, "{self._name}", *dynamic_cast<const {self._header_entry.selector[data_element][enum]}*>(x.{self._name}.get()), x.{self._name}_is_set);', # noqa: E501
227+
"}",
228+
]
229+
self.trace()
230+
231+
196232
@dataclass
197233
class ClassFactoryCreation(ElementSerialization):
198234
def __post_init__(self):
@@ -268,14 +304,14 @@ def _translate(self, container_class_name, header_tree):
268304

269305
self._get_items_to_serialize(header_tree.root)
270306

271-
def _get_items_to_serialize(self, header_tree: HeaderEntry):
307+
def _get_items_to_serialize(self, header_tree: HeaderEntry): # noqa: PLR0912 too-many-branches
272308
for h_entry in header_tree.child_entries:
273309
cpp_entry: Optional[ImplementationEntry] = None
274310
logger.debug(f"Header entry being processed: {h_entry.name} under {h_entry.parent.name}")
275311
if isinstance(h_entry, Struct) and len([c for c in h_entry.child_entries if isinstance(c, DataElement)]):
276312
cpp_entry = StructSerialization(
277313
h_entry, self._namespace
278-
) # Create the "from_json" function definition (header), only if it won't be empty
314+
) # Create the "from_json/"to_json"" function definition, only if they won't be empty
279315

280316
for data_element_entry in [c for c in h_entry.child_entries if isinstance(c, DataElement)]:
281317
# In function body, create each "get_to" for individual data elements
@@ -284,6 +320,14 @@ def _get_items_to_serialize(self, header_tree: HeaderEntry):
284320
else:
285321
c = OwnedElementSerialization(data_element_entry, cpp_entry)
286322

323+
cpp_entry = StructDeserialization(h_entry, self._namespace)
324+
325+
for data_element_entry in [c for c in h_entry.child_entries if isinstance(c, DataElement)]:
326+
if "unique_ptr" in data_element_entry.type:
327+
c = OwnedElementDownload(data_element_entry, cpp_entry)
328+
else:
329+
c = OwnedElementDeserialization(data_element_entry, cpp_entry)
330+
287331
elif isinstance(h_entry, DataElementStaticMetainfo):
288332
logger.debug(f"{h_entry.name} under {type(h_entry.parent)} under {self._namespace._name}")
289333
cpp_entry = DataElementStaticInitialization(h_entry, self._namespace)
@@ -295,6 +339,7 @@ def _get_items_to_serialize(self, header_tree: HeaderEntry):
295339
elif (
296340
isinstance(h_entry, FunctionalHeaderEntry)
297341
and not isinstance(h_entry, ObjectSerializationDeclaration)
342+
and not isinstance(h_entry, ObjectDeserializationDeclaration)
298343
and not isinstance(h_entry, VirtualDestructor)
299344
):
300345
cpp_entry = MemberFunctionDefinition(

lattice/cpp/header_entries.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,6 @@ def _get_scoped_inner_type(self, type_str: str) -> str:
219219
# Look through the references to assign a scope to the type
220220
for custom_type in self._referenced_datatypes:
221221
if inner_type == custom_type.name:
222-
print(f"{custom_type.namespace}", inner_type)
223222
self.scoped_innertype = (f"{custom_type.namespace}", inner_type)
224223
# namespace naming convention is snake_style, regardless of the schema file name
225224
return "::".join(self.scoped_innertype)
@@ -372,6 +371,20 @@ def __post_init__(self):
372371
self.trace()
373372

374373

374+
@dataclass
375+
class ObjectDeserializationDeclaration(FunctionalHeaderEntry):
376+
_f_ret: str = field(init=False)
377+
_f_name: str = field(init=False)
378+
_f_args: list[str] = field(init=False)
379+
380+
def __post_init__(self):
381+
self._f_ret = "void"
382+
self._f_name = "to_json"
383+
self._f_args = ["nlohmann::json& j", f"const {self.name}& x"]
384+
super().__post_init__()
385+
self.trace()
386+
387+
375388
@dataclass
376389
class VirtualDestructor(FunctionalHeaderEntry):
377390
_explicit_definition: Optional[str] = None

lattice/cpp/header_translator.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class HeaderTranslator:
5353
def __init__(
5454
self,
5555
input_file_path: Path,
56-
forward_declarations_path: Path,
56+
forward_declarations_path: Optional[Path],
5757
output_path: Path,
5858
top_namespace: str,
5959
):
@@ -129,7 +129,7 @@ def _translate(self,
129129
"Description")
130130

131131
for base_level_tag in self._list_objects_of_type("Data Group Template"):
132-
predefined_code_dir: Path = self._forward_declaration_dir.parent / "cpp" / "base_classes"
132+
predefined_code_dir: Path = self._source_dir.parent / "cpp" / "base_classes"
133133
base_class_file = Path(predefined_code_dir / f"{hyphen_separated_lowercase_style(base_level_tag)}.h")
134134
# TODO: Check naming assumptions and rules for pre-defined base class files
135135
if not base_class_file.exists():
@@ -170,9 +170,10 @@ def _translate(self,
170170
self._namespace,
171171
self._contents[base_level_tag]["Enumerators"])
172172
for base_level_tag in self._list_objects_of_type(self._data_group_types):
173-
# from_json declarations are necessary in top container, as the header-declared
173+
# from_json/to_json declarations are necessary in top container, as the header-declared
174174
# objects might be included and used from elsewhere.
175175
ObjectSerializationDeclaration(base_level_tag, self._namespace)
176+
ObjectDeserializationDeclaration(base_level_tag, self._namespace)
176177

177178
def _reset_parsing(self):
178179
"""Clear member containers for a new translation."""
@@ -220,6 +221,8 @@ def _load_meta_info(self, schema_section):
220221
self._fundamental_data_types[base_item] = cpp_types[ext_dict[base_item]["JSON Schema Type"]]
221222
for base_item in [name for name in ext_dict if ext_dict[name]["Object Type"] == "String Type"]:
222223
self._fundamental_data_types[base_item] = "std::string"
224+
if reference_name not in [self._schema_name, "core"]:
225+
self._load_meta_info(ext_dict["Schema"]) # Recursively collect references, as C++ does
223226
# fmt: on
224227

225228
def _add_include_guard(self, header_name):

lattice/cpp/templates/load-object.h.j2

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,19 @@ namespace {{namespace}} {
7373
}
7474
}
7575
}
76+
77+
template<class T>
78+
void json_set(nlohmann::json &j,
79+
const char *subnode,
80+
const T& object,
81+
bool object_is_set
82+
)
83+
{
84+
if (object_is_set)
85+
{
86+
j[subnode] = object;
87+
}
88+
}
7689
}
7790

7891
#endif

lattice/lattice.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,7 @@ def cpp_support_headers(self) -> list[Path]:
260260
def generate_cpp_project(self):
261261
"""Generate CPP header files, source files, and build support files."""
262262
for schema in self.cpp_schemas:
263-
h = HeaderTranslator(
264-
schema.file_path, self.schema_directory_path, self._cpp_output_include_dir, self.root_directory.name
265-
)
263+
h = HeaderTranslator(schema.file_path, None, self._cpp_output_include_dir, self.root_directory.name)
266264
string_to_file(str(h), schema.cpp_header_file_path)
267265
c = CPPTranslator(self.root_directory.name, h)
268266
string_to_file(str(c), schema.cpp_source_file_path)

0 commit comments

Comments
 (0)