www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Using std.conv.to with enum that is based on string type

reply "Uranuz" <neuranuz gmail.com> writes:
When I tried to use std.conv.to to serialize enum values that are 
based on string type I faced with situation that *to* not exactly 
as I expected. As far as I understand it tries to find match 
between passed string value and enum value identifier instead of 
just casting it to enum type.

1. So I want to know if it is expected behaviour?
2. What are the reasons for it?

There is a simple example that illustrates what I'm talking about.

import std.stdio, std.conv;

enum Category: string { first = "I", second = "II", third = "III" 
};

void main()
{
	
	assert( to!Category("first") == Category.first ); //This runs 
succesfully
	//assert( to!Category("I") == Category.first ); //This 
conversion fails

}

As a result I could try to fix it if it's not correct behaviour. 
Another concern about using *cast* is that it doesn't make some 
checks if value is one of enum values declared in code.

I found some topics about enum conversions.

http://forum.dlang.org/thread/bug-4744-3 http.d.puremagic.com%2Fissues%2F
http://forum.dlang.org/thread/bug-9821-3 http.d.puremagic.com%2Fissues%2F
http://forum.dlang.org/thread/fpzhgzhrhaxjfuiydwxh forum.dlang.org

Problem is that (as I think) that std.conv.to documentation 
doesn't describe how  *to* SHOULD work for different ways of 
coversions so it's not obvious how to *fix* it. The only way of 
using is via trials and errors and it's not garanteed that found 
out with this way will not be changed and your code will not be 
broken. It's because that implementers don't have information 
about how exactly it SHOULD work.

Sorry for some emotions but I have problems with lack of doc 
sometimes and it makes my job harder.
Jul 19 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
 1. So I want to know if it is expected behaviour?
Yes, this was the chosen behaviour.
 2. What are the reasons for it?
It's one of the two main behaviours you may want, even if your specific case it is not. It can be used to unserialize enums from a text file. This is the principle behind the text/to pair of functions, and this was copied by the repr of Python. Bye, bearophile
Jul 19 2014
parent "Uranuz" <neuranuz gmail.com> writes:
On Saturday, 19 July 2014 at 12:31:05 UTC, bearophile wrote:
 1. So I want to know if it is expected behaviour?
Yes, this was the chosen behaviour.
 2. What are the reasons for it?
It's one of the two main behaviours you may want, even if your specific case it is not. It can be used to unserialize enums from a text file. This is the principle behind the text/to pair of functions, and this was copied by the repr of Python. Bye, bearophile
It's not very clear to me what are 2 main behaviours you are talking about. And I don't agree with current behaviour and I have following reasons for it: 1. Because enum is based on some underlying type I expect that I work and make conversions with that exact value but not enum *identifier* for this value in the code. 2. Using these *identifiers* is for checking if variable has value from set that declared inside enum statement and creating readable name for underlying value in source code. If you want to create binding between some string identifier and value that will be exposed out of code you could use associative array instead of enum. 3. Current implementation makes compiler to save enum identifiers as strings somewhere in programme code (it's how I think it should do). It's not exactly what enum is indended for (having double set of values). 4. This implementation looks like mixing base language functionality with compile-time reflexion and should be moved to std.traits (for example) not std.conv. And it's not what expected by module that should simply provide correct conversion between base types. 5. D is not a Python at all All of the above is just my own opinion. So I can solve this with my own wrapper around std.conv. The question is shall all the users of language design their own wrappers for basic library function to just get basic behavior that they expect from standard function? P.S. All of these should be criticized))
Jul 19 2014
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/19/2014 04:53 AM, Uranuz wrote:

 When I tried to use std.conv.to to serialize enum values that are based
 on string type I faced with situation that *to* not exactly as I
 expected.
Maybe I misunderstand you but the behavior is consistent for all enum types in both conversion directions. The value 1 does not appear anywhere in the following conversions, so "hello" should not appear either: import std.conv; enum I : int { a = 1 } enum S : string { a = "hello" } void main() { assert(I.a.to!string == "a"); assert(S.a.to!string == "a"); assert("a".to!I == I.a); assert("a".to!S == S.a); } Doing anything special for a particular type would be confusing and make at least template programming hard. Ali
Jul 19 2014
parent reply "Uranuz" <neuranuz gmail.com> writes:
On Saturday, 19 July 2014 at 15:27:27 UTC, Ali Çehreli wrote:
 On 07/19/2014 04:53 AM, Uranuz wrote:

 When I tried to use std.conv.to to serialize enum values that
are based
 on string type I faced with situation that *to* not exactly
as I
 expected.
Maybe I misunderstand you but the behavior is consistent for all enum types in both conversion directions. The value 1 does not appear anywhere in the following conversions, so "hello" should not appear either: import std.conv; enum I : int { a = 1 } enum S : string { a = "hello" } void main() { assert(I.a.to!string == "a"); assert(S.a.to!string == "a"); assert("a".to!I == I.a); assert("a".to!S == S.a); } Doing anything special for a particular type would be confusing and make at least template programming hard. Ali
In this case I see than we exactly doing something special when converting to string type. Using this logic without doing something special when converting enum I : int { a = 1 } we should try to convert it's *identifier* into int and it should generate error. But we don't do it, because we convert it's underlying value into int (not an identifier). But in case with string we doing specific conversion (that looks unreasonable for me), because we convert not it's *value* but it's identifier. I think that we shouldn't touch enum value *identifier* and ALWAYS convert underlying value. It will be absolutely clear to explain in documentation. This is why I disagree with current implementation, because *to* set of functions is not toString function in objects (that tries to print pretty string), but it is utilitary function often used in template code and it should have uniform semantics for all enum types
Jul 19 2014
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Uranuz:

 This is why I disagree with current implementation,
The best that you can hope is having another different little function in Phobos that does what you ask for. Bye, bearophile
Jul 19 2014
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/19/2014 09:29 AM, Uranuz wrote:

 In this case I see than we exactly doing something special when
 converting to string type.
Ok, now I see what you mean. The title of the thread is a little confusing because actually you are not talking only about string-based enums. The issue is, since there is already .stringof to get the string representation of the identifier, to!string should be for the value. So there are two WATs in the following program for the two enums with int and string base types: import std.conv; struct MyStruct { string s; } void main() { enum I : int { a = 1 } assert(I.a.stringof == "a"); // the identifier assert(I.a == 1); // the value assert(I.a.to!int == 1); // the value assert(I.a.to!double == 1); // the value // ... same for other to!T ... assert(I.a.to!string == "a"); // WAT? enum S : string { a = "hello" } assert(S.a.stringof == "a"); // the identifier assert(S.a == "hello"); // the value assert(S.a.to!MyStruct == MyStruct("hello")); // the value // ... same for other to!T ... assert(S.a.to!string == "a"); // WAT? } I agree with that representation of the problem but as bearophile says, it is probably too late to make such a change. Ali
Jul 19 2014
parent reply "Uranuz" <neuranuz gmail.com> writes:
 Ok, now I see what you mean. The title of the thread is a 
 little confusing because actually you are not talking only 
 about string-based enums.
It's OK. It happened because I understood this problem myself in this way only during the discussion. So as I think there are 2 options. First is keep it al *as is*. In this case I propose to improve documentation about it that in case of conversion of enums into string types and vice versa we actualy converting *identifiers* but not underlying values. The second option is to change it to conversions between underlying values. In this case we need to evaluate how much code will be broken and take some measures to warn developers about these changes. I think more feedback is needed about this to rethink more suitable solution. P.S. I maybe explain it not so well. It's because of my bad English.
Jul 19 2014
parent "bearophile" <bearophileHUGS lycos.com> writes:
Uranuz:

 So as I think there are 2 options.
There is a third option, that is to add a little extra function that does what you want.
 First is keep it al *as is*. In this case I propose to improve 
 documentation about it that in case of conversion of enums into 
 string types and vice versa we actualy converting *identifiers* 
 but not underlying values.
Improving the documentation is good.
 The second option is to change it to conversions between 
 underlying values. In this case we need to evaluate how much 
 code will be broken and take some measures to warn developers 
 about these changes.
Time ago I opened an enhancement request that essentially asks this, and it was closed down. So I think this is not an option. (And now I have code that relies on the current behavour). Bye, bearophile
Jul 19 2014