January 13, 2015
On 1/12/2015 3:17 PM, deadalnix wrote:
> On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:
>> Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].
>
> It is not that big of a deal, EDX is a trash register anyway if memory serve,
> but then, it become very bad when it do not fit in register anymore.

Returning a slice, for example, already consumes EAX and EDX. Adding an error code pushes that into returning via a pointer to a stack temporary.

Also, there's the code needed to load the value into EDX in the first place.

It adds up.
January 13, 2015
On Monday, 12 January 2015 at 23:29:08 UTC, H. S. Teoh via Digitalmars-d wrote:
> On Mon, Jan 12, 2015 at 11:17:24PM +0000, deadalnix via Digitalmars-d wrote:
>> On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:
>> >Yes, it does. Returning an int in EAX now becomes returning a pair
>> >[EAX,EDX].
>> 
>> It is not that big of a deal, EDX is a trash register anyway if memory
>> serve, but then, it become very bad when it do not fit in register
>> anymore.
>
> On the contrary, it's an extremely big deal. Registers are a rare
> resource, and allocating them for maximal usefulness is a major issue in
> code optimization.
>

These are trash register. Meaning the callee can put whatever in them. The caller must consider them trashed after the call.

So no, it do NOT increase register pressure.
January 13, 2015
On Tuesday, 13 January 2015 at 00:05:31 UTC, Walter Bright wrote:
> On 1/12/2015 3:17 PM, deadalnix wrote:
>> On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:
>>> Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX].
>>
>> It is not that big of a deal, EDX is a trash register anyway if memory serve,
>> but then, it become very bad when it do not fit in register anymore.
>
> Returning a slice, for example, already consumes EAX and EDX. Adding an error code pushes that into returning via a pointer to a stack temporary.
>
> Also, there's the code needed to load the value into EDX in the first place.
>
> It adds up.

Quoting myself:

> it become very bad when it do not fit in register anymore.

So we agree :)
January 13, 2015
On Monday, 12 January 2015 at 22:54:08 UTC, Dicebot wrote:
> Which is equivalent to "don't use exceptions on servers" :) Yes, I know, this is why any alternative approach is worth interest.

I think error handling chains like Maybe!(Result) or Either!(Error, Result) could be nicely implemented in a library. I thought about using something like that for error handling in the dub-registey.
January 13, 2015
On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote:
>> To defend that argument we'd first have to fix our own codegen.
>> https://issues.dlang.org/show_bug.cgi?id=12442
>
> That issue has nothing to do with exception handling vs error codes.

If you start to discuss register allocation than the actual cost of "zero-cost" EH has quite a lot to do with it.
January 13, 2015
On 1/12/2015 4:40 PM, deadalnix wrote:
> These are trash register. Meaning the callee can put whatever in them. The
> caller must consider them trashed after the call.
>
> So no, it do NOT increase register pressure.

1. the register must be assigned a value - that has a cost
2. functions often get inlined
3. it prevents EDX from being used by the return value, when the return value is larger than a single register size
4. static functions may not need to follow the C ABI register convention, and can be so optimized
January 13, 2015
"H. S. Teoh via Digitalmars-d" <digitalmars-d@puremagic.com> wrote:
> On Mon, Jan 12, 2015 at 06:06:19PM +0000, Tobias Müller via Digitalmars-d wrote:
>> C#'s Dictionary has TryGetValue that returns a bool in addition to the normal indexing operator []  that throws, exactly for that reason. And this is no exception (no pun intended), there are several cases like that in C#.  IMO there's something wrong if you have to resort to such code duplification.
> 
> And what exactly should operator[] return if a key wasn't found?
> 
> The only sane way is to have the user specify a default value if the key wasn't found, since not all types have a null value (and besides, what if null is a valid value in the dictionary?). IOW something like TryGetValue that takes multiple arguments instead of operator[].

Just stick with one.
Some kind of Optional<T> would be a good fit.

> You really should be using operator[] only when the key is expected to exist. If during normal operations there's a 50% chance the key doesn't already exist, you shouldn't be using [].

I know that and this is exactly the point. You have two methods that do almost exactly the same just with different error handling and you have to choose based on what the caller considers an error.

It's often the case that only the caller can decide what is an error and what not. The designer of an API has to predict all those cases and provide different methods for each case.

The problem with exceptions is, that the *callee* has to decide, not the caller.
January 13, 2015
On Monday, 12 January 2015 at 23:18:52 UTC, bearophile wrote:
> Ola Fosheim Grøstad:
>
>> (Most of std::C++ is optional, templated and inefficient... There is no consistent culture. Though they got some thing right with unique_ptr and new language features recently.)
>
> I don't agree. The basic ideas of STL by Alexander Stepanov are very good. Phobos contains related ideas, repackaged in ranges. Ranges are a little more fundamental, ma but in practice they are often good enough and they are often more handy. Rust has chosen a different way so far, but will improve with higher order generics later.
>
> Bye,
> bearophile

Lets not forget Alexander Stepanov created STL for Ada originally, and managed to convince the C++ committee to improve templates in a way that could properly support STL.

His ideas from generic programming precede C++.

--
Paulo
January 13, 2015
On 2015-01-12 19:28, H. S. Teoh via Digitalmars-d wrote:

> While I agree with the general sentiment, I think the current convention
> of using a class hierarchy to implement exceptions is suboptimal.
>
> The problem with using a class hierarchy is that, like anything put into
> a hierarchy, some things just don't fit very well in a single-rooted
> hierarchy. This is especially true in D because there is no multiple
> inheritance (and for good reason too; multiple inheritance brings with
> it a whole set of nasty problems).
>
> A recent example was what to do with an exception that wraps around
> OS-level errors. From an implementor's POV, it makes sense to segregate
> exception types by implementation, that is, ErrnoException for Posix
> systems and SystemErrorCodeException (or some such) for Windows.
> However, this is totally useless to the end user: when you're traversing
> the filesystem, under this scheme you'd have to catch ErrnoException or
> catch SystemErrorCodeException (with a static-if on OS type, aka utter
> ugliness), and then ferret out the specific error code(s) you wish to
> handle, like what to do with an I/O error vs. a permission-denied error.
> Why should the *user* have to work with low-level implementation details
> like mapping Posix errno's and their corresponding Windows error codes
> to the semantic categories of real interest: i.e., access error /
> hardware failure / network error, etc.?
>
> So from the user's POV, the exception hierarchy ought to be semantically
> driven, rather than implementationally driven. Instead of ErrnoException
> and SystemErrorCodeException, one ought to have semantic categories like
> FileNotFoundException, AccessDeniedException, NetworkException, etc..
> However, this is burdensome on the implementor, because now a single
> underlying implementation like ErrnoException now has to be split across
> multiple unrelated semantic categories (FileNotFoundException must have
> an errno field on Posix and a systemErrorCode field on Windows, ditto
> for NetworkException, IOException, etc. -- and you can't factor it out
> into a base class because the class hierarchy is semantics driven, so
> ErrnoException doesn't fit anywhere in the hierarchy).
>
> Recently Dmitry (IIRC) came up with the rather clever idea of using
> interfaces instead of a single-rooted hierarchy for marking up semantic
> categories instead. Instead of trying to decide on whether we should
> have implementationally-driven OSException with subclasses
> ErrnoException and SystemErrorCodeException, or semantically-driven
> FileSystemException with subclasses FileNotFoundException and
> AccessDeniedException, we can have both: the class hierarchy itself
> (i.e. without the interfaces) uses base classes for factoring out
> implementation details, but tag each exception type with semantic
> categories derived from a distinct interface hierarchy. So we could have
> something like this:
>
> 	// Implementational hierarchy
> 	class Throwable { ... }
> 	class Exception : Throwable { ... }
> 	class ErrnoException : Exception {
> 		int errno;
> 		...
> 	}
> 	class SystemErrorCodeException : Exception {
> 		int systemErrorCode;
> 		...
> 	}
>
> 	// Semantic tags
> 	interface FilesystemException;
> 	interface FileNotFoundException : FilesystemException;
> 	interface AccessDeniedException : FilesystemException;
> 	..
>
> 	// Actual implementations
> 	static if (Posix) {
> 		// Note: you can even have multiple exception types that
> 		// inherit from FileNotFoundException, e.g., one thrown
> 		// by a manual stat() check, and one from a common
> 		// routine that interprets OS error codes. The user
> 		// wouldn't need to know the difference.
> 		class FileNotFoundExceptionImpl :
> 			ErrnoException,
> 			FileNotFoundException { ... }
> 		... // other Posix-specific exceptions here
> 	} else static if (Windows) {
> 		class FileNotFoundExceptionImpl :
> 			SystemErrorCodeException,
> 			FileNotFoundException { ... }
> 		... // other Windows-specific exceptions here
> 	}
>
> So now, user code doesn't have to worry about implementation details
> like whether the exception came from errno or a Windows error code, you
> can just catch semantic categories like FileNotFoundException instead.
>
> Currently, however, this scheme doesn't quite work because the compiler
> only allows catching classes derived from Throwable, and interfaces
> can't derive from non-interface base classes. Besides, I'm not sure the
> druntime exception catching code can deal with interfaces correctly as
> opposed to just base classes. But this is definitely an area that D
> could improve on!

I prefer a semantically driven hierarchy. I agree with you that the user should not need to care if the exception originated from D code with a native D exception or in C code with a error code converted to a D exception.

The reason this gets complicated seems to be the need for preserving the error code. Is that actually necessary? If all error codes are properly mapped to a D exception in a nice semantically driven hierarchy I'm not so sure if the error code is needed.

It seems to me that the interface approach is quite complicated on the implementation side with a static-if and all. But I guess that can be factored out.

I was thinking there could be one function that converts a given error code to a D exception. This hopefully only needs to be written once. Then it will be easy both on the user side and on the implementation side.

If you really do need the original error code, perhaps we could put that in a exception in Throwable.next.

The implementation side of some operation could look like this:

void openFile (in char* path)
{
    FILE* file = fopen(path, "w");
    if (!file)
        throwSystemException();
}

The implementation of throwSystemException would be something like:

void throwSystemException ()
{
    static if (Posix)
        auto errorCode = errno();

    else
        auto errorCode = GetLastError();

    throw errorCodeToThrowable(errorCode);
}

Throwable errorCodeToThrowable (int errorCode)
{
    Throwable throwable;

    static if (Posix)
    {
        switch (errorCode)
        {
            case ENOENT: thorwable = new FileNotFoundException;
            ...
        }

        // not sure if this is necessary
        throwable.next = new ErrnoException(errorCode);
    }
    else { ... }

    return throwable;
}

-- 
/Jacob Carlborg
January 13, 2015
Ola Fosheim Grøstad:

> I've tried to like STL for 17 years, and whenever speed and clear programming matters it is basically a good idea to throw it out.

Take a look at the ideas of "C++ seasoning" (http://channel9.msdn.com/Events/GoingNative/2013/Cpp-Seasoning ), where they suggest to do kind of the opposite of what you do, it means throwing out loops and other things, and replacing them with standard algorithms.


> A solution like list comprehensions is a lot easier on the programmer, if convenience is the goal.

There's still time to add lazy and eager sequence comprehensions (or even better the computational thinghies of F#) to D, but past suggestions were not welcomed. D has lot of features, adding more and more has costs.


> Phobos "ranges" need a next_simd() to be efficient. Right?

Perhaps, but first std.simd needs to be finished.

Bye,
bearophile