www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - disable("reason")

reply Marcel <marcelpi97 gmail.com> writes:
Hello!
I'm writing a library where under certain conditions i need all 
the default constructors to be disabled. I would like to tell the 
user why they can't instantiate the struct.
Is there a way to do that?
Jan 07 2020
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, January 7, 2020 5:23:48 PM MST Marcel via Digitalmars-d-learn 
wrote:
 Hello!
 I'm writing a library where under certain conditions i need all
 the default constructors to be disabled. I would like to tell the
 user why they can't instantiate the struct.
 Is there a way to do that?
In terms of an error message? Not really. You can put a pragma(msg, ""); in there, but that would always print, not just when someone tried to use it. I'd suggest that you just put the information in the documentation. If they write code that doesn't work because of you using disable this(); the documentation is where people will look to find out why anyway. And BTW, structs don't have default constructors in D. So, maybe it's just a question of terminology rather than you misunderstanding the semantics, but all variables in D get default initialized with their type's init value, including structs, and you can't actually declare a default constructor for a struct. So, if you have disable this(); in a struct, all that that's doing is disabling default initialization, not default construction. So, code like MyStruct ms; or MyStruct[] arr; won't compile, because they require default initialization. But even then, you don't actually get rid of the default value of the struct. You just make it so that it doesn't get used implicitly. Code such as auto ms = MyStruct.init; or MyStruct ms = MyStruct.init; will still work. So, you still potentially have to worry about the type's init value being used (though you could just document that no one should ever use its init value explicitly, and that they will have bugs if they do, not that that actually prevents people from using it incorrectly; it just informs them that they shouldn't). It's unlikely that many people will try to use the init value explicitly, but some generic code may do so (e.g. IIRC, std.algorithm's move function uses it on the source variable after moving the value to the target variable). - Jonathan M Davis
Jan 07 2020
next sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 8 January 2020 at 07:03:26 UTC, Jonathan M Davis 
wrote:
 you could just document that no one should ever use its init
 value explicitly, and that they will have bugs if they do
You also create a static init member marked disable: struct S { disable this(); disable static S init(); } This will give sensible error messages anywhere .init is being used. Now, Phobos and other libraries might expect that .init is always working, so this could potentially be a problem.
Jan 08 2020
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, January 8, 2020 4:54:06 AM MST Simen Kjærås via Digitalmars-d-
learn wrote:
 On Wednesday, 8 January 2020 at 07:03:26 UTC, Jonathan M Davis

 wrote:
 you could just document that no one should ever use its init
 value explicitly, and that they will have bugs if they do
You also create a static init member marked disable: struct S { disable this(); disable static S init(); } This will give sensible error messages anywhere .init is being used. Now, Phobos and other libraries might expect that .init is always working, so this could potentially be a problem.
That's likely to break a _lot_ of generic code, because init is used heavily in template constraints and static if conditions as the way to get a value of that type to test. It's also actually been argued before that it should be illegal to declare a symbol called init on any type, which is one reason why the init member was removed from TypeInfo. I'd strongly advise against anyone declaring a struct or class member named init whether it's disabled or not. - Jonathan M Davis
Jan 08 2020
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 08, 2020 at 06:06:33AM -0700, Jonathan M Davis via
Digitalmars-d-learn wrote:
 On Wednesday, January 8, 2020 4:54:06 AM MST Simen Kjærås via Digitalmars-d-
 learn wrote:
[...]
 struct S {
       disable this();
       disable static S init();
 }

 This will give sensible error messages anywhere .init is being
 used. Now, Phobos and other libraries might expect that .init is
 always working, so this could potentially be a problem.
That's likely to break a _lot_ of generic code, because init is used heavily in template constraints and static if conditions as the way to get a value of that type to test.
I think the new(er?) idiom is to use a lambda parameter to get the type instead. I.e., instead of: auto foo(T, U)(T t, U u) if (is(typeof(someOperation(T.init))) && is(typeof(otherOperation(U.init)))) { ... } you'd write: auto foo(T, U)(T t, U u) if (is(typeof((T t, U u) { someOperation(t); otherOperation(u); }))) { ... } This will work for types T, U that do not have default initialization (e.g., disabled this(), like in the OP), types that for whatever reason have overridden .init, or types that are non-copyable, etc.. Nonetheless, .init is one of those things that are just assumed to always exist, so overriding it is not advisable.
 It's also actually been argued before that it should be illegal to
 declare a symbol called init on any type, which is one reason why the
 init member was removed from TypeInfo.  I'd strongly advise against
 anyone declaring a struct or class member named init whether it's
  disabled or not.
Indeed: https://issues.dlang.org/show_bug.cgi?id=7066 T -- Today's society is one of specialization: as you grow, you learn more and more about less and less. Eventually, you know everything about nothing.
Jan 08 2020
prev sibling parent reply Marcel <marcelpi97 gmail.com> writes:
On Wednesday, 8 January 2020 at 07:03:26 UTC, Jonathan M Davis 
wrote:
 On Tuesday, January 7, 2020 5:23:48 PM MST Marcel via 
 Digitalmars-d-learn wrote:
 [...]
In terms of an error message? Not really. You can put a pragma(msg, ""); in there, but that would always print, not just when someone tried to use it. I'd suggest that you just put the information in the documentation. If they write code that doesn't work because of you using [...]
Oops, it appears I didn't actually understand this part of the language. I'm coming from C++ and I assumed both languages did this the same way, but thankfully I found a workaround that doesn't require doing what I wrote in the initial post and is better in general. I also agree with Adam, it can make for a nice little feature in D.
Jan 08 2020
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, January 8, 2020 2:58:59 PM MST Marcel via Digitalmars-d-learn 
wrote:
 On Wednesday, 8 January 2020 at 07:03:26 UTC, Jonathan M Davis

 wrote:
 On Tuesday, January 7, 2020 5:23:48 PM MST Marcel via

 Digitalmars-d-learn wrote:
 [...]
In terms of an error message? Not really. You can put a pragma(msg, ""); in there, but that would always print, not just when someone tried to use it. I'd suggest that you just put the information in the documentation. If they write code that doesn't work because of you using [...]
Oops, it appears I didn't actually understand this part of the language. I'm coming from C++ and I assumed both languages did this the same way, but thankfully I found a workaround that doesn't require doing what I wrote in the initial post and is better in general.
D took the approach of having all variables being default initalized with a value that's known at compile-time so that you don't risk dealing with garbage values as can happen in C/C++. For structs, the result is that default constructors aren't a thing, because the default vaule has to be a fixed value known at compile time, and that doesn't work with default constructors, which can run arbitrary code at runtime. And with non-default constructors, a struct is actually initialized with its default value before its constructor is even run. So, you don't risk reading garbage values in the object's member variables before properly initializing them. There are times when the lack of default constructors can be annoying, but it avoids a class of problems that C++ has, and overall, it works quite well. The ability to disable this(); was added later in order to facilitate rarer cases where using the default value for a type would be problematic (e.g. when a type absolutely needed to have some code run at runtime when constructing it for it to work properly). However, because the language was designed around the idea that all objects would be default-initialized, disabling default initialization tends to make some typical stuff not work with that type (like out parameters or arrays, since they both require the init value). Ultimately, D's approach is a tradeoff. Some of the problems that you can have in C++ are eliminated, but it introduces some complications that don't exist in C++.
 I also agree with Adam, it can make for a nice little feature in
 D.
I doubt that there would be much resistance to it, but since you almost certainly need to read the documentation to understand how to use the type properly if it can't be default-initialized, I don't know that it would actually add much benefit in practice. Either way, it's not something that's come up often enough for anyone to push for such a language change. - Jonathan M Davis
Jan 09 2020
prev sibling next sibling parent reply user1234 <user1234 1234.de> writes:
On Wednesday, 8 January 2020 at 00:23:48 UTC, Marcel wrote:
 Hello!
 I'm writing a library where under certain conditions i need all 
 the default constructors to be disabled. I would like to tell 
 the user why they can't instantiate the struct.
 Is there a way to do that?
class Example { disable this() { pragma(msg, "not allowed..."); } } void main() { new Example(); } outputs:
 not allowed...
 /tmp/temp_7F8C65489550.d(12,5): Error: constructor 
 `runnable.Example.this` cannot be used because it is annotated 
 with ` disable`
Because pragma(msg) are evaluated when encountered you can use abuse them. By specification they're not allowed to modify the meaning of the program.
Jan 08 2020
parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 8 January 2020 at 08:26:51 UTC, user1234 wrote:
 class Example
 {
      disable this() { pragma(msg, "not allowed..."); }
 }

 void main()
 {
     new Example();
 }

 outputs:

 not allowed...
 /tmp/temp_7F8C65489550.d(12,5): Error: constructor 
 `runnable.Example.this` cannot be used because it is annotated 
 with ` disable`
However, it will print that message even if the constructor is never called. If you make the constructor a template instead, you will only get the message when someone attempts to use the default constructor: class Example { disable this()() { pragma(msg, "not allowed..."); } } void main() { new Example(); } Sadly, this does not work for structs, as they don't really have a default constructor, as Jonathan pointed out. -- Simen
Jan 08 2020
prev sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 8 January 2020 at 00:23:48 UTC, Marcel wrote:
 I would like to tell the user why they can't instantiate the 
 struct.
 Is there a way to do that?
I'd love to have exactly what you said for this reason, but D doesn't really have it. You just have to hope they read the docs (my doc generator specifically calls out default ctors for this reason). But we should formally request disable("reason") to be added, it really is very nice and has precedent in deprecated("reason").
Jan 08 2020