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:
Teon Banek 2018-08-31 14:19:57 +02:00
parent 5c69ae8a0c
commit ee889b98fc
2 changed files with 67 additions and 21 deletions

View File

@ -672,14 +672,36 @@ list of pairs, e.g.
#### 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
serialization code you may use `lcp:capnp-save-optional` and
`lcp:capnp-load-optional`.
Both functions expect 3 arguments: Cap'n Proto type in C++, type of the value
inside `std::optional` and an optional C++ lambda code.
Both functions expect 3 arguments.
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:
@ -694,13 +716,13 @@ For example:
"[](const auto &reader) { ... return loaded_val; }"))))
```
**Helper for `std::vector`**
##### Helper for `std::vector`
For custom serialization of vector elements, you may use
`lcp:capnp-save-vector` and `lcp:capnp-load-vector`. They function exactly the
same as helpers for `std::optional`.
**Helper for enumerations**
##### Helper for enumerations
If the enumeration is defined via `lcp:define-enum`, the default LCP
serialization should generate the correct code.

View File

@ -123,7 +123,10 @@
(deftype cpp-primitive-type-keywords ()
"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))
"Return PascalCase of CPP-TYPE symbol or lowercase if it is a primitive type."
@ -165,6 +168,13 @@ produces:
(when (find #\& type-decl)
(error "References not supported in '~A'" 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)))
(when (and ptr-pos (not (cl-ppcre:scan "[()<>]" type-decl :start ptr-pos)))
(return-from parse-cpp-type-declaration
@ -172,6 +182,7 @@ produces:
:name (subseq type-decl ptr-pos)
:type-args (list (parse-cpp-type-declaration
(subseq type-decl 0 ptr-pos)))))))
;; Other cases
(destructuring-bind (full-name &optional template)
(cl-ppcre:split "<" type-decl :limit 2)
(let* ((namespace-split (cl-ppcre:split "::" full-name))
@ -580,25 +591,31 @@ encoded as union inheritance in Cap'n Proto."
(declare (type string cpp-type capnp-type))
(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)
(when (member name '(:int :uint))
(error "Unable to get Capnp type for integer without specified width."))
(case name
(:bool "Bool")
(:float "Float32")
(:double "Float64")
(otherwise
(let ((pos-of-i (position #\I (string name))))
;; Delete the _t suffix
(cl-ppcre:regex-replace
"_t$" (string-downcase name :start (1+ pos-of-i)) ""))))))
(let ((capnp-type
(case name
(:bool "Bool")
(:float "Float32")
(:double "Float64")
(otherwise
(let ((pos-of-i (position #\I (string name))))
;; Delete the _t suffix
(cl-ppcre:regex-replace
"_t$" (string-downcase name :start (1+ pos-of-i)) ""))))))
(if boxp
(concatenate 'string "Utils.Box" capnp-type)
capnp-type))))
(typecase cpp-type
(cpp-primitive-type-keywords (convert-primitive-type cpp-type))
(cpp-primitive-type (convert-primitive-type (cpp-type-base-name cpp-type)))
(string
(let ((type (parse-cpp-type-declaration cpp-type)))
(cond
((typep type 'cpp-primitive-type)
(convert-primitive-type (cpp-type-base-name type)))
((string= "string" (cpp-type-base-name type))
"Text")
((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))))))
((string= "optional" (cpp-type-base-name type))
(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=)
(cdr (assoc cpp-type *capnp-type-converters* :test #'string=)))
(t (cpp-type-name cpp-type)))))
@ -859,9 +877,12 @@ used for outside definition."
member-builder member-name)))))
((string= "optional" type-name)
(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
(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 type-name '("unique_ptr" "shared_ptr" "vector") :test #'string=)
(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)))))
((string= "optional" type-name)
(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
(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 type-name '("unique_ptr" "shared_ptr" "vector") :test #'string=)
(error "Use a custom :capnp-load function for ~A ~A" type-name member-name))