www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Nothrow std.conv.to with explicit default value

reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
I have a nothrow variant of std.conv.to defined as follows:

T toDefaulted(T, S, U)(S value, /*lazy*/ U defaultValue)
if (is(typeof(() { T r = defaultValue; }))) // TODO use 
std.traits.isAssignable!(T, U) ?
{
     try
     {
         import std.conv : to;
         return value.to!T;
     }
     catch (Exception e) // assume `ConvException`. TODO can we 
capture `ConvException` instead make it inferred `nothrow`
     {
         return defaultValue;
     }
}

The problem with this code is that throwing exceptions for the 
default case is costly at least with dmd. Is there another way to 
do this?
Jun 18 2018
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 18 June 2018 at 20:48:55 UTC, Per Nordlöw wrote:
 T toDefaulted(T, S, U)(S value, /*lazy*/ U defaultValue)
 if (is(typeof(() { T r = defaultValue; }))) // TODO use 
 std.traits.isAssignable!(T, U) ?
why not just make it T toDefaulted(T, S)(S value, T defaultValue) and forget U entirely?
 The problem with this code is that throwing exceptions for the 
 default case is costly at least with dmd. Is there another way 
 to do this?
It depends on what the types are. If it is like string to int, you can simply scan the string for the appropriate format. For other conversions though, I don't think you can tell in general. User-defined types might not even tell you except by exception.
Jun 18 2018
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/18/18 4:48 PM, Per Nordlöw wrote:
 The problem with this code is that throwing exceptions for the default 
 case is costly at least with dmd. Is there another way to do this?
Yes, have an internal implementation which doesn't throw, but rather returns an error code. Then you can call that and throw or use default value based on the return value. It just means re-doing std.conv.to, which is pretty hairy, but also pretty well-organized. -Steve
Jun 18 2018
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Monday, 18 June 2018 at 21:10:03 UTC, Steven Schveighoffer 
wrote:
 It just means re-doing std.conv.to, which is pretty hairy, but 
 also pretty well-organized.
Ok. Where in std.conv do the string-to-enum conversions take place?
Jun 20 2018
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 20 June 2018 at 09:27:14 UTC, Per Nordlöw wrote:
 On Monday, 18 June 2018 at 21:10:03 UTC, Steven Schveighoffer 
 wrote:
 It just means re-doing std.conv.to, which is pretty hairy, but 
 also pretty well-organized.
Ok. Where in std.conv do the string-to-enum conversions take place?
AFAICT, string-to-enum-conversion must include a switch containing a static foreach over all the enumerators, right?
Jun 20 2018
next sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 20 June 2018 at 09:37:00 UTC, Per Nordlöw wrote:
 AFAICT, string-to-enum-conversion must include a switch 
 containing a static foreach over all the enumerators, right?
My suggestion for nothrow nogc string-to-enum conversion with default value T toDefaulted(T)(scope const(char)[] value, T defaultValue) safe pure nothrow nogc if (is(T == enum)) { switch (value) { static foreach (index, member; __traits(allMembers, T)) { case member: return cast(T)(index); } default: return defaultValue; } } safe pure nothrow /*TODO nogc*/ unittest { enum E { unknown, x, y, z } assert("x".toDefaulted!(E)(E.init) == E.x); assert("_".toDefaulted!(E)(E.init) == E.unknown); }
Jun 20 2018
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 20 June 2018 at 09:52:04 UTC, Per Nordlöw wrote:
 My suggestion for nothrow  nogc string-to-enum conversion with 
 default value


 T toDefaulted(T)(scope const(char)[] value,
                  T defaultValue)  safe pure nothrow  nogc
 if (is(T == enum))
 {
     switch (value)
     {
         static foreach (index, member; __traits(allMembers, T))
         {
         case member:
             return cast(T)(index);
         }
Oops, this doesn't work for enums with "holes". How do I most easily fix that?
Jun 20 2018
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 20 June 2018 at 09:54:29 UTC, Per Nordlöw wrote:
 T toDefaulted(T)(scope const(char)[] value,
                  T defaultValue)  safe pure nothrow  nogc
 if (is(T == enum))
 {
     switch (value)
     {
         static foreach (index, member; __traits(allMembers, T))
         {
         case member:
             return cast(T)(index);
         }
Oops, this doesn't work for enums with "holes". How do I most easily fix that?
This is my solution: T toDefaulted(T)(scope const(char)[] value, T defaultValue) safe pure nothrow nogc if (is(T == enum)) { switch (value) { static foreach (member; __traits(allMembers, T)) { case member: mixin(`return T.` ~ member ~ `;`); } default: return defaultValue; } } Is there a way to avoid compile-time-string-concat plus mixin here?
Jun 20 2018
parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 20 June 2018 at 14:39:48 UTC, Per Nordlöw wrote:
 Is there a way to avoid compile-time-string-concat plus mixin 
 here?
Using __traits(getMember, ...) should compile faster, right? T toDefaulted(T)(scope const(char)[] value, T defaultValue) safe pure nothrow nogc if (is(T == enum)) { // doesn't need `std.conv.to` switch (value) { static foreach (member; __traits(allMembers, T)) // prevents call to slower `EnumMembers` { case member: return __traits(getMember, T, member); // NOTE this is slower: mixin(`return T.` ~ member ~ `;`); } default: return defaultValue; } }
Jun 21 2018
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, June 20, 2018 09:37:00 Per Nordlöw via Digitalmars-d-learn 
wrote:
 On Wednesday, 20 June 2018 at 09:27:14 UTC, Per Nordlöw wrote:
 On Monday, 18 June 2018 at 21:10:03 UTC, Steven Schveighoffer

 wrote:
 It just means re-doing std.conv.to, which is pretty hairy, but
 also pretty well-organized.
Ok. Where in std.conv do the string-to-enum conversions take place?
AFAICT, string-to-enum-conversion must include a switch containing a static foreach over all the enumerators, right?
If you want to know where std.conv deals with converting enums, look for EnumMembers, since that's the trait to get all of the members of an enum. - Jonathan M Davis
Jun 20 2018