www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Structs can't be zero bytes in size?

reply "Dylan Knutson" <tcdknutson gmail.com> writes:
Take a look at this:

http://dpaste.dzfl.pl/6bf578a3

```
struct Foo {}
pragma(msg, Foo.sizeof);
void main() {}
```

output: 1LU

It seems like structs can't have a size of zero, but this doesn't 
make a whole lot of sense semantically. I've currently got a 
project using `std.variant : Algebraic`, so a struct which simply 
acts as a distinct type would be useful to have.

Take, for example, a simple client-server payload passing 
implementation:

```
enum PayloadType {
		response_success,
		response_failure
}
alias Payload = Algebraic!(
	ResponseSuccess,
	ResponseFailure);

struct ResponseSuccess {
	static type = PayloadType.response_success;
}

struct ResponseFailure {
	static type = PayloadType.response_failure;

	uint code;
}


// Then pseudocode for how the structure is sent over a connection
void sendPayload(Resp)(Resp response, OutputStream conn) {
	conn.write(Resp.type);

	static if(Resp.sizeof) {
		// Always triggered, even for empty structs.
		conn.write(response);
	}
}

// And how it's recieved from a connection
Payload recievePayload(InputStream conn) {
	PayloadType type = conn.read!PayloadType();

	Payload ret;

	final switch(type) with(PayloadType) {
		case response_success:
			// Nothing actually needs to be read from the stream,
			// but 1 byte extra is written anyways because ResponseSuccess 
is 1 byte.
			ubyte throwaway = conn.read!ubyte();

			ret = ResponseSuccess();
			break;

		case response_failure:
			ResponseFailure failure = conn.read!ResponseFailure();
			ret = failure;
			break;
	}

	return ret;

}

// Usage:
auto conn = new InputOutputStream(); //Responds to .read() and 
.write()
conn.sendPayload(ReponseSuccess());

conn.recievePayload().visit!(
	(ResponseSuccess resp) {
		writeln("Success!");
	},
	(ResponseFailure resp) {
		writeln("Failure! Code: ", resp.code);
	}
)()
```

In the above example, if ResponseSuccess had a size of zero, 
there would be no additional network overhead. However, because 
the extra byte of `response` is written to the stream, one must 
read an empty byte on the recieving end, and throw it away. In 
this case, it accounts for 33% of the network overhead for 
transferring a ResponseSuccess structure. 2 bytes for the type of 
struct, and then 1 additional empty byte... just because. Not to 
mention that it's not intuitive with how structures normally 
work. According to individuals in the IRC channel, this is 
probably due to the implicit void in structs. Can someone shed 
some light on this?
Sep 02 2013
next sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
My current workaround is to use `Foo.tupleof.length == 0` to 
detect if a struct has size. But, as you can imagine, this is 
neither intuitive or optimal.
Sep 02 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 9/3/13, Dylan Knutson <tcdknutson gmail.com> wrote:
 Take a look at this:

 http://dpaste.dzfl.pl/6bf578a3

I suggest: ----- void sendPayload(Resp)(Resp response) { static if(Resp.sizeof) { pragma(msg, "has a size"); } else { pragma(msg, "has no size"); } } void main() { sendPayload(cast(void[0])[]); } ----- So try to use `void[0]` as a type. When you have such a type, you can initialize it with [], for example: ----- void[0][int] hash; void main() { hash[1] = []; assert(1 in hash); } -----
Sep 02 2013
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/2/2013 4:57 PM, Dylan Knutson wrote:
 Can someone shed some light on this?

It comes from C. This was done in C so that addresses of struct instances will always be unique.
Sep 02 2013
next sibling parent reply Lionello Lunesu <lionello lunesu.remove.com> writes:
On 9/3/13 8:34, Vladimir Panteleev wrote:
 On Tuesday, 3 September 2013 at 00:05:04 UTC, Walter Bright wrote:
 On 9/2/2013 4:57 PM, Dylan Knutson wrote:
 Can someone shed some light on this?

It comes from C. This was done in C so that addresses of struct instances will always be unique.

Why is that important, and why does D need it? In C, this might make some sense, however empty structs are much more useful in D, e.g. for metaprogramming.

struct Z {}; Z a, b; assert(&a != &b);
Sep 02 2013
parent Lionello Lunesu <lionello lunesu.remove.com> writes:
On 9/3/13 10:59, Andrej Mitrovic wrote:
 On 9/3/13, Lionello Lunesu <lionello lunesu.remove.com> wrote:
 struct Z {};
 Z a, b;
 assert(&a != &b);

Since the structs are declared empty, their 1-byte values don't matter. So their addresses don't really matter either.

It has to do with the ol' 'identity vs equality'. In the example above, a and b are not identical ("do not refer to the same thing") and so their addresses should not be equal. The address of two 'named things' is the same if-and-only-if the names refer to the same thing. L.
Sep 04 2013
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/2/2013 5:34 PM, Vladimir Panteleev wrote:
 On Tuesday, 3 September 2013 at 00:05:04 UTC, Walter Bright wrote:
 On 9/2/2013 4:57 PM, Dylan Knutson wrote:
 Can someone shed some light on this?

It comes from C. This was done in C so that addresses of struct instances will always be unique.

Why is that important, and why does D need it? In C, this might make some sense, however empty structs are much more useful in D, e.g. for metaprogramming.

There were huge debates about this back when the C standard was in development. I don't really want to start that up again :-), I don't remember all the pros and cons, suffice to say that with D's vaunted C compatibility I think it should behave the same way.
Sep 02 2013
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/2/2013 9:39 PM, Dylan Knutson wrote:
 On Tuesday, 3 September 2013 at 04:33:16 UTC, Walter Bright wrote:
 There were huge debates about this back when the C standard was in
 development. I don't really want to start that up again :-), I don't remember
 all the pros and cons, suffice to say that with D's vaunted C compatibility I
 think it should behave the same way.

Fair enough. Was there any discussion on these forums about it? It sounds like an interesting topic of discussion, because I'm curious what the use cases are in D. Not to start a debate on it, but what compatibility with C is D is afforded by not having zero sized structs? It seems like D has abandoned a fair number of C warts, so it seems odd that this (to me, obscure one) one would stay.

Not following this aspect of C would result in silent and unexpected changes in behavior when interfacing D to C data structures, which D is supposed to be very good at.
Sep 02 2013
parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
On 03.09.2013 07:51, Walter Bright wrote:
 On 9/2/2013 9:39 PM, Dylan Knutson wrote:
 On Tuesday, 3 September 2013 at 04:33:16 UTC, Walter Bright wrote:
 There were huge debates about this back when the C standard was in
 development. I don't really want to start that up again :-), I don't
 remember
 all the pros and cons, suffice to say that with D's vaunted C
 compatibility I
 think it should behave the same way.

Fair enough. Was there any discussion on these forums about it? It sounds like an interesting topic of discussion, because I'm curious what the use cases are in D. Not to start a debate on it, but what compatibility with C is D is afforded by not having zero sized structs? It seems like D has abandoned a fair number of C warts, so it seems odd that this (to me, obscure one) one would stay.

Not following this aspect of C would result in silent and unexpected changes in behavior when interfacing D to C data structures, which D is supposed to be very good at.

How about specifying extern(C) before each C structure? The majority of bindings already have "extern(C):" on top of module.
Sep 03 2013
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/3/2013 9:00 AM, Piotr Szturmaj wrote:
 How about specifying extern(C) before each C structure? The majority of
bindings
 already have "extern(C):" on top of module.

While we could do that, at what point does this become arcane complexity?
Sep 03 2013
parent Walter Bright <newshound2 digitalmars.com> writes:
On 9/3/2013 1:01 PM, Dylan Knutson wrote:
 Perhaps something like `enum struct Foo;`, to indicate that it's just a
 compile-time used distinct type, and not a value? Just throwing that out there.
 I'm sure there is a better way to indicate that a type is just used as a
 type/namespace, without a value persay.

If you need a zero sized object in D, alias whatever[0] zeroSizeObject;
 When you say retain C compatibility, do you mean having C code which makes use
 of empty structs still be compile-able with the D compiler? Or D code
 inter-oping with external C libraries?

Yes.
Sep 03 2013
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/2/2013 9:28 PM, Dylan Knutson wrote:
 On Tuesday, 3 September 2013 at 00:05:04 UTC, Walter Bright wrote:
 On 9/2/2013 4:57 PM, Dylan Knutson wrote:
 Can someone shed some light on this?

It comes from C. This was done in C so that addresses of struct instances will always be unique.

So, because backwards compatibility is an important consideration, what type of code breakage might one expect if D was to implement zero-size structs?

Some background: http://stackoverflow.com/questions/7881487/zero-sized-struct http://stackoverflow.com/questions/10971651/zero-size-struct http://stackoverflow.com/questions/755305/empty-structure-in-c http://connect.microsoft.com/VisualStudio/feedback/details/355187/marshal-sizeof-returns-wrong-struct-size-for-empty-struct https://mail.elegosoft.com/pipermail/m3devel/2012-October/010196.html
Sep 03 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/03/2013 12:35 PM, Walter Bright wrote:

 Some background:

Ooh! This one has a very interesting excerpt: http://stackoverflow.com/a/10971748 "If the struct-declaration-list does not contain any named members, either directly or via an anonymous structure or anonymous union, the behavior is undefined." So the question is moot because empty struct has undefined behavior... in C... perhaps in D as well... Sweet! :) Ali
Sep 03 2013
parent Walter Bright <newshound2 digitalmars.com> writes:
On 9/3/2013 2:43 PM, Ali Çehreli wrote:
 On 09/03/2013 12:35 PM, Walter Bright wrote:

 Some background:

Ooh! This one has a very interesting excerpt: http://stackoverflow.com/a/10971748 "If the struct-declaration-list does not contain any named members, either directly or via an anonymous structure or anonymous union, the behavior is undefined." So the question is moot because empty struct has undefined behavior... in C... perhaps in D as well... Sweet! :)

In C++ it is defined.
Sep 03 2013
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Tuesday, 3 September 2013 at 00:05:04 UTC, Walter Bright wrote:
 On 9/2/2013 4:57 PM, Dylan Knutson wrote:
 Can someone shed some light on this?

It comes from C. This was done in C so that addresses of struct instances will always be unique.

Why is that important, and why does D need it? In C, this might make some sense, however empty structs are much more useful in D, e.g. for metaprogramming.
Sep 02 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 9/3/13, Lionello Lunesu <lionello lunesu.remove.com> wrote:
 struct Z {};
 Z a, b;
 assert(&a != &b);

Since the structs are declared empty, their 1-byte values don't matter. So their addresses don't really matter either.
Sep 02 2013
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Tuesday, 3 September 2013 at 02:31:44 UTC, Lionello Lunesu 
wrote:
 On 9/3/13 8:34, Vladimir Panteleev wrote:
 On Tuesday, 3 September 2013 at 00:05:04 UTC, Walter Bright 
 wrote:
 On 9/2/2013 4:57 PM, Dylan Knutson wrote:
 Can someone shed some light on this?

It comes from C. This was done in C so that addresses of struct instances will always be unique.

Why is that important, and why does D need it? In C, this might make some sense, however empty structs are much more useful in D, e.g. for metaprogramming.

struct Z {}; Z a, b; assert(&a != &b);

Yes, but why is this important? If you declare Z as "alias int[0] Z", the assert passes, so why is this property needed for structs but not static arrays?
Sep 02 2013
prev sibling next sibling parent "growler" <growlercab gmail.com> writes:
On Tuesday, 3 September 2013 at 03:08:34 UTC, Vladimir Panteleev 
wrote:
 On Tuesday, 3 September 2013 at 02:31:44 UTC, Lionello Lunesu 
 wrote:
 On 9/3/13 8:34, Vladimir Panteleev wrote:
 On Tuesday, 3 September 2013 at 00:05:04 UTC, Walter Bright 
 wrote:
 On 9/2/2013 4:57 PM, Dylan Knutson wrote:
 Can someone shed some light on this?

It comes from C. This was done in C so that addresses of struct instances will always be unique.

Why is that important, and why does D need it? In C, this might make some sense, however empty structs are much more useful in D, e.g. for metaprogramming.

struct Z {}; Z a, b; assert(&a != &b);

Yes, but why is this important? If you declare Z as "alias int[0] Z", the assert passes, so why is this property needed for structs but not static arrays?

alias int[0] Y; Y.sizeof == 0 // Bug? Y a; a.length == 0; // Correct. This is a bug IMO because the size of Y is 1 byte, not 0. struct Z{} Z.sizeof = 1; Z a,b; If I see code like this I expect a & b to have different addresses. But then I'm heavily influenced by C/C++. I'd prefer a different syntax, say: union struct Z{} or immutable struct Z{} either of these reflect that all instances will share the same memory location. G.
Sep 02 2013
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Tuesday, 3 September 2013 at 03:57:09 UTC, growler wrote:
 If I see code like this I expect a & b to have different 
 addresses.

Why? What do you need that assumption for? What will break if the structs became 0-sized?
Sep 02 2013
prev sibling next sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
On Tuesday, 3 September 2013 at 00:05:04 UTC, Walter Bright wrote:
 On 9/2/2013 4:57 PM, Dylan Knutson wrote:
 Can someone shed some light on this?

It comes from C. This was done in C so that addresses of struct instances will always be unique.

So, because backwards compatibility is an important consideration, what type of code breakage might one expect if D was to implement zero-size structs?
Sep 02 2013
prev sibling next sibling parent "growler" <growlercab gmail.com> writes:
On Tuesday, 3 September 2013 at 04:25:33 UTC, Vladimir Panteleev 
wrote:
 On Tuesday, 3 September 2013 at 03:57:09 UTC, growler wrote:
 If I see code like this I expect a & b to have different 
 addresses.

Why? What do you need that assumption for? What will break if the structs became 0-sized?

There's no real reason, it's just a personal preference. As I said I'm heavily influenced by a C/C++ background and as such would prefer a different syntax.
Sep 02 2013
prev sibling next sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
On Tuesday, 3 September 2013 at 04:33:16 UTC, Walter Bright wrote:
 There were huge debates about this back when the C standard was 
 in development. I don't really want to start that up again :-), 
 I don't remember all the pros and cons, suffice to say that 
 with D's vaunted C compatibility I think it should behave the 
 same way.

Fair enough. Was there any discussion on these forums about it? It sounds like an interesting topic of discussion, because I'm curious what the use cases are in D. Not to start a debate on it, but what compatibility with C is D is afforded by not having zero sized structs? It seems like D has abandoned a fair number of C warts, so it seems odd that this (to me, obscure one) one would stay.
Sep 02 2013
prev sibling next sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
On Tuesday, 3 September 2013 at 05:51:36 UTC, Walter Bright wrote:
 Not following this aspect of C would result in silent and 
 unexpected changes in behavior when interfacing D to C data 
 structures, which D is supposed to be very good at.

Fair enough. I'll guess stick with `SomeStruct.tupleof.length == 0` to determine if a struct has no members.
Sep 02 2013
prev sibling next sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
On Tuesday, 3 September 2013 at 19:30:53 UTC, Walter Bright wrote:
 On 9/3/2013 9:00 AM, Piotr Szturmaj wrote:
 How about specifying extern(C) before each C structure? The 
 majority of bindings
 already have "extern(C):" on top of module.

While we could do that, at what point does this become arcane complexity?

I think it depends how much metaprogramming could benefit in the language. Algebraic datatypes use zero sized structs in may places (see Rust, with Option(T) variants None and Some(T)), and I've heard of devs using structs as namespaces. In C, there was pretty much no metaprogramming to speak of, so semantic struct sizes just weren't a bit deal (as far as I can tell, that is). But, in D, metaprogramming is a ubiquitous feature, so I do think it's reasonable to consider some way of specifying zero sized structs in the language. I'd argue for it to be an opt-in feature though, so empty structs, by default, can't have a zero size. This keeps code backwards compatible. Perhaps something like `enum struct Foo;`, to indicate that it's just a compile-time used distinct type, and not a value? Just throwing that out there. I'm sure there is a better way to indicate that a type is just used as a type/namespace, without a value persay. When you say retain C compatibility, do you mean having C code which makes use of empty structs still be compile-able with the D compiler? Or D code inter-oping with external C libraries?
Sep 03 2013
prev sibling next sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
On Tuesday, 3 September 2013 at 20:07:57 UTC, Walter Bright wrote:
 If you need a zero sized object in D,

 alias whatever[0] zeroSizeObject;

I tried that, but unfortunately std.variant isn't compatible with zero sized types: ``` import std.variant; import std.stdio; import std.typecons; void main() { // This would be preferable, but Algebraic doens't like zero sized types //alias FirstType = void[0]; //alias SecondType = void[0]; // phobos\std\variant.d(165): Error: static assert (0u >= 4u) is false // phobos\std\variant.d(1149): instantiated from here: VariantN!(0u, void[0u], void[0u]) // Typedef!() wraps void[0] in a struct, which still has a size, and therefore // memory overhead, so we're back to square one. alias FirstType = Typedef!(void[0]); alias SecondType = Typedef!(void[0]); pragma(msg, FirstType.sizeof); // 1UL, darn alias Variants = Algebraic!(FirstType, SecondType); } ```
Sep 03 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 9/3/13, Dylan Knutson <tcdknutson gmail.com> wrote:
 I tried that, but unfortunately std.variant isn't compatible with
 zero sized types:

Please do file this as a bug: http://d.puremagic.com/issues/enter_bug.cgi?product=D If you don't have the time, we'll file it for you. Thanks!
Sep 03 2013
prev sibling parent "Dylan Knutson" <tcdknutson gmail.com> writes:
On Tuesday, 3 September 2013 at 20:42:47 UTC, Andrej Mitrovic 
wrote:
 On 9/3/13, Dylan Knutson <tcdknutson gmail.com> wrote:
 I tried that, but unfortunately std.variant isn't compatible 
 with
 zero sized types:

Please do file this as a bug: http://d.puremagic.com/issues/enter_bug.cgi?product=D If you don't have the time, we'll file it for you. Thanks!

Reported. I didn't realize this wasn't the intended behavior of Variant. :-) http://d.puremagic.com/issues/show_bug.cgi?id=10958
Sep 03 2013