Implement cloning in LCP
Summary: Add automatic generating of cloning (deep-copy) functions for LCP defined classes. This enables us to remove a bunch of manually written `Clone` functions from AST and also to implement logical plan cloning properly (before it was using serialization). Reviewers: teon.banek, llugovic Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1808
This commit is contained in:
parent
0111fa506a
commit
f8e76efa3e
235
docs/dev/lcp.md
235
docs/dev/lcp.md
@ -23,6 +23,7 @@ Contents
|
||||
- [Defining an RPC](#defining-an-rpc)
|
||||
- [Cap'n Proto Serialization](#capnp-serial)
|
||||
- [SaveLoadKit Serialization](#slk-serial)
|
||||
- [Object Cloning](#object-cloning)
|
||||
|
||||
## Running LCP
|
||||
|
||||
@ -1014,3 +1015,237 @@ example:
|
||||
(:serialize (:slk)))
|
||||
```
|
||||
|
||||
### Object Cloning
|
||||
|
||||
LCP supports automatic generation of cloning (deep copy) code for user-defined
|
||||
classes.
|
||||
|
||||
A textbook example of an object that would require a deep copy functionality is
|
||||
a tree structure. The following class represents a node in the binary tree,
|
||||
carrying an integer value and having pointers to its two children:
|
||||
|
||||
```lisp
|
||||
(lcp:define-class node ()
|
||||
((value :int32_t)
|
||||
(left "std::unique_ptr<Node>")
|
||||
(right "std::unique_ptr<Node>"))
|
||||
(:clone :return-type (lambda (typename)
|
||||
#>cpp
|
||||
std::unique_ptr<${typename}>
|
||||
cpp<#)
|
||||
:init-object (lambda (var typename)
|
||||
#>cpp
|
||||
auto ${var} = std::make_unique<${typename}>();
|
||||
cpp<#)))
|
||||
```
|
||||
|
||||
The above will generate the following C++ class with a `Clone` function that
|
||||
can be used for making a deep copy of the binary tree structure:
|
||||
|
||||
```cpp
|
||||
class Node {
|
||||
public:
|
||||
std::unique_ptr<Node> Clone() const {
|
||||
auto object = std::make_unique<Node>();
|
||||
object->value_ = value_;
|
||||
object->left_ = left_ ? left_->Clone() : nullptr;
|
||||
object->right_ = right_ ? right_->Clone() : nullptr;
|
||||
return object;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t value_;
|
||||
std::unique_ptr<Node> left_;
|
||||
std::unique_ptr<Node> right_;
|
||||
};
|
||||
```
|
||||
|
||||
To specify that a class is deep copyable, `:clone` class option must be passed.
|
||||
We have already seen two options that `:clone` accepts: `:return-type` and
|
||||
`:init-object`.
|
||||
|
||||
`:return-type` expects a function that takes a single argument which is the C++
|
||||
type name of the class and produces C++ code, which is a valid C++ type
|
||||
delcaration. Here we used it to specify that `Clone` function should return a
|
||||
`std::unique_ptr` to the newly created `Node` to override the default behavior.
|
||||
When `:return-type` option is not provided and class `T` is a member of an
|
||||
inheritance hierarchy, `Clone` will return `std::unique_ptr<Base>`, where
|
||||
`Base` is the root of that hierarchy. If `T` is not a member of inheritance
|
||||
hierarchy, `Clone` will return `T` by default.
|
||||
|
||||
`:init-object` expects a function that takes two arguments, first is a variable
|
||||
name, and the second one is the C++ type name of the class. It must produce C++
|
||||
code that initializes an object with the given name of the same type that
|
||||
`Clone` function returns. Here we had to use it since we are overriding the
|
||||
default return value of `Clone`. Unless `:init-object` argument is provided, an
|
||||
object of type `T` will be instantiated with `auto object =
|
||||
std::make_unique<T>();` if `T` is a member of inheritance hierarchy, and `T
|
||||
object;` if it is not. As you can see, deep copyable objects must be default
|
||||
constructible.
|
||||
|
||||
|
||||
#### Single Inheritance
|
||||
|
||||
LCP supports deep copying of classes with single inheritance. The root class
|
||||
will have a virtual `Clone` function that child classes will override. For
|
||||
example:
|
||||
|
||||
```lisp
|
||||
(lcp:define-class base ()
|
||||
((member :int32_t))
|
||||
(:clone))
|
||||
|
||||
(lcp:define-class derived (base)
|
||||
((another-member :int32_t))
|
||||
(:clone))
|
||||
```
|
||||
|
||||
We get the following code:
|
||||
|
||||
```cpp
|
||||
class Base {
|
||||
public:
|
||||
virtual std::unique_ptr<Base> Clone() const {
|
||||
auto object = std::make_unique<Base>();
|
||||
object->member_ = member_;
|
||||
return object;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t member_;
|
||||
};
|
||||
|
||||
class Derived : public Base {
|
||||
public:
|
||||
std::unique_ptr<Base> Clone() const override {
|
||||
auto object = std::make_unique<Derived>();
|
||||
object->member_ = member_;
|
||||
object->another_member_ = another_member_;
|
||||
return object;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t another_member_;
|
||||
};
|
||||
```
|
||||
|
||||
Notice that the `Clone` function of derived class also returns
|
||||
`std::unique_ptr<Base>`, because C++ doesn't support return type covariance
|
||||
with smart pointers.
|
||||
|
||||
#### Multiple Inheritance
|
||||
|
||||
Deep copying of classes with multiple inheritance is *not* supported!
|
||||
|
||||
Usually, multiple inheritance is used to satisfy some interface which doesn't
|
||||
carry data. In such cases, you can ignore the multiple inheritance by
|
||||
specifying `:ignore-other-base-classes` option. For example:
|
||||
|
||||
```lisp
|
||||
(lcp:define-class derived (primary-base some-interface ...)
|
||||
...
|
||||
(:clone :ignore-other-base-classes t))
|
||||
```
|
||||
|
||||
The above will produce deep copying code as if `derived` is inheriting *only*
|
||||
from `primary-base`.
|
||||
|
||||
#### Templated Types
|
||||
|
||||
Deep copying of templated types is *not* supported!
|
||||
|
||||
#### Custom Clone Hooks
|
||||
|
||||
In cases when default deep copying code is not adequate, you may wish to
|
||||
provide your own. LCP provides `:clone` option that can be specified for each
|
||||
member.
|
||||
|
||||
These hooks for custom copying expect a function with two arguments, `source`
|
||||
and `dest`, representing the member location in the cloned struct and member
|
||||
location in the new struct. This allows to have a more generic function which
|
||||
works with any member of some type. The return value of the function needs to
|
||||
be C++ code.
|
||||
|
||||
It is also possible to specify that a member is cloned by copying by passing
|
||||
`:copy` instead of a function as an argument to `:clone`.
|
||||
|
||||
```lisp
|
||||
(lcp:define-class my-class ()
|
||||
((callback "std::function<void(int, int)>"
|
||||
:clone :copy)
|
||||
(widget "Widget"
|
||||
:clone (lambda (source dest)
|
||||
#>cpp
|
||||
${dest} = WidgetFactory::Create(${source}.type());
|
||||
cpp<#)))
|
||||
(:clone))
|
||||
```
|
||||
|
||||
#### Additional Arguments to Generated Clone Function
|
||||
|
||||
By default, `Clone` function takes no argument. In some cases we would like to
|
||||
accept additional arguments necessary to create a deep copy. Let's see how this
|
||||
is done in LCP using the `:args` option.
|
||||
|
||||
`:args` expects a list of pairs. Each pair designates one argument. The first
|
||||
element of pair is the argument name and the second is the C++ type of that
|
||||
argument.
|
||||
|
||||
One case where we want to pass additional arguments to `Clone` is when there is
|
||||
another object that owns all objects being cloned. For example, `AstStorage`
|
||||
owns all Memgraph AST nodes. For that reason, `Clone` function of all AST node
|
||||
types takes an `AstStorage \*` argument. Here's a snippet from the actual AST
|
||||
code:
|
||||
|
||||
```lisp
|
||||
(lcp:define-class tree ()
|
||||
((uid :int32_t))
|
||||
(:abstractp t)
|
||||
...
|
||||
(:clone :return-type (lambda (typename)
|
||||
(format nil "~A*" typename))
|
||||
:args '((storage "AstStorage *"))
|
||||
:init-object (lambda (var typename)
|
||||
(format nil "~A* ~A = storage->Create<~A>();"
|
||||
typename var typename))))
|
||||
|
||||
(lcp:define-class expression (tree)
|
||||
()
|
||||
(:abstractp t)
|
||||
...
|
||||
(:clone))
|
||||
|
||||
(lcp:define-class where (tree)
|
||||
((expression "Expression *" :initval "nullptr" :scope :public))
|
||||
(:clone))
|
||||
```
|
||||
|
||||
`:args` option is only passed to the root class in inheritance hierarchy. By
|
||||
default, the same extra arguments will be passed to all class members that are
|
||||
cloned using `Clone` metehod. The generated code is:
|
||||
|
||||
```cpp
|
||||
class Tree {
|
||||
public:
|
||||
virtual Tree *Clone(AstStorage *storage) const = 0;
|
||||
private:
|
||||
int32_t uid_;
|
||||
};
|
||||
|
||||
class Expression : public Tree {
|
||||
public:
|
||||
Expression *Clone(AstStorage *storage) const override = 0;
|
||||
};
|
||||
|
||||
class Where : public Tree {
|
||||
public:
|
||||
Expression *expression_{nullptr};
|
||||
|
||||
Where *Clone(AstStorage *storage) const override {
|
||||
Where *object = storage->Create<Where>();
|
||||
object->uid_ = uid_;
|
||||
object->expression_ = expression_ ? expression_->Clone(storage) : nullptr;
|
||||
return object;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
@ -6,6 +6,7 @@ set(lcp_src_files
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/lcp-compile
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/package.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/types.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/clone.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/code-gen.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/slk.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/lcp.lisp
|
||||
@ -64,6 +65,7 @@ macro(define_add_lcp name main_src_files generated_lcp_files)
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/lcp-compile
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/package.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/types.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/clone.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/code-gen.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/slk.lisp
|
||||
${CMAKE_SOURCE_DIR}/src/lisp/lcp.lisp
|
||||
|
255
src/lisp/clone.lisp
Normal file
255
src/lisp/clone.lisp
Normal file
@ -0,0 +1,255 @@
|
||||
(in-package #:lcp.clone)
|
||||
|
||||
(defvar *variable-idx* 0 "Used to generate unique variable names")
|
||||
|
||||
(defmacro with-vars (vars &body body)
|
||||
"Generates unique variable names for use in generated code by
|
||||
appending an index to desired variable names. Useful when generating
|
||||
loops which might reuse counter names.
|
||||
|
||||
Usage example:
|
||||
(with-vars ((loop-counter \"i\"))
|
||||
(format nil \"for (auto ~A = 0; ~A < v.size(); ++~A) {
|
||||
// do something
|
||||
}\"
|
||||
loop-counter loop-counter loop-counter))"
|
||||
`(let* ((*variable-idx* (1+ *variable-idx*))
|
||||
,@(loop for var in vars collecting
|
||||
`(,(first var)
|
||||
(format nil "~A~A" ,(second var) *variable-idx*))))
|
||||
,@body))
|
||||
|
||||
(define-condition clone-error (error)
|
||||
((message :type string :initarg :message :reader clone-error-message)
|
||||
(format-args :type list :initform nil :initarg :format-args :reader clone-error-format-args))
|
||||
(:report (lambda (condition stream)
|
||||
(apply #'format stream
|
||||
(clone-error-message condition)
|
||||
(clone-error-format-args condition)))))
|
||||
|
||||
(defun clone-error (message &rest format-args)
|
||||
(error 'clone-error :message message :format-args format-args))
|
||||
|
||||
(defun cloning-parent (cpp-class)
|
||||
(let ((supers (lcp::cpp-class-super-classes cpp-class))
|
||||
(opts (lcp::cpp-class-clone-opts cpp-class)))
|
||||
(unless opts
|
||||
(clone-error "Class ~A isn't cloneable" (lcp::cpp-type-base-name cpp-class)))
|
||||
(cond
|
||||
((lcp::clone-opts-base opts) nil)
|
||||
((lcp::clone-opts-ignore-other-base-classes opts) (car supers))
|
||||
(t
|
||||
(when (> (length supers) 1)
|
||||
(clone-error "Cloning doesn't support multiple inheritance (class '~A', parents: '~A')"
|
||||
(lcp::cpp-type-base-name cpp-class) supers))
|
||||
(car supers)))))
|
||||
|
||||
(defun cloning-root (cpp-class)
|
||||
(let ((parent-class (cloning-parent cpp-class)))
|
||||
(if parent-class
|
||||
(cloning-root (lcp::find-cpp-class parent-class))
|
||||
cpp-class)))
|
||||
|
||||
(defun members-for-cloning (cpp-class)
|
||||
(do ((current-class cpp-class) members)
|
||||
((not current-class) members)
|
||||
(setf members (append (remove-if-not #'lcp::cpp-member-clone
|
||||
(lcp::cpp-class-members current-class))
|
||||
members))
|
||||
(setf current-class (lcp::find-cpp-class (cloning-parent current-class)))))
|
||||
|
||||
(defun copy-object (source-name dest-name)
|
||||
(format nil "~A = ~A;" dest-name source-name))
|
||||
|
||||
(defun clone-by-copy-p (object-type)
|
||||
(cond
|
||||
((string= "vector" (lcp::cpp-type-name object-type))
|
||||
(clone-by-copy-p (car (lcp::cpp-type-type-args object-type))))
|
||||
((string= "optional" (lcp::cpp-type-name object-type))
|
||||
(clone-by-copy-p (car (lcp::cpp-type-type-args object-type))))
|
||||
((string= "unordered_map" (lcp::cpp-type-name object-type))
|
||||
(and (clone-by-copy-p (first (lcp::cpp-type-type-args object-type)))
|
||||
(clone-by-copy-p (second (lcp::cpp-type-type-args object-type)))))
|
||||
((string= "pair" (lcp::cpp-type-name object-type))
|
||||
(and (clone-by-copy-p (first (lcp::cpp-type-type-args object-type)))
|
||||
(clone-by-copy-p (second (lcp::cpp-type-type-args object-type)))))
|
||||
((lcp::cpp-type-type-args object-type) nil)
|
||||
(t (or
|
||||
(lcp::find-cpp-enum (lcp::cpp-type-name object-type))
|
||||
(typep object-type 'lcp::cpp-primitive-type)
|
||||
(string= "string" (lcp::cpp-type-name object-type))
|
||||
(not (lcp::find-cpp-class (lcp::cpp-type-name object-type)))
|
||||
(not (lcp::cpp-class-clone-opts
|
||||
(lcp::find-cpp-class (lcp::cpp-type-name object-type))))))))
|
||||
|
||||
(defun clone-object (object-type source-name dest-name &key args)
|
||||
(let ((object-type
|
||||
(ctypecase object-type
|
||||
(lcp::cpp-type object-type)
|
||||
(string (lcp::parse-cpp-type-declaration object-type))
|
||||
(symbol (lcp::cpp-type object-type))))
|
||||
(arg-list (format nil "~{~A~^, ~}"
|
||||
(mapcar (lambda (name-and-type)
|
||||
(lcp::cpp-variable-name (first name-and-type)))
|
||||
args))))
|
||||
(cond
|
||||
((clone-by-copy-p object-type)
|
||||
(copy-object source-name dest-name))
|
||||
((lcp::cpp-pointer-type-p object-type)
|
||||
(format nil "~A = ~A ? ~A->Clone(~A) : nullptr;"
|
||||
dest-name source-name source-name arg-list))
|
||||
((string= "optional" (lcp::cpp-type-name object-type))
|
||||
(let ((value-type (car (lcp::cpp-type-type-args object-type))))
|
||||
(clone-optional value-type source-name dest-name :args args)))
|
||||
((string= "vector" (lcp::cpp-type-name object-type))
|
||||
(let ((elem-type (car (lcp::cpp-type-type-args object-type))))
|
||||
(clone-vector elem-type source-name dest-name :args args)))
|
||||
((string= "unordered_map" (lcp::cpp-type-name object-type))
|
||||
(let ((key-type (first (lcp::cpp-type-type-args object-type)))
|
||||
(value-type (second (lcp::cpp-type-type-args object-type))))
|
||||
(clone-map key-type value-type source-name dest-name :args args)))
|
||||
((string= "pair" (lcp::cpp-type-name object-type))
|
||||
(let ((first-type (first (lcp::cpp-type-type-args object-type)))
|
||||
(second-type (second (lcp::cpp-type-type-args object-type))))
|
||||
(clone-pair first-type second-type source-name dest-name :args args)))
|
||||
((and (lcp::find-cpp-class (lcp::cpp-type-name object-type))
|
||||
(lcp::cpp-class-clone-opts (lcp::find-cpp-class (lcp::cpp-type-name object-type))))
|
||||
(format nil "~A = ~A.Clone(~A);" dest-name source-name arg-list))
|
||||
(t
|
||||
(format nil "static_assert(false, \"Don't know how to clone object of type ~A\");"
|
||||
(lcp::cpp-type-decl object-type))))))
|
||||
|
||||
(defun clone-vector (elem-type source-name dest-name &key args)
|
||||
(with-vars ((loop-counter "i"))
|
||||
(format nil
|
||||
"~A.resize(~A.size());
|
||||
for (auto ~A = 0; ~A < ~A.size(); ++~A) { ~A }"
|
||||
dest-name source-name
|
||||
loop-counter loop-counter source-name loop-counter
|
||||
(clone-object elem-type
|
||||
(format nil "~A[~A]" source-name loop-counter)
|
||||
(format nil "~A[~A]" dest-name loop-counter)
|
||||
:args args))))
|
||||
|
||||
(defun clone-map (key-type value-type source-name dest-name &key args)
|
||||
(with-vars ((loop-var "kv") (entry-var "entry"))
|
||||
(let ((entry-type (lcp::make-cpp-type "pair"
|
||||
:namespace '("std")
|
||||
:type-args (list key-type value-type))))
|
||||
(format nil
|
||||
"for (const auto &~A : ~A) {
|
||||
~A ~A;
|
||||
~A
|
||||
~A.emplace(std::move(~A));
|
||||
}"
|
||||
loop-var source-name
|
||||
(lcp::cpp-type-decl entry-type) entry-var
|
||||
(clone-object entry-type loop-var entry-var :args args)
|
||||
dest-name entry-var))))
|
||||
|
||||
(defun clone-optional (value-type source-name dest-name &key args)
|
||||
(with-vars ((value-var "value"))
|
||||
(format nil
|
||||
"if (~A) {
|
||||
~A ~A;
|
||||
~A
|
||||
~A.emplace(std::move(~A));
|
||||
} else {
|
||||
~A = std::experimental::nullopt;
|
||||
}"
|
||||
source-name
|
||||
(lcp::cpp-type-decl value-type) value-var
|
||||
(clone-object value-type
|
||||
(format nil "(*~A)" source-name)
|
||||
value-var
|
||||
:args args)
|
||||
dest-name value-var
|
||||
dest-name)))
|
||||
|
||||
(defun clone-pair (first-type second-type source-name dest-name &key args)
|
||||
(with-vars ((first-var "first") (second-var "second"))
|
||||
(with-output-to-string (cpp-out)
|
||||
(lcp::with-cpp-block-output (cpp-out)
|
||||
(format cpp-out
|
||||
"~A ~A;
|
||||
~A
|
||||
~A ~A;
|
||||
~A
|
||||
~A = std::make_pair(std::move(~A), std::move(~A));"
|
||||
(lcp::cpp-type-decl first-type) first-var
|
||||
(clone-object first-type
|
||||
(format nil "~A.first" source-name)
|
||||
first-var
|
||||
:args args)
|
||||
(lcp::cpp-type-decl second-type) second-var
|
||||
(clone-object second-type
|
||||
(format nil "~A.second" source-name)
|
||||
second-var
|
||||
:args args)
|
||||
dest-name first-var second-var))
|
||||
cpp-out)))
|
||||
|
||||
(defun clone-function-definition-for-class (cpp-class)
|
||||
(check-type cpp-class lcp::cpp-class)
|
||||
(when (lcp::cpp-type-type-params cpp-class)
|
||||
(clone-error "Don't know how to clone templated class '~A'"
|
||||
(lcp::cpp-type-base-name cpp-class)))
|
||||
(let* ((cloning-root (cloning-root cpp-class))
|
||||
(root-opts (lcp::cpp-class-clone-opts cloning-root))
|
||||
(inheritancep (or (lcp::direct-subclasses-of cpp-class)
|
||||
(cloning-parent cpp-class)))
|
||||
(return-type (cond
|
||||
((lcp::clone-opts-return-type root-opts)
|
||||
(lcp::cpp-code
|
||||
(funcall (lcp::clone-opts-return-type root-opts)
|
||||
(lcp::cpp-type-name cpp-class))))
|
||||
(inheritancep (format nil "std::unique_ptr<~A>"
|
||||
(lcp::cpp-type-name (cloning-root cpp-class))))
|
||||
(t (lcp::cpp-type-name cpp-class))))
|
||||
(declaration
|
||||
(lcp::cpp-method-declaration cpp-class "Clone"
|
||||
:args (lcp::clone-opts-args root-opts)
|
||||
:returns return-type
|
||||
:virtual (and inheritancep
|
||||
(eq cpp-class cloning-root))
|
||||
:inline t
|
||||
:const t
|
||||
:override (and inheritancep
|
||||
(not (eq cpp-class cloning-root)))
|
||||
:delete (lcp::cpp-class-abstractp cpp-class))))
|
||||
(if (lcp::cpp-class-abstractp cpp-class)
|
||||
(return-from clone-function-definition-for-class (format nil "~A;" declaration)))
|
||||
(with-output-to-string (cpp-out)
|
||||
(lcp::with-cpp-block-output (cpp-out :name declaration :semicolonp nil)
|
||||
(let (object-access)
|
||||
(cond
|
||||
((lcp::clone-opts-init-object root-opts)
|
||||
(setf object-access "object->")
|
||||
(write-line
|
||||
(lcp::cpp-code
|
||||
(funcall (lcp::clone-opts-init-object root-opts)
|
||||
"object" (lcp::cpp-type-name cpp-class)))
|
||||
cpp-out))
|
||||
(inheritancep
|
||||
(setf object-access "object->")
|
||||
(format cpp-out "~&auto object = std::make_unique<~A>();"
|
||||
(lcp::cpp-type-name cpp-class)))
|
||||
(t
|
||||
(setf object-access "object.")
|
||||
(format cpp-out "~&~A object;"
|
||||
(lcp::cpp-type-name cpp-class))))
|
||||
(dolist (member (members-for-cloning cpp-class))
|
||||
(let* ((source (lcp::cpp-member-name member :struct (lcp::cpp-class-structp cpp-class)))
|
||||
(dest (format nil "~A~A" object-access source)))
|
||||
(cond
|
||||
((eq (lcp::cpp-member-clone member) :copy)
|
||||
(format cpp-out "~&~A" (copy-object source dest)))
|
||||
((functionp (lcp::cpp-member-clone member))
|
||||
(format cpp-out "~&~A"
|
||||
(lcp::cpp-code (funcall (lcp::cpp-member-clone member) source dest))))
|
||||
(t
|
||||
(format cpp-out "~&~A"
|
||||
(clone-object (lcp::cpp-member-type member)
|
||||
source dest
|
||||
:args (lcp::clone-opts-args root-opts)))))))
|
||||
(format cpp-out "~&return object;"))))))
|
@ -564,3 +564,379 @@
|
||||
((protected-member :int64_t :scope :protected)
|
||||
(public-member :char))))
|
||||
'lcp.slk:slk-error)))
|
||||
|
||||
(deftest "clone"
|
||||
(subtest "no inheritance"
|
||||
(undefine-cpp-types)
|
||||
(let ((tree-class (lcp:define-class tree ()
|
||||
((value :int32_t)
|
||||
(left "std::unique_ptr<Tree>")
|
||||
(right "std::unique_ptr<Tree>"))
|
||||
(:clone :return-type (lambda (typename)
|
||||
(format nil "std::unique_ptr<~A>" typename))
|
||||
:init-object (lambda (var typename)
|
||||
(format nil "auto ~A = std::make_unique<~A>();"
|
||||
var typename)))))
|
||||
(forest-class (lcp:define-class forest ()
|
||||
((name "std::string")
|
||||
(small-tree "std::unique_ptr<Tree>")
|
||||
(big-tree "std::unique_ptr<Tree>"))
|
||||
(:clone))))
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class tree-class)
|
||||
"std::unique_ptr<Tree> Clone() const {
|
||||
auto object = std::make_unique<Tree>();
|
||||
object->value_ = value_;
|
||||
object->left_ = left_ ? left_->Clone() : nullptr;
|
||||
object->right_ = right_ ? right_->Clone() : nullptr;
|
||||
return object;
|
||||
}")
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class forest-class)
|
||||
"Forest Clone() const {
|
||||
Forest object;
|
||||
object.name_ = name_;
|
||||
object.small_tree_ = small_tree_ ? small_tree_->Clone() : nullptr;
|
||||
object.big_tree_ = big_tree_ ? big_tree_->Clone() : nullptr;
|
||||
return object;
|
||||
}")))
|
||||
(subtest "single inheritance"
|
||||
(undefine-cpp-types)
|
||||
;; Simple case
|
||||
(let ((base-class (lcp:define-class base ()
|
||||
((int-member :int32_t)
|
||||
(string-member "std::string"))
|
||||
(:clone)))
|
||||
(child-class (lcp:define-class child (base)
|
||||
((another-int-member :int64_t))
|
||||
(:clone))))
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class base-class)
|
||||
"virtual std::unique_ptr<Base> Clone() const {
|
||||
auto object = std::make_unique<Base>();
|
||||
object->int_member_ = int_member_;
|
||||
object->string_member_ = string_member_;
|
||||
return object;
|
||||
}")
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class child-class)
|
||||
"std::unique_ptr<Base> Clone() const override {
|
||||
auto object = std::make_unique<Child>();
|
||||
object->int_member_ = int_member_;
|
||||
object->string_member_ = string_member_;
|
||||
object->another_int_member_ = another_int_member_;
|
||||
return object;
|
||||
}"))
|
||||
(undefine-cpp-types)
|
||||
;; Abstract base class
|
||||
(let ((base-class (lcp:define-class base ()
|
||||
((int-member :int32_t)
|
||||
(string-member "std::string"))
|
||||
(:abstractp t)
|
||||
(:clone)))
|
||||
(child-class (lcp:define-class child (base)
|
||||
((another-int-member :int64_t))
|
||||
(:clone))))
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class base-class)
|
||||
"virtual std::unique_ptr<Base> Clone() const = 0;")
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class child-class)
|
||||
"std::unique_ptr<Base> Clone() const override {
|
||||
auto object = std::make_unique<Child>();
|
||||
object->int_member_ = int_member_;
|
||||
object->string_member_ = string_member_;
|
||||
object->another_int_member_ = another_int_member_;
|
||||
return object;
|
||||
}"))
|
||||
(undefine-cpp-types)
|
||||
;; :return-type and :init-object propagation
|
||||
(let ((base-class (lcp:define-class base ()
|
||||
((int-member :int32_t)
|
||||
(string-member "std::string"))
|
||||
(:abstractp t)
|
||||
(:clone :return-type (lambda (typename)
|
||||
(format nil "~A*" typename))
|
||||
:init-object (lambda (var typename)
|
||||
(format nil "~A* ~A = GlobalFactory::Create();"
|
||||
typename var)))))
|
||||
(child-class (lcp:define-class child (base)
|
||||
((another-int-member :int64_t))
|
||||
(:clone))))
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class base-class)
|
||||
"virtual Base *Clone() const = 0;")
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class child-class)
|
||||
"Child *Clone() const override {
|
||||
Child *object = GlobalFactory::Create();
|
||||
object->int_member_ = int_member_;
|
||||
object->string_member_ = string_member_;
|
||||
object->another_int_member_ = another_int_member_;
|
||||
return object;
|
||||
}"))
|
||||
(undefine-cpp-types)
|
||||
;; inheritance with :ignore-other-base-classes and :base
|
||||
(let ((base-class (lcp:define-class base ("utils::TotalOrdering")
|
||||
((int-member :int32_t)
|
||||
(string-member "std::string"))
|
||||
(:abstractp t)
|
||||
(:clone :base t
|
||||
:return-type (lambda (typename)
|
||||
(format nil "~A*" typename))
|
||||
:init-object (lambda (var typename)
|
||||
(format nil "~A* ~A = GlobalFactory::Create();"
|
||||
typename var)))))
|
||||
(child-class (lcp:define-class child (base "utils::TotalOrdering" "utils::TotalOrdering")
|
||||
((another-int-member :int64_t))
|
||||
(:clone :ignore-other-base-classes t))))
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class base-class)
|
||||
"virtual Base *Clone() const = 0;")
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class child-class)
|
||||
"Child *Clone() const override {
|
||||
Child *object = GlobalFactory::Create();
|
||||
object->int_member_ = int_member_;
|
||||
object->string_member_ = string_member_;
|
||||
object->another_int_member_ = another_int_member_;
|
||||
return object;
|
||||
}")))
|
||||
(subtest "extra args"
|
||||
(undefine-cpp-types)
|
||||
;; extra arguments are always passed when calling `Clone` function
|
||||
(let ((expression-class (lcp:define-class expression ()
|
||||
((lhs "Expression *")
|
||||
(rhs "Expression *"))
|
||||
(:abstractp t)
|
||||
(:clone :return-type (lambda (typename)
|
||||
(format nil "~A*" typename))
|
||||
:init-object (lambda (var typename)
|
||||
(format nil "~A* ~A = storage->Create<~A>();"
|
||||
typename var typename))
|
||||
:args '((storage "ExpressionStorage *")))))
|
||||
(and-class (lcp:define-class and (expression)
|
||||
()
|
||||
(:clone)))
|
||||
(or-class (lcp:define-class or (expression)
|
||||
()
|
||||
(:clone)))
|
||||
(filter-class (lcp:define-class filter ()
|
||||
((expressions "std::vector<Expression *>"))
|
||||
(:clone :args '((exp-storage "ExpressionStorage *"))))))
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class expression-class)
|
||||
"virtual Expression *Clone(ExpressionStorage *storage) const = 0;")
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class and-class)
|
||||
"And *Clone(ExpressionStorage *storage) const override {
|
||||
And *object = storage->Create<And>();
|
||||
object->lhs_ = lhs_ ? lhs_->Clone(storage) : nullptr;
|
||||
object->rhs_ = rhs_ ? rhs_->Clone(storage) : nullptr;
|
||||
return object;
|
||||
}")
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class or-class)
|
||||
"Or *Clone(ExpressionStorage *storage) const override {
|
||||
Or *object = storage->Create<Or>();
|
||||
object->lhs_ = lhs_ ? lhs_->Clone(storage) : nullptr;
|
||||
object->rhs_ = rhs_ ? rhs_->Clone(storage) : nullptr;
|
||||
return object;
|
||||
}")
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class filter-class)
|
||||
"Filter Clone(ExpressionStorage *exp_storage) const {
|
||||
Filter object;
|
||||
object.expressions_.resize(expressions_.size());
|
||||
for (auto i1 = 0; i1 < expressions_.size(); ++i1) {
|
||||
object.expressions_[i1] =
|
||||
expressions_[i1] ? expressions_[i1]->Clone(exp_storage) : nullptr;
|
||||
}
|
||||
return object;
|
||||
}")))
|
||||
(subtest "unsupported"
|
||||
;; multiple inheritance
|
||||
(undefine-cpp-types)
|
||||
(lcp:define-class first-base ()
|
||||
((int-member :int32_t))
|
||||
(:clone))
|
||||
(lcp:define-class second-base ()
|
||||
((private-member :int32_t :scope :private))
|
||||
(:clone))
|
||||
(let ((child-class (lcp:define-class child (first-base second-base)
|
||||
((name "std::string"))
|
||||
(:clone))))
|
||||
(is-error (lcp.clone:clone-function-definition-for-class child-class)
|
||||
'lcp.clone:clone-error))
|
||||
;; template classes
|
||||
(undefine-cpp-types)
|
||||
(let ((container-class (lcp:define-class (my-container t-element) ()
|
||||
((data "TElement *")
|
||||
(size "size_t")))))
|
||||
(is-error (lcp.clone:clone-function-definition-for-class container-class)
|
||||
'lcp.clone:clone-error)))
|
||||
(subtest "custom clone"
|
||||
(undefine-cpp-types)
|
||||
(let ((my-class (lcp:define-class my-class ()
|
||||
((callback "std::function<void(int, int)>" :clone :copy)
|
||||
(click-counter :int32_t :clone nil)
|
||||
(widget "Widget"
|
||||
:clone (lambda (source dest)
|
||||
#>cpp
|
||||
${dest} = WidgetFactory::Create(${source}.type());
|
||||
cpp<#)))
|
||||
(:clone))))
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class my-class)
|
||||
"MyClass Clone() const {
|
||||
MyClass object;
|
||||
object.callback_ = callback_;
|
||||
object.widget_ = WidgetFactory::Create(widget_.type());
|
||||
return object;
|
||||
}")))
|
||||
(subtest "types"
|
||||
(undefine-cpp-types)
|
||||
(macrolet ((single-member-test (member expected)
|
||||
(let ((class-sym (gensym)))
|
||||
`(let ((,class-sym (lcp:define-class my-class ()
|
||||
(,member)
|
||||
(:clone))))
|
||||
(is-generated (lcp.clone:clone-function-definition-for-class ,class-sym)
|
||||
(format nil "MyClass Clone() const {
|
||||
MyClass object;
|
||||
~A
|
||||
return object;
|
||||
}"
|
||||
,expected))))))
|
||||
(lcp:define-class klondike ()
|
||||
()
|
||||
(:clone))
|
||||
(subtest "vector"
|
||||
(single-member-test (member "std::vector<int32_t>")
|
||||
"object.member_ = member_;")
|
||||
(single-member-test (member "std::vector<std::vector<int32_t>>")
|
||||
"object.member_ = member_;")
|
||||
(single-member-test (member "std::vector<Klondike>")
|
||||
"object.member_.resize(member_.size());
|
||||
for (auto i1 = 0; i1 < member_.size(); ++i1) {
|
||||
object.member_[i1] = member_[i1].Clone();
|
||||
}")
|
||||
(single-member-test (member "std::vector<std::vector<Klondike>>")
|
||||
"object.member_.resize(member_.size());
|
||||
for (auto i1 = 0; i1 < member_.size(); ++i1) {
|
||||
object.member_[i1].resize(member_[i1].size());
|
||||
for (auto i2 = 0; i2 < member_[i1].size(); ++i2) {
|
||||
object.member_[i1][i2] = member_[i1][i2].Clone();
|
||||
}
|
||||
}"))
|
||||
(subtest "optional"
|
||||
(single-member-test (member "std::experimental::optional<int32_t>")
|
||||
"object.member_ = member_;")
|
||||
(single-member-test (member "std::experimental::optional<Klondike>")
|
||||
"if (member_) {
|
||||
Klondike value1;
|
||||
value1 = (*member_).Clone();
|
||||
object.member_.emplace(std::move(value1));
|
||||
} else {
|
||||
object.member_ = std::experimental::nullopt;
|
||||
}"))
|
||||
(subtest "unordered_map"
|
||||
(single-member-test (member "std::unordered_map<int32_t, std::string>")
|
||||
"object.member_ = member_;")
|
||||
(single-member-test (member "std::unordered_map<int32_t, std::unordered_map<int32_t, std::string>>")
|
||||
"object.member_ = member_;")
|
||||
(single-member-test (member "std::unordered_map<int32_t, Klondike>")
|
||||
"for (const auto &kv1 : member_) {
|
||||
std::pair<int32_t, Klondike> entry1;
|
||||
{
|
||||
int32_t first2;
|
||||
first2 = kv1.first;
|
||||
Klondike second2;
|
||||
second2 = kv1.second.Clone();
|
||||
entry1 = std::make_pair(std::move(first2), std::move(second2));
|
||||
}
|
||||
|
||||
object.member_.emplace(std::move(entry1));
|
||||
}")
|
||||
(single-member-test (member "std::unordered_map<int32_t, std::unordered_map<int32_t, Klondike>>")
|
||||
"for (const auto &kv1 : member_) {
|
||||
std::pair<int32_t, std::unordered_map<int32_t, Klondike>> entry1;
|
||||
{
|
||||
int32_t first2;
|
||||
first2 = kv1.first;
|
||||
std::unordered_map<int32_t, Klondike> second2;
|
||||
for (const auto &kv3 : kv1.second) {
|
||||
std::pair<int32_t, Klondike> entry3;
|
||||
{
|
||||
int32_t first4;
|
||||
first4 = kv3.first;
|
||||
Klondike second4;
|
||||
second4 = kv3.second.Clone();
|
||||
entry3 = std::make_pair(std::move(first4), std::move(second4));
|
||||
}
|
||||
|
||||
second2.emplace(std::move(entry3));
|
||||
}
|
||||
entry1 = std::make_pair(std::move(first2), std::move(second2));
|
||||
|
||||
}
|
||||
|
||||
object.member_.emplace(std::move(entry1));
|
||||
}")
|
||||
(single-member-test (member "std::unordered_map<Klondike, Klondike>")
|
||||
"for (const auto &kv1 : member_) {
|
||||
std::pair<Klondike, Klondike> entry1;
|
||||
{
|
||||
Klondike first2;
|
||||
first2 = kv1.first.Clone();
|
||||
Klondike second2;
|
||||
second2 = kv1.second.Clone();
|
||||
entry1 = std::make_pair(std::move(first2), std::move(second2));
|
||||
}
|
||||
|
||||
object.member_.emplace(std::move(entry1));
|
||||
}"))
|
||||
(subtest "pair"
|
||||
(single-member-test (member "std::pair<int32_t, int32_t>")
|
||||
"object.member_ = member_;")
|
||||
(single-member-test (member "std::pair<int32_t, Klondike>")
|
||||
"{
|
||||
int32_t first1;
|
||||
first1 = member_.first;
|
||||
Klondike second1;
|
||||
second1 = member_.second.Clone();
|
||||
object.member_ = std::make_pair(std::move(first1), std::move(second1));
|
||||
}")
|
||||
(single-member-test (member "std::pair<Klondike, int32_t>")
|
||||
"{
|
||||
Klondike first1;
|
||||
first1 = member_.first.Clone();
|
||||
int32_t second1;
|
||||
second1 = member_.second;
|
||||
object.member_ = std::make_pair(std::move(first1), std::move(second1));
|
||||
}")
|
||||
(single-member-test (member "std::pair<Klondike, Klondike>")
|
||||
"{
|
||||
Klondike first1;
|
||||
first1 = member_.first.Clone();
|
||||
Klondike second1;
|
||||
second1 = member_.second.Clone();
|
||||
object.member_ = std::make_pair(std::move(first1), std::move(second1));
|
||||
}")
|
||||
(single-member-test (member "std::pair<std::string, std::pair<int32_t, Klondike>>")
|
||||
"{
|
||||
std::string first1;
|
||||
first1 = member_.first;
|
||||
std::pair<int32_t, Klondike> second1;
|
||||
{
|
||||
int32_t first2;
|
||||
first2 = member_.second.first;
|
||||
Klondike second2;
|
||||
second2 = member_.second.second.Clone();
|
||||
second1 = std::make_pair(std::move(first2), std::move(second2));
|
||||
}
|
||||
|
||||
object.member_ = std::make_pair(std::move(first1), std::move(second1));
|
||||
}")
|
||||
)
|
||||
(subtest "pointers"
|
||||
(single-member-test (member "Klondike *")
|
||||
"object.member_ = member_ ? member_->Clone() : nullptr;")
|
||||
(single-member-test (member "std::unique_ptr<Klondike>")
|
||||
"object.member_ = member_ ? member_->Clone() : nullptr;")
|
||||
(single-member-test (member "std::shared_ptr<Klondike>")
|
||||
"object.member_ = member_ ? member_->Clone() : nullptr;"))
|
||||
(subtest "enum"
|
||||
(lcp:define-enum enum '(val1 val2 val3))
|
||||
(single-member-test (member "Enum")
|
||||
"object.member_ = member_;"))
|
||||
(subtest "builtin c++ types"
|
||||
(single-member-test (member :int32_t)
|
||||
"object.member_ = member_;")
|
||||
(single-member-test (member :char)
|
||||
"object.member_ = member_;")))))
|
||||
|
@ -8,6 +8,7 @@
|
||||
(:file "types")
|
||||
(:file "code-gen")
|
||||
(:file "slk")
|
||||
(:file "clone")
|
||||
(:file "lcp"))
|
||||
:in-order-to ((test-op (test-op "lcp/test"))))
|
||||
|
||||
|
@ -96,7 +96,9 @@ NIL, returns a string."
|
||||
(format s "~%~{~A~%~}" (mapcar #'cpp-code (cpp-class-public cpp-class)))
|
||||
(format s "~{~%~A~}~%" (mapcar #'cpp-member-reader-definition reader-members))
|
||||
(format s "~{ ~%~A~}~%"
|
||||
(mapcar #'member-declaration (cpp-class-members-scoped :public)))))
|
||||
(mapcar #'member-declaration (cpp-class-members-scoped :public)))
|
||||
(when (cpp-class-clone-opts cpp-class)
|
||||
(format s "~%~A" (lcp.clone:clone-function-definition-for-class cpp-class)))))
|
||||
(when (or (cpp-class-protected cpp-class) (cpp-class-members-scoped :protected))
|
||||
(write-line " protected:" s)
|
||||
(format s "~{~A~%~}" (mapcar #'cpp-code (cpp-class-protected cpp-class)))
|
||||
@ -144,7 +146,7 @@ the function. TYPE-PARAMS is a list of names for template argments"
|
||||
|
||||
(defun cpp-method-declaration (class method-name
|
||||
&key args (returns "void") (inline t) static
|
||||
virtual const override)
|
||||
virtual const override delete)
|
||||
"Generate a C++ method declaration as a string for the given METHOD-NAME on
|
||||
CLASS. ARGS is a list of (variable type) arguments to method. RETURNS is the
|
||||
return type of the function. When INLINE is set to NIL, generates a
|
||||
@ -165,11 +167,12 @@ which generate the corresponding C++ keywords."
|
||||
(cpp-variable-name (first name-and-type))))
|
||||
args)))
|
||||
(const (if const "const" ""))
|
||||
(override (if (and override inline) "override" "")))
|
||||
(override (if (and override inline) "override" ""))
|
||||
(delete (if delete "= 0" "")))
|
||||
(raw-cpp-string
|
||||
#>cpp
|
||||
${template} ${static/virtual}
|
||||
${returns} ${namespace}${method-name}(${args}) ${const} ${override}
|
||||
${returns} ${namespace}${method-name}(${args}) ${const} ${override} ${delete}
|
||||
cpp<#)))
|
||||
|
||||
(defstruct cpp-list
|
||||
|
@ -32,3 +32,8 @@
|
||||
#:save-function-definition-for-enum
|
||||
#:load-function-declaration-for-enum
|
||||
#:load-function-definition-for-enum))
|
||||
|
||||
(defpackage #:lcp.clone
|
||||
(:use #:cl)
|
||||
(:export #:clone-error
|
||||
#:clone-function-definition-for-class))
|
||||
|
@ -105,18 +105,6 @@ generation expects the declarations and definitions to be in `slk` namespace."
|
||||
"Load" :args (cons self-arg (cons reader-arg (load-extra-args cpp-class)))
|
||||
:type-params (lcp::cpp-type-type-params cpp-class))))
|
||||
|
||||
(defun cpp-type-pointer-p (cpp-type)
|
||||
(check-type cpp-type (or lcp::cpp-type string lcp::cpp-primitive-type-keywords))
|
||||
(typecase cpp-type
|
||||
(string (cpp-type-pointer-p (lcp::parse-cpp-type-declaration cpp-type)))
|
||||
(lcp::cpp-type
|
||||
(or
|
||||
(string= "*" (lcp::cpp-type-name cpp-type))
|
||||
(string= "shared_ptr" (lcp::cpp-type-name cpp-type))
|
||||
;; Note, we could forward to default slk::Load for unique_ptr and hope
|
||||
;; everything is alright w.r.t to inheritance.
|
||||
(string= "unique_ptr" (lcp::cpp-type-name cpp-type))))))
|
||||
|
||||
(defun save-members (cpp-class)
|
||||
"Generate code for saving members of CPP-CLASS. Raise `SLK-ERROR' if the
|
||||
serializable member has no public access."
|
||||
@ -134,7 +122,7 @@ serializable member has no public access."
|
||||
member-name))
|
||||
s)))
|
||||
;; TODO: Maybe support saving (but not loading) unique_ptr.
|
||||
((cpp-type-pointer-p (lcp::cpp-member-type member))
|
||||
((lcp::cpp-pointer-type-p (lcp::cpp-member-type member))
|
||||
(slk-error "Don't know how to save pointer '~A' in '~A'"
|
||||
(lcp::cpp-member-type member)
|
||||
(lcp::cpp-type-base-name cpp-class)))
|
||||
@ -164,7 +152,7 @@ serializable member has no public access."
|
||||
(write-line (lcp::cpp-code (funcall (lcp::cpp-member-slk-load member)
|
||||
member-name))
|
||||
s)))
|
||||
((cpp-type-pointer-p (lcp::cpp-member-type member))
|
||||
((lcp::cpp-pointer-type-p (lcp::cpp-member-type member))
|
||||
(slk-error "Don't know how to load pointer '~A' in '~A'"
|
||||
(lcp::cpp-member-type member)
|
||||
(lcp::cpp-type-base-name cpp-class)))
|
||||
|
@ -139,7 +139,8 @@
|
||||
;; May be a function which takes 1 argument, member-name. It needs to
|
||||
;; return C++ code.
|
||||
(slk-save nil :type (or null function) :read-only t)
|
||||
(slk-load nil :type (or null function) :read-only t))
|
||||
(slk-load nil :type (or null function) :read-only t)
|
||||
(clone t :type (or boolean (eql :copy) function) :read-only t))
|
||||
|
||||
(defstruct capnp-opts
|
||||
"Cap'n Proto serialization options for C++ class."
|
||||
@ -171,6 +172,15 @@
|
||||
;; In case of multiple inheritance, pretend we only inherit the 1st base class.
|
||||
(ignore-other-base-classes nil :type boolean :read-only t))
|
||||
|
||||
(defstruct clone-opts
|
||||
"Cloning options for C++ class."
|
||||
;; Extra arguments to the generated clone function. List of (name cpp-type).
|
||||
(args nil :read-only t)
|
||||
(return-type nil :type (or null function) :read-only t)
|
||||
(base nil :read-only t)
|
||||
(ignore-other-base-classes nil :read-only t)
|
||||
(init-object nil :read-only t))
|
||||
|
||||
(defclass cpp-class (cpp-type)
|
||||
((structp :type boolean :initarg :structp :initform nil
|
||||
:reader cpp-class-structp)
|
||||
@ -186,6 +196,8 @@
|
||||
:reader cpp-class-capnp-opts)
|
||||
(slk-opts :type (or null slk-opts) :initarg :slk-opts :initform nil
|
||||
:reader cpp-class-slk-opts)
|
||||
(clone-opts :type (or null clone-opts) :initarg :clone-opts :initform nil
|
||||
:reader cpp-class-clone-opts)
|
||||
(inner-types :initarg :inner-types :initform nil :reader cpp-class-inner-types)
|
||||
(abstractp :initarg :abstractp :initform nil :reader cpp-class-abstractp))
|
||||
(:documentation "Meta information on a C++ class (or struct)."))
|
||||
@ -270,6 +282,17 @@ documentation on `CPP-TYPE' members for function arguments."
|
||||
"Whether the C++ type designated by TYPE-DECL is a primitive type."
|
||||
(typep (cpp-type type-decl) 'cpp-primitive-type))
|
||||
|
||||
(defun cpp-pointer-type-p (type-decl)
|
||||
"Whether the C++ type designated by TYPE-DECL is a smart or raw pointer type."
|
||||
(check-type type-decl (or lcp::cpp-type string lcp::cpp-primitive-type-keywords))
|
||||
(typecase type-decl
|
||||
(string (cpp-pointer-type-p (lcp::parse-cpp-type-declaration type-decl)))
|
||||
(lcp::cpp-type
|
||||
(or
|
||||
(string= "*" (lcp::cpp-type-name type-decl))
|
||||
(string= "shared_ptr" (lcp::cpp-type-name type-decl))
|
||||
(string= "unique_ptr" (lcp::cpp-type-name type-decl))))))
|
||||
|
||||
(defun parse-cpp-type-declaration (type-decl)
|
||||
"Parse C++ type from TYPE-DECL string and return CPP-TYPE.
|
||||
|
||||
@ -579,6 +602,8 @@ Generates C++:
|
||||
`(make-capnp-opts ,@(cdr (assoc :capnp serialize))))
|
||||
:slk-opts ,(when (assoc :slk serialize)
|
||||
`(make-slk-opts ,@(cdr (assoc :slk serialize))))
|
||||
:clone-opts ,(when (assoc :clone options)
|
||||
`(make-clone-opts ,@(cdr (assoc :clone options))))
|
||||
:abstractp ,abstractp
|
||||
:namespace (reverse *cpp-namespaces*)
|
||||
;; Set inner types at the end. This works
|
||||
|
Loading…
Reference in New Issue
Block a user