Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions std/typecons.d
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,17 @@ $(D T) to deallocate or clean up any non-GC resources.
If it is desirable to persist a $(D Unique!T) outside of its original
scope, then it can be transferred. The transfer can be explicit, by
calling $(D release), or implicit, when returning Unique from a
function. The resource $(D T) can be a polymorphic class object, in
which case Unique behaves polymorphically too.
function. The resource $(D T) can be a polymorphic class object or
instance of an interface, in which case Unique behaves polymorphically
too.

If $(D T) is a value type, then $(D Unique!T) will be implemented
as a reference to a $(D T).
*/
struct Unique(T)
{
/** Represents a reference to $(D T). Resolves to $(D T*) if $(D T) is a value type. */
static if (is(T:Object))
static if (is(T == class) || is(T == interface))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work with extern(C++) or extern(Windows) classes & interfaces? If not we should not allow them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZombineDev Good luck detecting them. This is something that @bbasile has been working on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know about extern(Windows) - isn't that similar to extern(C) which wouldn't work with classes (I really haven't done much with Windows and D, and Windows linking is way more complicated in general than *nix)? However, as for something like extern(C++) or COM interfaces, wouldn't checking

is(T : Object) && (is(T == class) || is(T == interface))

do the trick?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is, if the idea is to exclude them anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmdavis the problem is that Object is a class, so if T is an interface it won't extend Object.

Copy link
Contributor Author

@tmccombs tmccombs Mar 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know a lot about extern(c++) or extern(Windows), but from reading http://dlang.org/spec/interfaceToC.html, it seems like extern(Windows) isn't really relevant since it is more about function calling conventions, and from http://dlang.org/spec/cpp_interface.html it seems like this might work with C++ classes. Although it isn't really clear if it is safe to call destroy on a C++ class. Of course, Unique certainly won't clean up the memory for a C++ class, but that is true regardless of if RefT is T or T*

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this shouldn't be stalled on whether it works with extern(C++)/extern(Windows) classes & interfaces. If they didn't work before then this PR doesn't change the status quo.

alias RefT = T;
else
alias RefT = T*;
Expand Down Expand Up @@ -280,6 +281,51 @@ private:
assert(!ub2.isEmpty);
}

@system unittest
{
debug(Unique) writeln("Unique interface");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW: @JackStouffer has been working on getting rid of these superfluous debug statements

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good. I was going to request that they be removed but it's easy to submit a PR removing them all at once so I didn't bother.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it seems it was common style in this module

interface Bar
{
int val() const;
}
class BarImpl : Bar
{
static int count;
this()
{
count++;
}
~this()
{
count--;
}
int val() const { return 4; };
}
alias UBar = Unique!Bar;
UBar g(UBar u)
{
debug(Unique) writeln("inside g");
return u.release;
}
void consume(UBar u)
{
assert(u.val() == 4);
// Resource automatically deleted here
}
auto ub = UBar(new BarImpl);
assert(BarImpl.count == 1);
assert(!ub.isEmpty);
assert(ub.val == 4);
static assert(!__traits(compiles, {auto ub3 = g(ub);}));
debug(Unique) writeln("Calling g");
auto ub2 = g(ub.release);
debug(Unique) writeln("Returned from g");
assert(ub.isEmpty);
assert(!ub2.isEmpty);
consume(ub2.release);
assert(BarImpl.count == 0);
}

@system unittest
{
debug(Unique) writeln("Unique struct");
Expand Down