( If you dont’t understand why, dont’t worry it will be obvious to you in 3 years :-)
( If you dont’t understand why, dont’t worry it will be obvious to you in 3 years :-)
Not to mention Go having multiple return values IMHO also takes away the major pain point of returning errors in C; using up your only return value and having to take pointer arguments when you would rather not.
#ifdef _WIN32
# ifdef EXPORTING
# define DECL __declspec(dllexport)
# define STORAGE
# else
# define DECL __declspec(dllimport)
# define STORAGE extern
# endif
#else
# define DECL
# define STORAGE
#endif
// For every return tuple type actually used:
STORAGE template class DECL std::tuple<error_type, return_type1>;
STORAGE template class DECL std::tuple<error_type, return_type2>;
STORAGE template class DECL std::tuple<error_type, return_type3, return_type4>;
// etc.
Even without the above, exposing STL across shared objects leads to the games of "which side of the shared object did this get created" and "which STL did this shared object link against". Losing these games leads to sizeable butt pain.This alone makes exporting STL objects pretty much impossible in a sizable cross-platform project, in my experience.
It's only the workaround (tuples) of this C++ issue that platform-specific issues are allowed to arise, like Windows as you observed, which is why I view the platform-specific issues as symptoms of a deeper problem.
> if multiple return types were natively supported by the C++ language, none of this would be needed since no one would be trying to export a STL object across shared object (or DLL) boundaries.
I believe you're talking about C++ export. Everybody will say that export was too complicated to implement; although (1) the full sentence should be "too complicated to implement, using current linkers," and (2) both CFront and EDG implemented did implement export using current linkers CFront implemented it in the early '90s), so the argument that it can't be implemented is simply wrong. It is accurate to say "using current linkers, refusing to use nonportable linker features, and following the Borland template instantiation model ( https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.ht... ), it is hard to implement export; only EDG ever paid the price to do so."
But the C macros to #declare dllimport/dllexport (which is what I thought your original complaint was) are meant to solve a Windows linking issue that plagues C as much as it plagues C++. And, to be honest, it's a link time optimization that non-Microsoft linkers don't appear to have trouble with.
i.e.,
int foo_new(foo_t **res);
int foo_mutate_copy(foo_t *foo, bar_t *bar, foo_t **new_foo);
Instead of foo_t *foo_new(); // returns NULL on failure
foo_t *foo_mutate_copy(foo_t *foo, bar_t *bar); // returns NULL on failure
I also like the Go way, just thought this way of doing things in C should be mentioned in the thread.I specifically mention the C way of doing things- but use you demonstrate it above. I was suggesting the Go way is superior.
I don't like that I can't locally hide the boiler plate. There's obviously some measure of essential tension there; I could cope.
I still haven't gotten over the use of product types for return values rather than sum types, and I'm not sure if I will.
Also, with Go's approach you have to either explicitly ignore the error or deal with it.
If I'm doing something wrong here I'd love to know it.
- you can collect errors in a function and return them once or provide an extra method, like http://golang.org/pkg/bufio/#Scanner.Err
- you can type switch on the error; maybe there are errors that you can recover/retry locally, such as connection errors, or general io failures - so you do not need to pass back all errors to the caller
More on errors: https://blog.golang.org/errors-are-values
I do find littering code with err checks can be annoying and ended up reaching a compromise. For all code that is likely to be part of an externally public package I use 'if err !=' checks. For application level code that will be internal to my application (and internal to the file I'm working in) I use a small library [1] I wrote, that uses the panic-defer-recover style talked about in the Go documents for internal error handling [2].
However, I find that in most situations I usually prefer to not use panic-defer-recover, with if checks providing more clarity.
[1] https://github.com/surullabs/fault [2] http://golang.org/doc/effective_go.html#recover
I feel like so many developers have been so damaged by Java's approach to exception handling that, of course, error codes seem simpler by comparison. I can't help by feel the recent push-back against exceptions is a step backwards in many situations. A whole generation of developers will have to re-learn the pain that lead to the development of exceptions in the first place.