Skip to content

ExprContainer with value semantics - #592

Draft
Krzmbrzl wants to merge 49 commits into
ValeevGroup:masterfrom
Krzmbrzl:value-like-expr-container
Draft

ExprContainer with value semantics#592
Krzmbrzl wants to merge 49 commits into
ValeevGroup:masterfrom
Krzmbrzl:value-like-expr-container

Conversation

@Krzmbrzl

Copy link
Copy Markdown
Collaborator

This is supposed to be an alternative (longer-term perhaps even replacement) for ExprPtr. It can be used to store expressions but contrary to ExprPtr, the new ExprContainer has value semantics. That is, copying the container actually copies the underlying expression. This makes things much easier to reason about and as a side-effect this fixes the const issue of ExprPtr which allows you to do

void func(const ExprPtr &ptr) {
  auto copy = ptr; // only copies _pointer_
  expand(copy);
}

which ends up modifying the original expression ptr was is pointing to. Hence, with ExprPtr we don't have any way to avoid accidental modification.


Note: This PR is based on top of #589

They aren't universally supported by all expression subclasses.
Therefore, them being defined in the general Expr API doesn't make too
much sense. It's better to be notified of a missing operator via a
compiler error than via a runtime exception.
This avoids situations in which important functions are not implemented
for a given expression type (as was the case with
NormalOperatorSequence). Thus, this gives a compiler-enforced guarantee
that these functions will not just remain at the (useless) base
implementations that just throw.
Since C++11 std::swap will use move semantics so this custom swap impl
doesn't get us any benefit.
The new function returns a unique_ptr<Expr> instead of an ExprPtr.
Reason being that this gives much more flexibility such as moving the
ownership of the object out of the smart pointer or simply using as a
unique_ptr. Since unique_ptr is implicitly convertible to a shared_ptr
(via move ctor), conversion to ExprPtr is trivially possible.

To retain compatibility with existing interface, Expr now implements a
clone() function by means of the new unique_copy().
This is in the way of having expression objects that are not managed by
shared_ptr
@evaleev

evaleev commented Aug 18, 2026

Copy link
Copy Markdown
Member

@Krzmbrzl would it make sense to do copy-on-mutable-access to save on copying in trivial cases?

@Krzmbrzl

Krzmbrzl commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

would it make sense to do copy-on-mutable-access to save on copying in trivial cases?

I don't have experience implementing something like this but it seems like that would significantly complicate the implementation, no? Also, I would really like to not give up on the implicit conversion to (const) Expr &(&) which is what almost all operations act on. Having a copy-on-write seems like it would require introducing something like ExprContainer::to_expr() and Expr::to_mutable_expr() where the former returns const Expr & and the latter returns a copy (which would then still have to be stored in its own container again) 🤔

EDIT: Or are you strictly speaking about the copies performed during construction of ExprContainer instances themselves?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants