From ee889b98fc5e0b4485cc4c7d38474445702ce7ef Mon Sep 17 00:00:00 2001 From: Teon Banek Date: Fri, 31 Aug 2018 14:19:57 +0200 Subject: [PATCH] Add default LCP save of primitive types in optional Reviewers: msantl, mtomic Reviewed By: msantl Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1579 --- docs/dev/lcp.md | 32 ++++++++++++++++++++++----- src/lisp/lcp.lisp | 56 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/docs/dev/lcp.md b/docs/dev/lcp.md index 38a7f6537..a04909458 100644 --- a/docs/dev/lcp.md +++ b/docs/dev/lcp.md @@ -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")) + (:serialize :capnp)) + +(lcp:define-class my-class-with-known-type-optional () + ((known-type-optional "std::experimental::optional")) + (: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. diff --git a/src/lisp/lcp.lisp b/src/lisp/lcp.lisp index 9ffdbf7fa..7ddbd5706 100644 --- a/src/lisp/lcp.lisp +++ b/src/lisp/lcp.lisp @@ -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))