www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Feature request: Attribute with which to enable the requirement of

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Let's say you define an enum, which is to be used as a variable:

enum Machine
{
    X86,
    X86_64,
}

So off you go and let the users (or yourself) use this in code:

-----
void main()
{
    Machine machine;
    ...
    callSomething(machine);
}
-----

And here's the problem: the enum variable was default-initialized to
the first value. This could either be deliberate, or in many other
cases it could be an issue of forgotten initialization.

If it's the latter, then this might become a problem. For example, a
library writer could update the enum and inject a new enum member at
the first position (before X86). This would update any
default-initialized enums in user-code to become something else, and
this may cause logical bugs.

Because of this, what I usually do when I define enums is to inject an
"Invalid" member as a sentinel at the first position of the enum:

enum Machine
{
    Invalid,  // sentinel
    X86,
    X86_64,
}

Then, if the user really does forget to initialize the enum I will
make sure I throw an exception when I run a `final switch` on the enum
variable in the functions I provide (when I encounter the Invalid
sentinel).  However, the user would have to do this in his own
functions as well to make everything perfectly safe.

So, the above doesn't really scale.

What I propose is to add the ability to mark an enum somehow (e.g.
with a "required initializer attribute"), so the user will have to
manually initialize the enum instead of relying on
default-initialization. Default-initialization would fail to compile
for this type of enum.

Think of it as the mirror feature of " disable this" of structs. To
provide an example:

 RequireInit enum Machine
{
    X86,
    X86_64,
}

void main()
{
    // Machine machine;  // compile-time error: Machine cannot be
default-initialized

    Machine machine = Machine.X86_64;  // ok, explicitly initialized
}

Thoughts?
Jun 02 2013
next sibling parent "Diggory" <diggsey googlemail.com> writes:
On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
 Let's say you define an enum, which is to be used as a variable:

 enum Machine
 {
     X86,
     X86_64,
 }

 So off you go and let the users (or yourself) use this in code:

 -----
 void main()
 {
     Machine machine;
     ...
     callSomething(machine);
 }
 -----

 And here's the problem: the enum variable was 
 default-initialized to
 the first value. This could either be deliberate, or in many 
 other
 cases it could be an issue of forgotten initialization.

 If it's the latter, then this might become a problem. For 
 example, a
 library writer could update the enum and inject a new enum 
 member at
 the first position (before X86). This would update any
 default-initialized enums in user-code to become something 
 else, and
 this may cause logical bugs.

 Because of this, what I usually do when I define enums is to 
 inject an
 "Invalid" member as a sentinel at the first position of the 
 enum:

 enum Machine
 {
     Invalid,  // sentinel
     X86,
     X86_64,
 }

 Then, if the user really does forget to initialize the enum I 
 will
 make sure I throw an exception when I run a `final switch` on 
 the enum
 variable in the functions I provide (when I encounter the 
 Invalid
 sentinel).  However, the user would have to do this in his own
 functions as well to make everything perfectly safe.

 So, the above doesn't really scale.

 What I propose is to add the ability to mark an enum somehow 
 (e.g.
 with a "required initializer attribute"), so the user will have 
 to
 manually initialize the enum instead of relying on
 default-initialization. Default-initialization would fail to 
 compile
 for this type of enum.

 Think of it as the mirror feature of " disable this" of 
 structs. To
 provide an example:

  RequireInit enum Machine
 {
     X86,
     X86_64,
 }

 void main()
 {
     // Machine machine;  // compile-time error: Machine cannot 
 be
 default-initialized

     Machine machine = Machine.X86_64;  // ok, explicitly 
 initialized
 }

 Thoughts?

Sounds like the exact same feature as " disable this()" but for enums, so perhaps it would be better to follow that syntax?
Jun 02 2013
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, June 03, 2013 05:27:03 Diggory wrote:
 Sounds like the exact same feature as " disable this()" but for
 enums, so perhaps it would be better to follow that syntax?

Except that disable this() is specifically for cases where a type _can't_ have a default value and do what it's designed to do (like non-nullable references). I don't think that that same logic applies to an enum. - Jonathan M Davis
Jun 02 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
 Let's say you define an enum, which is to be used as a variable:
 ...

 Thoughts?

I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).
Jun 02 2013
prev sibling next sibling parent "Diggory" <diggsey googlemail.com> writes:
On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
 On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
 Let's say you define an enum, which is to be used as a 
 variable:
 ...

 Thoughts?

I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).

It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.
Jun 03 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Monday, 3 June 2013 at 11:12:10 UTC, Diggory wrote:
 On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
 On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
 Let's say you define an enum, which is to be used as a 
 variable:
 ...

 Thoughts?

I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).

It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.

This is again using wrong terminology to move meaning from type to pointed data (if any) as happened recently with dynamic arrays. Nothing on the Earth promises that if in one language class type is allocated memory, than in another language class should be also so, and if it is not, then hoards of programmist should use first naming conversion with no reason. Consult the spec what class type is in D and please do not confuse D with other languages. Anyway, this irrelevant here, because what I mean is: class A { disable this(); // or RequireInit } A a; // does not work Currently disable prevents allocation with specified ctor, but does not stops from creating null initialized object. Giving demand for non-nullable classes, probably it is a good idea to support this feature by broading disable this in context of classes or creating similar feature from scrath like RequireInit. This issue with classes is more important than with enums, and if such feature is implemented, I see no reason for it not to work with enums as with other user-defined types. And if consensus is that the feature in classes is not need, then it is likely less needed in enums.
Jun 03 2013
prev sibling next sibling parent "Diggory" <diggsey googlemail.com> writes:
On Monday, 3 June 2013 at 12:13:30 UTC, Maxim Fomin wrote:
 On Monday, 3 June 2013 at 11:12:10 UTC, Diggory wrote:
 On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
 On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
 Let's say you define an enum, which is to be used as a 
 variable:
 ...

 Thoughts?

I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).

It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.

This is again using wrong terminology to move meaning from type to pointed data (if any) as happened recently with dynamic arrays. Nothing on the Earth promises that if in one language class type is allocated memory, than in another language class should be also so, and if it is not, then hoards of programmist should use first naming conversion with no reason. Consult the spec what class type is in D and please do not confuse D with other languages.

My point is completely applicable to D - it applies to any form of polymorphic type. In D the type of a class variable is determined at runtime, not at compile time, so what you're saying makes no sense. The feature you want is exactly what NotNull!T does, the way you are suggesting of implementing it doesn't work.
Jun 03 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Monday, 3 June 2013 at 16:46:15 UTC, Diggory wrote:
 On Monday, 3 June 2013 at 12:13:30 UTC, Maxim Fomin wrote:
 On Monday, 3 June 2013 at 11:12:10 UTC, Diggory wrote:
 On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
 On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic 
 wrote:
 Let's say you define an enum, which is to be used as a 
 variable:
 ...

 Thoughts?

I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).

It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.

This is again using wrong terminology to move meaning from type to pointed data (if any) as happened recently with dynamic arrays. Nothing on the Earth promises that if in one language class type is allocated memory, than in another language class should be also so, and if it is not, then hoards of programmist should use first naming conversion with no reason. Consult the spec what class type is in D and please do not confuse D with other languages.

My point is completely applicable to D - it applies to any form of polymorphic type. In D the type of a class variable is determined at runtime, not at compile time, so what you're saying makes no sense.

No, this is completely wrong. D has static type system and type of expression is determined at compile time. import std.stdio; class A {} class B : A { } void foo(T) (T t) if (is(T == class)) { } void main() { A a; foo(a); // belongs to class types irrespective to // allocation and polymorphic type a = new B; pragma(msg, typeof(a)); // static type system - prints A writeln(typeof(a).stringof); // static type system - prints A } in addition with supporting runtime polymorphism. You completely confuse language type system with polymorphism and allocation state as well as misunderstanding caused by confusion with an official language spec.
Jun 03 2013
prev sibling parent "Diggory" <diggsey googlemail.com> writes:
On Monday, 3 June 2013 at 17:36:13 UTC, Maxim Fomin wrote:
 On Monday, 3 June 2013 at 16:46:15 UTC, Diggory wrote:
 On Monday, 3 June 2013 at 12:13:30 UTC, Maxim Fomin wrote:
 On Monday, 3 June 2013 at 11:12:10 UTC, Diggory wrote:
 On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
 On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic 
 wrote:
 Let's say you define an enum, which is to be used as a 
 variable:
 ...

 Thoughts?

I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).

It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.

This is again using wrong terminology to move meaning from type to pointed data (if any) as happened recently with dynamic arrays. Nothing on the Earth promises that if in one language class type is allocated memory, than in another language class should be also so, and if it is not, then hoards of programmist should use first naming conversion with no reason. Consult the spec what class type is in D and please do not confuse D with other languages.

My point is completely applicable to D - it applies to any form of polymorphic type. In D the type of a class variable is determined at runtime, not at compile time, so what you're saying makes no sense.

No, this is completely wrong. D has static type system and type of expression is determined at compile time.

No that's wrong, the static type only determines the highest class in the class hierarchy that can be stored, it does not determine the actual runtime type of the expression. If you try to implement " disable this" using the static type it breaks all the rules of covariance and contravariance that classes are expected to follow, and thus breaks the type system. That's why it's implemented as NotNull!T.
Jun 03 2013