immer icon indicating copy to clipboard operation
immer copied to clipboard

Add constructor for box<T> from box<Y> for compatible Y

Open KholdStare opened this issue 4 years ago • 3 comments

Similar to how https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr has a constructor (9) that participates in overload resolution if Y* is compatible with T*, that can work for box<T>.

E.g. something like:

struct A
{
  int x;
};

struct B : A
{
  int y;
};

void example()
{
  immer::box<B> b;
  immer::box<A> a{b}; // should work
}

I don't think there should be concerns with object slicing since the values held are immutable and are not copied/moved by immer. I think implementation-wise this would mean a virtual function or some function pointer in the MemoryPolicy::refcount class that does deletion. What do you think?

KholdStare avatar Aug 25 '20 15:08 KholdStare

Hmmm, I find the suggestion interesting, but I'm not sure supporting sharing in this case (instead of slicing) justifies the added complexity. In any case due to immutability the two cases (slicing or sharing) should be semantically equivalent.

If we go down this road, note that you are most probably using inheritance for composition. One could also consider this other use-case, which is actually supported by shared ptr as well:

struct A { ... };

struct B 
{
    A member;
};

void example()
{
    immer::box<B> b;
    immer::box<A> a{b.member, b};
}

arximboldi avatar Aug 25 '20 15:08 arximboldi

I see what you mean. I'm going to give an extremely contrived example of my usecase:

struct Product
{
  int id;
};

struct Hat : Product
{
  Color color;
};

struct CustomerOrder
{
  immer::box<Product> product;
};

We're writing a HatStore and we know all our products are going to be Hats. In order to avoid excessive templates, I'm keeping CustomerOrder as is, and don't want to have CustomerOrder<Hat>. Some component will want to look at box::immer<Hat> at some point so there will need to be some downcasting involved from Product to Hat.

Without downcasting, sharing and slicing would be semantically equivalent, I agree. Here I was trying to avoid putting more templates than necessary, and that involves "knowledge outside the type system" that all Products happen to be Hats for this particular application.

KholdStare avatar Aug 25 '20 16:08 KholdStare

Hi!

Sorry for the late reply, I was on vacation :)

I see, that is a bit of an unorthodox design, I am unsure yet what the full consequences of that would be (How do you discriminate the types for downcasting? Is there a place where you can query any product? etc.).

Normally you would just store the id in the customer order, and somewhere else in your program you have a immer::map<id, Hat> for hats that you pass around when you need to use hats. (Alternativelly, you can use Product = variant<Hat, ...> and put that in the map).

If this is for a commercial project, send me an email, we can maybe arrange a contract to develop this feature in the library, but maybe I can help you review the design to avoid these pitfalls.

arximboldi avatar Sep 05 '20 12:09 arximboldi