www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Enum "Inheritance"?

reply %u <wfunction hotmail.com> writes:
I have a question on enum "inheritance" -- I'm wondering if what's happening
below is a bug or by
design?

I have code like this:

enum AccessMask { GenericRead = 0x80000000 }
enum FileAccess : AccessMask { ReadData = 1 } //Errors
void foo(FileAccess a) { /*Do something*/ }
void bar() { foo(AccessMask.GenericRead); } //Errors

I get two errors:

1. Inside FileAccess, I get an error with the assignment, telling me that it
can't work implicitly.
If instead of "ReadData = 1" I write "ReadData = cast(AccessMask)1", it works
fine, but it's a pain.

2. bar() doesn't compile, because it's passing a less specialized value to a
more specialized one.
While I understand why this wouldn't work from an actual _object_ inheritance
perspective, I don't
see why this doesn't work in the case of enums -- after all, isn't the subclass
supposed to contain
every member member of the superclass? Is this by design, and is there a neat
solution?


Thanks!
Feb 23 2011
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
%u:

 enum AccessMask { GenericRead = 0x80000000 }
 enum FileAccess : AccessMask { ReadData = 1 } //Errors

Nice. Has Walter thought about the possibility of such D code? Similar problems with typedef (that doesn't play well with OOP) have pushed Andrei to remove typedef from D. So be careful, or we'll lose enum too :o) Bye, bearophile
Feb 23 2011
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"%u" <wfunction hotmail.com> wrote in message 
news:ik432q$29qu$1 digitalmars.com...
I have a question on enum "inheritance" -- I'm wondering if what's 
happening below is a bug or by
 design?

 I have code like this:

 enum AccessMask { GenericRead = 0x80000000 }
 enum FileAccess : AccessMask { ReadData = 1 } //Errors
 void foo(FileAccess a) { /*Do something*/ }
 void bar() { foo(AccessMask.GenericRead); } //Errors

 I get two errors:

 1. Inside FileAccess, I get an error with the assignment, telling me that 
 it can't work implicitly.
 If instead of "ReadData = 1" I write "ReadData = cast(AccessMask)1", it 
 works fine, but it's a pain.

 2. bar() doesn't compile, because it's passing a less specialized value to 
 a more specialized one.
 While I understand why this wouldn't work from an actual _object_ 
 inheritance perspective, I don't
 see why this doesn't work in the case of enums -- after all, isn't the 
 subclass supposed to contain
 every member member of the superclass? Is this by design, and is there a 
 neat solution?

You're misunderstanding the way enum works. The "enum A : B {}" isn't inheritence, even though the syntax is similar to class inheritence. The part after the ":" is not a parent type, but the underlying representational type. Ie: enum IntBased { ValueA = 1; } enum IntBased : int { ValueA = 1; } // Same as above enum CharBased : char {ValueA = 'x'; } enum StringBased : string {ValueA = "hello"; } // Not sure if this actually works enum DoubleBased : double {ValueA = 3.14; } // Etc... The part after ":" just specifies what type each value is internally stored as. So when you say: enum AccessMask { GenericRead = 0x80000000 } enum FileAccess : AccessMask { ... } ...you've just told the compiler that each value of FileAccess is supposed to resolve to an AccessMask, not an int. But then you told it that FileAccess.ReadData is supposed to be 1, but that's impossible because 1 isn't an AccessMask, it's an int. And not only that, but it's an int that doesn't even exist in AccessMask (which is why you need that cast. Although, all that your cast is doing is forcefully creating a completely invalid AccessMask). What you need to do is something like: enum AccessMask { GenericRead = 0x80000000 } enum FileAccess : AccessMask { ReadData = AccessMask.GenericRead } or: enum AccessMask { GenericRead = 0x80000000, ReadData = 1 } enum FileAccess : AccessMask { AccessMask.ReadData = 1 } If neither of those are what you're after, then "enum FileAccess : AccessMask" isn't appropriate for the task anyway.
Feb 23 2011
parent reply %u <wfunction hotmail.com> writes:
 enum AccessMask { GenericRead = 0x80000000 }
 enum FileAccess : AccessMask { ReadData = 1 } //Errors


 Nice. Has Walter thought about the possibility of such D code? Similar
problems with typedef

or we'll lose enum too :o) Lol, okay. Do you have any other suggestions for a better solution on how to avoid duplicating generic access masks like MAXIMUM_ALLOWED inside each access mask type?
Feb 23 2011
parent reply Kagamin <spam here.lot> writes:
%u Wrote:

 Lol, okay. Do you have any other suggestions for a better solution on how to
avoid duplicating
 generic access masks like MAXIMUM_ALLOWED inside each access mask type?

You're doing it wrong. Create OO wrapper instead.
Feb 24 2011
parent reply %u <wfunction hotmail.com> writes:
 Lol, okay. Do you have any other suggestions for a better


MAXIMUM_ALLOWED inside each access mask type?
 You're doing it wrong. Create OO wrapper instead.

Hm... but: (1) I'm not going to create a new instance of an entire class every single time I need to check an access mask, that's wasteful. When was the last time you created a "new int()" on the stack and all you did was pass the dereferenced value to a function? (2) A singleton pattern won't really work with bitwise OR, unless I instantiate a new access mask object at each call, in which case we go back to point (1). Any other ways? Or am I misunderstanding this?
Feb 24 2011
next sibling parent "Nick Sabalausky" <a a.a> writes:
"%u" <wfunction hotmail.com> wrote in message 
news:ik70pq$1a37$1 digitalmars.com...
 Lol, okay. Do you have any other suggestions for a better


MAXIMUM_ALLOWED inside each access mask type?
 You're doing it wrong. Create OO wrapper instead.

Hm... but: (1) I'm not going to create a new instance of an entire class every single time I need to check an access mask, that's wasteful. When was the last time you created a "new int()" on the stack and all you did was pass the dereferenced value to a function? (2) A singleton pattern won't really work with bitwise OR, unless I instantiate a new access mask object at each call, in which case we go back to point (1). Any other ways? Or am I misunderstanding this?

What exactly is it that you're trying to do?
Feb 24 2011
prev sibling parent reply Kagamin <spam here.lot> writes:
%u Wrote:

 Lol, okay. Do you have any other suggestions for a better


MAXIMUM_ALLOWED inside each access mask type?
 You're doing it wrong. Create OO wrapper instead.

Hm... but: (1) I'm not going to create a new instance of an entire class every single time I need to check an access mask, that's wasteful.

I meant, you write File.open or File.isReadable which do the job for you and don't expose OS cruft.
Feb 25 2011
parent reply %u <wfunction hotmail.com> writes:
 What exactly is it that you're trying to do?

 (1) I'm not going to create a new instance of an entire class


 I meant, you write File.open or File.isReadable which do the job

That's what I'm doing! I'm precisely trying to wrap the Windows NT API, and so I'm making classes like NtObject, NtFile, NtToken, etc., to wrap functions like NtOpenFile, NtQueryObject, etc. (I'm aware that some of them are undocumented, but I live with that.) So I have different kinds of access masks: 1. Generic access masks that can be passed to _any_ procedure and combined with any other access masks, like GENERIC_READ, MAXIMUM_ALLOWED, etc. 2. Object-specific access masks that can be only used for specific objects and that can be combined with each other and with generic access masks, like FILE_READ_DATA, TOKEN_ALL_ACCESS, etc. So I'm trying to avoid redundant code here, because any member of an AccessMask enumeration would also be inside the FileAccess enum, the TokenAccess enum, the ThreadAccess enum, etc. But at the same time, I need to be able to pass both specific and generic access masks to functions that take in specific access masks. So the question is, what's the best way to do it?
Feb 25 2011
next sibling parent reply Kagamin <spam here.lot> writes:
%u Wrote:

 So the question is, what's the best way to do it?

As long as you expose OS cruft, your wrappers are useless, I'm afraid. I suggest you to provide safe and meaningful wrappers, e.g. streams, ranges, for concrete business cases which deal with OS directly so that the wrapper won't need flags from user (they are usually encoded in the function name) and know all necessary flags for OS call. Some flags are even win9x legacy and new code should not use them and request only needed access. For example, generic* and maximum* flags are usually never needed and their use is discouraged.
Feb 25 2011
parent %u <wfunction hotmail.com> writes:
 As long as you expose OS cruft, your wrappers are useless, I'm

 I suggest you to provide safe and meaningful wrappers, e.g.

directly so that the wrapper won't need flags from user (they are usually encoded in the function name) and know all necessary flags for OS call. Some flags are even win9x legacy and new code should not use them and request only needed access. For example, generic* and maximum* flags are usually never needed and their use is discouraged. Thanks for the advice, but I wasn't asking about whether my wrappers are useful (they're certainly useful for myself, and that's enough reason for me to make them). I was asking about how to avoid redundancy in enums (it's not like they don't have uses other than access masks...), and this didn't answer the question.
Feb 25 2011
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"%u" <wfunction hotmail.com> wrote in message 
news:ik7slf$2q2u$1 digitalmars.com...
 What exactly is it that you're trying to do?

 (1) I'm not going to create a new instance of an entire class


 I meant, you write File.open or File.isReadable which do the job

That's what I'm doing! I'm precisely trying to wrap the Windows NT API, and so I'm making classes like NtObject, NtFile, NtToken, etc., to wrap functions like NtOpenFile, NtQueryObject, etc. (I'm aware that some of them are undocumented, but I live with that.) So I have different kinds of access masks: 1. Generic access masks that can be passed to _any_ procedure and combined with any other access masks, like GENERIC_READ, MAXIMUM_ALLOWED, etc. 2. Object-specific access masks that can be only used for specific objects and that can be combined with each other and with generic access masks, like FILE_READ_DATA, TOKEN_ALL_ACCESS, etc. So I'm trying to avoid redundant code here, because any member of an AccessMask enumeration would also be inside the FileAccess enum, the TokenAccess enum, the ThreadAccess enum, etc. But at the same time, I need to be able to pass both specific and generic access masks to functions that take in specific access masks. So the question is, what's the best way to do it?

I know some people don't like using mixins, but I think that's really the best way to achieve that effect without loosing DIY and without resorting to something more heavy-weight: void funcA(A a) {} void funcB(B b) {} enum stdAccess = q{ STD_ELEMENT_1 = 17, STD_ELEMENT_2 = 42, }; enum A { mixin(stdAccess); A_ACCESS_1 = 0x80, } enum B { mixin(stdAccess); B_ACCESS_1 = 0x80, } Or, if you really don't want mixins, or if you really want STD_ELEMENT_1/etc... to always be of one single type, then make the std ones their own distict type and use either function overloading or algebraic types: enum STD { STD_ELEMENT_1 = 17, STD_ELEMENT_2 = 42, } enum A { A_ACCESS_1 = 0x80, } enum B { B_ACCESS_1 = 0x80, } void funcA(A a) {} void funcA(STD a) {} void funcB(B b) {} void funcB(STD b) {} // Or: void funcA(Algebraic!(A,STD) a) {} void funcB(Algebraic!(B,STD) b) {} I think your main original problem was that there isn't really a way to compose enums additively, only subtractive. Although, maybe you could experiment with something like: enum X : Algebraic!(...) {} Not sure if that would help get what you want, though, or if it would even work at all.
Feb 26 2011
parent reply "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:ikcepj$248p$1 digitalmars.com...
 I think your main original problem was that there isn't really a way to 
 compose enums additively, only subtractive.

That just gave me an idea: I think you should be able to get away with an ordinary union: enum STD { STD_ELEMENT_1 = 17, STD_ELEMENT_2 = 42, } enum AOnly { A_ACCESS_1 = 0x80, } enum BOnly { B_ACCESS_1 = 0x80, } union A { STD std; A a; } union B { STD std; B b; } void funcA(A a) { if(a.a == A_ACCESS_1) {} if(a.std == STD_ELEMENT_1) {} } void funcB(B b) { if(b.b == B_ACCESS_1) {} if(b.std == STD_ELEMENT_1) {} } In the general case, I'd be very hesitent to use a non-tagged union like this, because it wouldn't provide protection against accidentally having the same integer value for a member of STD and a member of A or B (which would obviously screw the whole thing up). But in your paricular case it doesn't seem that would likely be an issue. Of course, if you dodn't mind a heavier-weight OO-based solution, you could always use classes instead of enums and use static immutable class members as the possible values (and make the constructor private to prevent additional possible values), but personally I think that would be over-engineered compared to either the above or the mixin solution in my previous message.
Feb 26 2011
next sibling parent %u <wfunction hotmail.com> writes:
 I know some people don't like using mixins, but I think that's

without resorting to something more heavy-weight
 ...
 That just gave me an idea: I think you should be able to get away

I thought about those, but ultimately, the problem is that they're not extensible: if a new type of access mask is introduced. In the former case, I can't AND it with a generic access mask to test what the result is, because they're of different types; in the latter case, there's no way to extend this whatsoever. :\ I'm trying to keep this extensible while avoiding code duplication, and it's proven to be surprisingly hard to do this neatly.
Feb 26 2011
prev sibling parent %u <wfunction hotmail.com> writes:
Actually, on a second thought, I just noticed that the union method
*is* extensible, since you're putting things in different unions... I
think I'll give it a shot, thanks! :)
Feb 26 2011
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On 02/27/2011 04:20 AM, Nick Sabalausky wrote:
 "Nick Sabalausky"<a a.a>  wrote in message
 news:ikcepj$248p$1 digitalmars.com...
 I think your main original problem was that there isn't really a way to
 compose enums additively, only subtractive.

That just gave me an idea: I think you should be able to get away with an ordinary union: enum STD { STD_ELEMENT_1 = 17, STD_ELEMENT_2 = 42, } enum AOnly { A_ACCESS_1 = 0x80, } enum BOnly { B_ACCESS_1 = 0x80, } union A { STD std; A a; } union B { STD std; B b; } void funcA(A a) { if(a.a == A_ACCESS_1) {} if(a.std == STD_ELEMENT_1) {} } void funcB(B b) { if(b.b == B_ACCESS_1) {} if(b.std == STD_ELEMENT_1) {} } In the general case, I'd be very hesitent to use a non-tagged union like this, because it wouldn't provide protection against accidentally having the same integer value for a member of STD and a member of A or B (which would obviously screw the whole thing up). But in your paricular case it doesn't seem that would likely be an issue. Of course, if you dodn't mind a heavier-weight OO-based solution, you could always use classes instead of enums and use static immutable class members as the possible values (and make the constructor private to prevent additional possible values), but personally I think that would be over-engineered compared to either the above or the mixin solution in my previous message.

Very interesting solution, indeed: enum composition via union. I think as well it's safe because --per definition-- each 'code' of a discriminating enum must have a distinct value. (They are nominals: http://en.wikipedia.org/wiki/Nominal_number). Denis -- _________________ vita es estrany spir.wikidot.com
Feb 27 2011
prev sibling parent spir <denis.spir gmail.com> writes:
On 02/27/2011 07:21 AM, %u wrote:
 Actually, on a second thought, I just noticed that the union method
 *is* extensible, since you're putting things in different unions... I
 think I'll give it a shot, thanks! :)

And you can always "super-compose" unions. This view, in fact, is similar to the (famous) OO debate upon composition vs inheritance; but at lower level. http://en.wikipedia.org/wiki/Composition_over_inheritance Denis -- _________________ vita es estrany spir.wikidot.com
Feb 27 2011