www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Opaque structs

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Traditionally using opaque structs in D when interfacing with C (where
one should only ever use such structs with a pointer) are implemented
like so:

-----
struct S;

void main()
{
    S* s1;  // ok
    S s2;  // linker error
}
-----

Unfortunately this tends to spawn unreadable error messages:

-----
test.d(3): Error: struct test.S unknown size
test.d(3): Error: struct test.S no size yet for forward reference
test.d(3): Error: struct test.S unknown size
test.d(3): Error: struct test.S no size yet for forward reference
test.d(12): Error: variable test.main.s2 no definition of struct S
test.d(3): Error: struct test.S unknown size
test.d(3): Error: struct test.S no size yet for forward reference
-----

I was thinking we could also implement opaque structs like so:

-----
struct S
{
     disable this();
     disable this(this);
}

void main()
{
    S* s1;
    S s2;
}
-----

The error is then:

-----
Error: variable test.main.s2 initializer required for type S
-----

The question is, is a disabled ctor and postblit enough?

Note that if we implement Issue 8728[1], we could even create a better
error message via:

-----
struct S
{
     disable("S is an opaque C type and must only be used as a pointer")
    this();

     disable("S is an opaque C type and must only be used as a pointer")
    this(this);
}

void main()
{
    S* s1;  // ok
    S s2;  // user error
}
-----

[1] : http://d.puremagic.com/issues/show_bug.cgi?id=8728
Jun 27 2013
next sibling parent reply "Brad Anderson" <eco gnuk.net> writes:
On Friday, 28 June 2013 at 01:40:44 UTC, Andrej Mitrovic wrote:
 Note that if we implement Issue 8728[1], we could even create a 
 better
 error message via:

 -----
 struct S
 {
      disable("S is an opaque C type and must only be used as a 
 pointer")
     this();

      disable("S is an opaque C type and must only be used as a 
 pointer")
     this(this);
 }

 void main()
 {
     S* s1;  // ok
     S s2;  // user error
 }
 -----

 [1] : http://d.puremagic.com/issues/show_bug.cgi?id=8728
+1. Anything that makes error messages clearer is a win in my book and there is precedents for it in deprecate(msg) which was a clear win.
Jun 27 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 28 June 2013 at 02:17:06 UTC, Brad Anderson wrote:
 On Friday, 28 June 2013 at 01:40:44 UTC, Andrej Mitrovic wrote:
 Note that if we implement Issue 8728[1], we could even create 
 a better
 error message via:

 -----
 struct S
 {
     disable("S is an opaque C type and must only be used as a 
 pointer")
    this();

     disable("S is an opaque C type and must only be used as a 
 pointer")
    this(this);
 }

 void main()
 {
    S* s1;  // ok
    S s2;  // user error
 }
 -----

 [1] : http://d.puremagic.com/issues/show_bug.cgi?id=8728
+1. Anything that makes error messages clearer is a win in my book and there is precedents for it in deprecate(msg) which was a clear win.
+1 also. I was going to say "deprecated does it that way, so should disable", but that's already in the ticket ^^
Jun 28 2013
prev sibling parent reply Johannes Pfau <nospam example.com> writes:
Am Fri, 28 Jun 2013 03:40:31 +0200
schrieb Andrej Mitrovic <andrej.mitrovich gmail.com>:

 struct S
 {
      disable("S is an opaque C type and must only be used as a
 pointer") this();
 
      disable("S is an opaque C type and must only be used as a
 pointer") this(this);
 }
A naive question: Why isn't struct S {} enough? This should be a struct with size 0 so why do we need to disable the constructor and postblit explicitly?
Jun 28 2013
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/28/13, Johannes Pfau <nospam example.com> wrote:
 A naive question: Why isn't struct S {} enough? This should be a struct
 with size 0 so why do we need to disable the constructor and postblit
 explicitly?
Because the user should never be able to use such a struct by value, in other words a user might mistakenly write code such as: ----- struct S { } extern(C) S* get(); extern(C) void call(S*); void main() { S* s = getS(); S s2 = *s; // copies 1 byte call(&s2); // no telling what will happen on the C side, usually memory corruption + crash } -----
Jun 28 2013
parent reply Johannes Pfau <nospam example.com> writes:
Am Fri, 28 Jun 2013 22:16:33 +0200
schrieb Andrej Mitrovic <andrej.mitrovich gmail.com>:

 On 6/28/13, Johannes Pfau <nospam example.com> wrote:
 A naive question: Why isn't struct S {} enough? This should be a
 struct with size 0 so why do we need to disable the constructor and
 postblit explicitly?
Because the user should never be able to use such a struct by value, in other words a user might mistakenly write code such as: S s2 = *s; // copies 1 byte
But why is that legal / does that copy _one_ byte? It seems like that's totally arbitrary. Shouldn't doing anything value-related on an empty struct be invalid anyway?
Jun 29 2013
next sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Saturday, 29 June 2013 at 08:01:17 UTC, Johannes Pfau wrote:
 Am Fri, 28 Jun 2013 22:16:33 +0200
 schrieb Andrej Mitrovic <andrej.mitrovich gmail.com>:

 On 6/28/13, Johannes Pfau <nospam example.com> wrote:
 A naive question: Why isn't struct S {} enough? This should 
 be a
 struct with size 0 so why do we need to disable the 
 constructor and
 postblit explicitly?
Because the user should never be able to use such a struct by value, in other words a user might mistakenly write code such as: S s2 = *s; // copies 1 byte
But why is that legal / does that copy _one_ byte? It seems like that's totally arbitrary. Shouldn't doing anything value-related on an empty struct be invalid anyway?
It copies one byte because empty structs have one byte - according to D implementation. The value can be adjusted using align() atrribute.
Jun 29 2013
parent reply Johannes Pfau <nospam example.com> writes:
Am Sat, 29 Jun 2013 10:54:32 +0200
schrieb "Maxim Fomin" <maxim maxim-fomin.ru>:

 On Saturday, 29 June 2013 at 08:01:17 UTC, Johannes Pfau wrote:
 Am Fri, 28 Jun 2013 22:16:33 +0200
 schrieb Andrej Mitrovic <andrej.mitrovich gmail.com>:

 On 6/28/13, Johannes Pfau <nospam example.com> wrote:
 A naive question: Why isn't struct S {} enough? This should 
 be a
 struct with size 0 so why do we need to disable the 
 constructor and
 postblit explicitly?
Because the user should never be able to use such a struct by value, in other words a user might mistakenly write code such as: S s2 = *s; // copies 1 byte
But why is that legal / does that copy _one_ byte? It seems like that's totally arbitrary. Shouldn't doing anything value-related on an empty struct be invalid anyway?
It copies one byte because empty structs have one byte - according to D implementation. The value can be adjusted using align() atrribute.
I see. I didn't know that we have this in the spec, but I guess there's some good reason for this behavior if it was explicitly specified / implemented.
Jun 29 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Saturday, 29 June 2013 at 12:58:51 UTC, Johannes Pfau wrote:
 Am Sat, 29 Jun 2013 10:54:32 +0200
 schrieb "Maxim Fomin" <maxim maxim-fomin.ru>:

 On Saturday, 29 June 2013 at 08:01:17 UTC, Johannes Pfau wrote:
 Am Fri, 28 Jun 2013 22:16:33 +0200
 schrieb Andrej Mitrovic <andrej.mitrovich gmail.com>:

 On 6/28/13, Johannes Pfau <nospam example.com> wrote:
 A naive question: Why isn't struct S {} enough? This 
 should be a
 struct with size 0 so why do we need to disable the 
 constructor and
 postblit explicitly?
Because the user should never be able to use such a struct by value, in other words a user might mistakenly write code such as: S s2 = *s; // copies 1 byte
But why is that legal / does that copy _one_ byte? It seems like that's totally arbitrary. Shouldn't doing anything value-related on an empty struct be invalid anyway?
It copies one byte because empty structs have one byte - according to D implementation. The value can be adjusted using align() atrribute.
I see. I didn't know that we have this in the spec, but I guess there's some good reason for this behavior if it was explicitly specified / implemented.
For the same reasons as in C/C++, "[they] require empty classes to have non-zero size to ensure object identity". For example, calculating the size of an array using: "size_t size = sizeof(arr) / sizeof(arr[0])" Requires the object's size to be non null. Iterating with: s* it = arr; s* it_end = arr + size; for ( ; it != it_end ; ++it ) {} Requires the objects to take up space.
Jun 29 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/29/13, Johannes Pfau <nospam example.com> wrote:
 Shouldn't doing anything value-related on
 an empty struct be invalid anyway?
Maybe, maybe not. I could imagine it would cause problems if we simply disallowed it, e.g. if you want to copy attributes from one declaration to another.
Jun 29 2013
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Saturday, 29 June 2013 at 08:01:17 UTC, Johannes Pfau wrote:
 Shouldn't doing anything value-related on
 an empty struct be invalid anyway?
Why ? The fact that the struct has no members is an implementation detail which should have no impact on the user of the struct.
Jun 29 2013
parent reply Johannes Pfau <nospam example.com> writes:
Am Sat, 29 Jun 2013 17:38:38 +0200
schrieb "monarch_dodra" <monarchdodra gmail.com>:

 On Saturday, 29 June 2013 at 08:01:17 UTC, Johannes Pfau wrote:
 Shouldn't doing anything value-related on
 an empty struct be invalid anyway?
Why ? The fact that the struct has no members is an implementation detail which should have no impact on the user of the struct.
It's probably a matter of perception. As you said in your other post there are good reasons to give empty structs a size. But if you (naively) think of a struct as a simple aggregate of other types, then a aggregate of zero other types should have size zero. There's no information in such a struct which would have to take up space. And doing something value-related on some type which doesn't have a size and therefore doesn't have a value is not really well-defined. (How do you copy a value of size 0? What happens if you dereference a pointer to a value of size 0?).
Jun 30 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 30 June 2013 at 08:18:39 UTC, Johannes Pfau wrote:
 Am Sat, 29 Jun 2013 17:38:38 +0200
 schrieb "monarch_dodra" <monarchdodra gmail.com>:

 On Saturday, 29 June 2013 at 08:01:17 UTC, Johannes Pfau wrote:
 Shouldn't doing anything value-related on
 an empty struct be invalid anyway?
Why ? The fact that the struct has no members is an implementation detail which should have no impact on the user of the struct.
It's probably a matter of perception. As you said in your other post there are good reasons to give empty structs a size. But if you (naively) think of a struct as a simple aggregate of other types, then a aggregate of zero other types should have size zero.
Well, *technically*, it should have a size of *at least* 0, since the compiler is allowed to add as much padding as it wishes (which it does).
 There's no
 information in such a struct which would have to take up space. 
 And
 doing something value-related on some type which doesn't have a 
 size
 and therefore doesn't have a value is not really well-defined. 
 (How do
 you copy a value of size 0? What happens if you dereference a 
 pointer
 to a value of size 0?).
Well, as you say, it is a matter of perception: From the client side, all the client should know is that the structs hold no "information", the actual *size*, is not his problem: EG, it is an empty "bag". The fact that the bag is empty though shouldn't prevent the client from having an array of bags, or to have pointers to the bag. If the client can't say "I created an S, which is a struct that holds no *data*, and this is it's address", then there is a problem, and it is the implementation's fault. The compiler works around that problem by giving the empty struct a size. It is a "dirty" way to do it, but it works :)
Jun 30 2013