www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Safely wrapping an uncopyable struct to implement an interface

reply Gregor =?UTF-8?B?TcO8Y2ts?= <gregormueckl gmx.de> writes:
Hi!

I've just created a situation in my code that is summarized by 
the following example. I don't know how to solve it with  safe 
code.

A third party library provides a struct that is not copyable:

// provided by third party
struct Foo {
      disable this()  safe;
      disable this(ref return scope Foo other)  safe;

     void magic()  safe;
}

What I want to do is to provide a safe wrapper around it that 
adapts to another interface:

// intended common interface
interface IWrapper {
     void bar()  safe;
}

Now, the obvious way to wrap this fails:

class FooWrapper : IWrapper {
     Foo f;

     this(Foo f)  safe {
         this.f = f; // this fails because it would be a copy
     }

     override void bar()  safe
     {
         f.magic();
     }
}

If Foo were a class, f would be a reference and everything would 
be fine. But f is a struct that can't be copied and taking a 
pointer to f makes FooWrapper obviously unsafe. How could I solve 
this?

I've come up with a workaround for my actual use case that 
doesn't need to use the uncopyable struct this way. But I'm 
curious if I'm missing something regarding references to structs.
Mar 04 2020
parent reply aliak <something something.com> writes:
On Wednesday, 4 March 2020 at 12:03:48 UTC, Gregor Mückl wrote:
 Hi!

 I've just created a situation in my code that is summarized by 
 the following example. I don't know how to solve it with  safe 
 code.

 A third party library provides a struct that is not copyable:

 // provided by third party
 struct Foo {
      disable this()  safe;
      disable this(ref return scope Foo other)  safe;

     void magic()  safe;
 }

 What I want to do is to provide a safe wrapper around it that 
 adapts to another interface:

 // intended common interface
 interface IWrapper {
     void bar()  safe;
 }

 Now, the obvious way to wrap this fails:

 class FooWrapper : IWrapper {
     Foo f;

     this(Foo f)  safe {
         this.f = f; // this fails because it would be a copy
     }

     override void bar()  safe
     {
         f.magic();
     }
 }

 If Foo were a class, f would be a reference and everything 
 would be fine. But f is a struct that can't be copied and 
 taking a pointer to f makes FooWrapper obviously unsafe. How 
 could I solve this?

 I've come up with a workaround for my actual use case that 
 doesn't need to use the uncopyable struct this way. But I'm 
 curious if I'm missing something regarding references to 
 structs.
You can use move maybe? : https://dlang.org/library/std/algorithm/mutation/move.html So this.f = f.move; But you should be aware that it could cause problems when f has pointers to its internals. I.e. if Foo had a pointer to it's own member then the value of of that pointer to me "copied" over to this.f, but the address of the member in this.f is different that the original f's member address.
Mar 04 2020
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 3/4/20 9:04 AM, aliak wrote:
 On Wednesday, 4 March 2020 at 12:03:48 UTC, Gregor Mückl wrote:
 Hi!

 I've just created a situation in my code that is summarized by the 
 following example. I don't know how to solve it with  safe code.

 A third party library provides a struct that is not copyable:

 // provided by third party
 struct Foo {
      disable this()  safe;
      disable this(ref return scope Foo other)  safe;

     void magic()  safe;
 }

 What I want to do is to provide a safe wrapper around it that adapts 
 to another interface:

 // intended common interface
 interface IWrapper {
     void bar()  safe;
 }

 Now, the obvious way to wrap this fails:

 class FooWrapper : IWrapper {
     Foo f;

     this(Foo f)  safe {
         this.f = f; // this fails because it would be a copy
     }

     override void bar()  safe
     {
         f.magic();
     }
 }

 If Foo were a class, f would be a reference and everything would be 
 fine. But f is a struct that can't be copied and taking a pointer to f 
 makes FooWrapper obviously unsafe. How could I solve this?

 I've come up with a workaround for my actual use case that doesn't 
 need to use the uncopyable struct this way. But I'm curious if I'm 
 missing something regarding references to structs.
You can use move maybe? : https://dlang.org/library/std/algorithm/mutation/move.html So this.f = f.move;
In addition, you must call the constructor with either an rvalue or your own move call. In other words, you need to do new FooWrapper(f.move); Alternatively, you can take the parameter via ref, but that might not be what you wish for. In general, when you use non-copyable structs, you will need to use move.
 But you should be aware that it could cause problems when f has pointers 
 to its internals. I.e. if Foo had a pointer to it's own member then the 
 value of of that pointer to me "copied" over to this.f, but the address 
 of the member in this.f is different that the original f's member address.
In general, you should not worry about this as it's highly unusual in D, and it's generally accepted that one can always move structs without issues (the compiler can do this without you asking it to). However, in practice it does happen, sometimes not intentionally. I think there's a function somewhere that checks to see if a type is pointing at itself, but can't find it right now. -Steve
Mar 04 2020