Add default LCP save of primitive types in optional
Reviewers: msantl, mtomic Reviewed By: msantl Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1579
This commit is contained in:
parent
5c69ae8a0c
commit
ee889b98fc
@ -672,14 +672,36 @@ list of pairs, e.g.
|
|||||||
|
|
||||||
#### Custom Serialization Helper Functions
|
#### Custom Serialization Helper Functions
|
||||||
|
|
||||||
**Helper for `std::optional`**
|
##### Helper for `std::optional`
|
||||||
|
|
||||||
|
When using `std::optional` with primitive C++ types or custom types known to
|
||||||
|
LCP, you do not need to use any helper. In the example below, things should be
|
||||||
|
serialized as expected:
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(lcp:define-class my-class-with-primitive-optional ()
|
||||||
|
((primitive-optional "std::experimental::optional<int64_t>"))
|
||||||
|
(:serialize :capnp))
|
||||||
|
|
||||||
|
(lcp:define-class my-class-with-known-type-optional ()
|
||||||
|
((known-type-optional "std::experimental::optional<MyClassWithPrimitiveOptional>"))
|
||||||
|
(:serialize :capnp))
|
||||||
|
```
|
||||||
|
|
||||||
In cases when the value contained in `std::optional` needs custom
|
In cases when the value contained in `std::optional` needs custom
|
||||||
serialization code you may use `lcp:capnp-save-optional` and
|
serialization code you may use `lcp:capnp-save-optional` and
|
||||||
`lcp:capnp-load-optional`.
|
`lcp:capnp-load-optional`.
|
||||||
|
|
||||||
Both functions expect 3 arguments: Cap'n Proto type in C++, type of the value
|
Both functions expect 3 arguments.
|
||||||
inside `std::optional` and an optional C++ lambda code.
|
|
||||||
|
1. Cap'n Proto type in C++.
|
||||||
|
2. C++ type of the value inside `std::optional`.
|
||||||
|
3. Optional C++ lambda code.
|
||||||
|
|
||||||
|
The lambda code is optional, because LCP will generate the default
|
||||||
|
serialization code which invokes `Save` and `Load` function on the value
|
||||||
|
stored inside the optional. Since most of the serialized classes follow the
|
||||||
|
convention, you will rarely need to provide this 3rd argument.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@ -694,13 +716,13 @@ For example:
|
|||||||
"[](const auto &reader) { ... return loaded_val; }"))))
|
"[](const auto &reader) { ... return loaded_val; }"))))
|
||||||
```
|
```
|
||||||
|
|
||||||
**Helper for `std::vector`**
|
##### Helper for `std::vector`
|
||||||
|
|
||||||
For custom serialization of vector elements, you may use
|
For custom serialization of vector elements, you may use
|
||||||
`lcp:capnp-save-vector` and `lcp:capnp-load-vector`. They function exactly the
|
`lcp:capnp-save-vector` and `lcp:capnp-load-vector`. They function exactly the
|
||||||
same as helpers for `std::optional`.
|
same as helpers for `std::optional`.
|
||||||
|
|
||||||
**Helper for enumerations**
|
##### Helper for enumerations
|
||||||
|
|
||||||
If the enumeration is defined via `lcp:define-enum`, the default LCP
|
If the enumeration is defined via `lcp:define-enum`, the default LCP
|
||||||
serialization should generate the correct code.
|
serialization should generate the correct code.
|
||||||
|
@ -123,7 +123,10 @@
|
|||||||
|
|
||||||
(deftype cpp-primitive-type-keywords ()
|
(deftype cpp-primitive-type-keywords ()
|
||||||
"List of keywords that specify a primitive type in C++."
|
"List of keywords that specify a primitive type in C++."
|
||||||
`(member :bool :int :int16_t :int32_t :int64_t :uint :uint32_t :uint64_t :float :double))
|
`(member :bool :int :int16_t :int32_t :int64_t :uint :uint16_t :uint32_t :uint64_t :float :double))
|
||||||
|
|
||||||
|
(defconstant +cpp-primitive-type-keywords+
|
||||||
|
'(:bool :int :int16_t :int32_t :int64_t :uint :uint16_t :uint32_t :uint64_t :float :double))
|
||||||
|
|
||||||
(defmethod cpp-type-name ((cpp-type symbol))
|
(defmethod cpp-type-name ((cpp-type symbol))
|
||||||
"Return PascalCase of CPP-TYPE symbol or lowercase if it is a primitive type."
|
"Return PascalCase of CPP-TYPE symbol or lowercase if it is a primitive type."
|
||||||
@ -165,6 +168,13 @@ produces:
|
|||||||
(when (find #\& type-decl)
|
(when (find #\& type-decl)
|
||||||
(error "References not supported in '~A'" type-decl))
|
(error "References not supported in '~A'" type-decl))
|
||||||
(setf type-decl (string-trim +whitespace-chars+ type-decl))
|
(setf type-decl (string-trim +whitespace-chars+ type-decl))
|
||||||
|
;; Check if primitive type
|
||||||
|
(let ((type-keyword (member type-decl +cpp-primitive-type-keywords+
|
||||||
|
:test #'string-equal)))
|
||||||
|
(when type-keyword
|
||||||
|
(return-from parse-cpp-type-declaration
|
||||||
|
(make-instance 'cpp-primitive-type :name (car type-keyword)))))
|
||||||
|
;; Check if pointer
|
||||||
(let ((ptr-pos (position #\* type-decl :from-end t)))
|
(let ((ptr-pos (position #\* type-decl :from-end t)))
|
||||||
(when (and ptr-pos (not (cl-ppcre:scan "[()<>]" type-decl :start ptr-pos)))
|
(when (and ptr-pos (not (cl-ppcre:scan "[()<>]" type-decl :start ptr-pos)))
|
||||||
(return-from parse-cpp-type-declaration
|
(return-from parse-cpp-type-declaration
|
||||||
@ -172,6 +182,7 @@ produces:
|
|||||||
:name (subseq type-decl ptr-pos)
|
:name (subseq type-decl ptr-pos)
|
||||||
:type-args (list (parse-cpp-type-declaration
|
:type-args (list (parse-cpp-type-declaration
|
||||||
(subseq type-decl 0 ptr-pos)))))))
|
(subseq type-decl 0 ptr-pos)))))))
|
||||||
|
;; Other cases
|
||||||
(destructuring-bind (full-name &optional template)
|
(destructuring-bind (full-name &optional template)
|
||||||
(cl-ppcre:split "<" type-decl :limit 2)
|
(cl-ppcre:split "<" type-decl :limit 2)
|
||||||
(let* ((namespace-split (cl-ppcre:split "::" full-name))
|
(let* ((namespace-split (cl-ppcre:split "::" full-name))
|
||||||
@ -580,10 +591,11 @@ encoded as union inheritance in Cap'n Proto."
|
|||||||
(declare (type string cpp-type capnp-type))
|
(declare (type string cpp-type capnp-type))
|
||||||
(push (cons cpp-type capnp-type) *capnp-type-converters*))
|
(push (cons cpp-type capnp-type) *capnp-type-converters*))
|
||||||
|
|
||||||
(defun capnp-type<-cpp-type (cpp-type)
|
(defun capnp-type<-cpp-type (cpp-type &key boxp)
|
||||||
(flet ((convert-primitive-type (name)
|
(flet ((convert-primitive-type (name)
|
||||||
(when (member name '(:int :uint))
|
(when (member name '(:int :uint))
|
||||||
(error "Unable to get Capnp type for integer without specified width."))
|
(error "Unable to get Capnp type for integer without specified width."))
|
||||||
|
(let ((capnp-type
|
||||||
(case name
|
(case name
|
||||||
(:bool "Bool")
|
(:bool "Bool")
|
||||||
(:float "Float32")
|
(:float "Float32")
|
||||||
@ -593,12 +605,17 @@ encoded as union inheritance in Cap'n Proto."
|
|||||||
;; Delete the _t suffix
|
;; Delete the _t suffix
|
||||||
(cl-ppcre:regex-replace
|
(cl-ppcre:regex-replace
|
||||||
"_t$" (string-downcase name :start (1+ pos-of-i)) ""))))))
|
"_t$" (string-downcase name :start (1+ pos-of-i)) ""))))))
|
||||||
|
(if boxp
|
||||||
|
(concatenate 'string "Utils.Box" capnp-type)
|
||||||
|
capnp-type))))
|
||||||
(typecase cpp-type
|
(typecase cpp-type
|
||||||
(cpp-primitive-type-keywords (convert-primitive-type cpp-type))
|
(cpp-primitive-type-keywords (convert-primitive-type cpp-type))
|
||||||
(cpp-primitive-type (convert-primitive-type (cpp-type-base-name cpp-type)))
|
(cpp-primitive-type (convert-primitive-type (cpp-type-base-name cpp-type)))
|
||||||
(string
|
(string
|
||||||
(let ((type (parse-cpp-type-declaration cpp-type)))
|
(let ((type (parse-cpp-type-declaration cpp-type)))
|
||||||
(cond
|
(cond
|
||||||
|
((typep type 'cpp-primitive-type)
|
||||||
|
(convert-primitive-type (cpp-type-base-name type)))
|
||||||
((string= "string" (cpp-type-base-name type))
|
((string= "string" (cpp-type-base-name type))
|
||||||
"Text")
|
"Text")
|
||||||
((string= "shared_ptr" (cpp-type-base-name type))
|
((string= "shared_ptr" (cpp-type-base-name type))
|
||||||
@ -617,7 +634,8 @@ encoded as union inheritance in Cap'n Proto."
|
|||||||
(cpp-type-decl (first (cpp-type-type-args type))))))
|
(cpp-type-decl (first (cpp-type-type-args type))))))
|
||||||
((string= "optional" (cpp-type-base-name type))
|
((string= "optional" (cpp-type-base-name type))
|
||||||
(format nil "Utils.Optional(~A)"
|
(format nil "Utils.Optional(~A)"
|
||||||
(capnp-type<-cpp-type (cpp-type-decl (first (cpp-type-type-args type))))))
|
(capnp-type<-cpp-type (cpp-type-decl (first (cpp-type-type-args type)))
|
||||||
|
:boxp t)))
|
||||||
((assoc cpp-type *capnp-type-converters* :test #'string=)
|
((assoc cpp-type *capnp-type-converters* :test #'string=)
|
||||||
(cdr (assoc cpp-type *capnp-type-converters* :test #'string=)))
|
(cdr (assoc cpp-type *capnp-type-converters* :test #'string=)))
|
||||||
(t (cpp-type-name cpp-type)))))
|
(t (cpp-type-name cpp-type)))))
|
||||||
@ -859,9 +877,12 @@ used for outside definition."
|
|||||||
member-builder member-name)))))
|
member-builder member-name)))))
|
||||||
((string= "optional" type-name)
|
((string= "optional" type-name)
|
||||||
(let* ((elem-type (car (cpp-type-type-args type)))
|
(let* ((elem-type (car (cpp-type-type-args type)))
|
||||||
(capnp-cpp-type (capnp-cpp-type<-cpp-type elem-type :boxp t)))
|
(capnp-cpp-type (capnp-cpp-type<-cpp-type elem-type :boxp t))
|
||||||
|
(lambda-code (when (typep elem-type 'cpp-primitive-type)
|
||||||
|
"[](auto *builder, const auto &v){ builder->setValue(v); }")))
|
||||||
(raw-cpp-string
|
(raw-cpp-string
|
||||||
(funcall (capnp-save-optional (cpp-type-decl capnp-cpp-type) (cpp-type-decl elem-type))
|
(funcall (capnp-save-optional
|
||||||
|
(cpp-type-decl capnp-cpp-type) (cpp-type-decl elem-type) lambda-code)
|
||||||
member-builder member-name))))
|
member-builder member-name))))
|
||||||
((member type-name '("unique_ptr" "shared_ptr" "vector") :test #'string=)
|
((member type-name '("unique_ptr" "shared_ptr" "vector") :test #'string=)
|
||||||
(error "Use a custom :capnp-save function for ~A ~A" type-name member-name))
|
(error "Use a custom :capnp-save function for ~A ~A" type-name member-name))
|
||||||
@ -1052,9 +1073,12 @@ used for outside definition."
|
|||||||
member-reader member-name)))))
|
member-reader member-name)))))
|
||||||
((string= "optional" type-name)
|
((string= "optional" type-name)
|
||||||
(let* ((elem-type (car (cpp-type-type-args type)))
|
(let* ((elem-type (car (cpp-type-type-args type)))
|
||||||
(capnp-cpp-type (capnp-cpp-type<-cpp-type elem-type :boxp t)))
|
(capnp-cpp-type (capnp-cpp-type<-cpp-type elem-type :boxp t))
|
||||||
|
(lambda-code (when (string= "Box" (cpp-type-name capnp-cpp-type) :end2 (length "Box"))
|
||||||
|
"[](const auto &reader){ return reader.getValue(); }")))
|
||||||
(raw-cpp-string
|
(raw-cpp-string
|
||||||
(funcall (capnp-load-optional (cpp-type-decl capnp-cpp-type) (cpp-type-decl elem-type))
|
(funcall (capnp-load-optional
|
||||||
|
(cpp-type-decl capnp-cpp-type) (cpp-type-decl elem-type) lambda-code)
|
||||||
member-reader member-name))))
|
member-reader member-name))))
|
||||||
((member type-name '("unique_ptr" "shared_ptr" "vector") :test #'string=)
|
((member type-name '("unique_ptr" "shared_ptr" "vector") :test #'string=)
|
||||||
(error "Use a custom :capnp-load function for ~A ~A" type-name member-name))
|
(error "Use a custom :capnp-load function for ~A ~A" type-name member-name))
|
||||||
|
Loading…
Reference in New Issue
Block a user