Re: Onwards and upwards
On Friday, April 3, 2015 at 8:12:03 AM UTC-5, =D6=F6 Tiib wrote:
On Thursday, 2 April 2015 07:51:11 UTC+3, woodb...@gmail.com wrote:
What do you think of this class?
Average class.
Here's an updated version
class failure : public ::std::exception {
::std::string whatStr;
public:
explicit failure (char const* w) : whatStr(w) {}
explicit failure (::std::string w) : whatStr(::std::move(w)) {}
char const* what () const throw()
{ return whatStr.c_str(); }
failure& operator<< (char const* s)
{
whatStr.append(s);
return *this;
}
failure& operator<< (char* s)
{
whatStr.append(s);
return *this;
}
failure& operator<< (::std::string const& s)
{
whatStr.append(s);
return *this;
}
template <class T>
failure& operator<< (T val)
{
using ::std::to_string;
return *this << to_string(val);
}
};
I took your advice and removed the destructor and added
the using statement. I'm not sure what the advantage
would be to changing that signature from char const* to
char const [] so I didn't change that.
class failure : public ::std::exception {
::std::string whatStr;
public:
explicit failure (char const* w) : whatStr(w) {}
Can use alternative syntax 'char const s[]' to indicate that C string
is expected.
explicit failure (::std::string w) : whatStr(::std::move(w)) {}
~failure () throw() {}
Feels pointless to indicate 'noexcept' here since compiler can see it
itself.
I don't think of programs as selves. I'd compare a compiler
to a rock or a shovel.
You can indicate that destructor is default and virtual like:
virtual ~failure() = default;
However since you do not define neither assignments nor
copy-constructions of 'failure' it feels better to remove the
destructor as well by rule of zero.
char const* what () const throw()
{ return whatStr.c_str(); }
Can indicate that member function is virtual override (with keywords
'virtual' and 'override').
failure& operator<< (char const* s)
{
whatStr.append(s);
return *this;
}
failure& operator<< (char* s)
{
whatStr.append(s);
return *this;
}
That looks unneeded overload; its body is identical to previous overload.
It looks that way to me too, but I've not been able to
get rid of it. If I remove it, the compiler chooses
between the char const* and the templated operator<<.
Rather than picking the char const* version, it picks
the templated version and then gives errors. Previously
on the newsgroup I mentioned this and someone suggested
using enable_if, but I wasn't crazy about that idea.
failure& operator<< (::std::string const& s)
{
whatStr.append(s);
return *this;
}
template <class T>
failure& operator<< (T val)
{
whatStr.append(::std::to_string(val));
return *this;
}
Can use:
using std::to_string;
whatStr.append(to_string(val));
That will consider likely 'to_string's from T's namespace in name lookup.
};
I'm thinking about changing it to something like this:
class failure : public ::std::string {
public:
explicit failure (char const* w) : std::string(w) {}
explicit failure (::std::string w) : std::string(::std::move(w)) {}
~failure () throw() {}
template <class T>
failure& operator<< (T val)
{
this->append(::std::to_string(val));
return *this;
}
};
Some preliminary tests show smaller executable sizes with
this second approach.
Technically it is 'catch (std::string const&)' for user?
Performance considerations are always less important than usability
considerations so I usually derive exception types that may be
thrown out of my code from 'std::runtime_error'. Better usability
for caller, microscopic difference in performance.
If you really need such microscopic performance benefits then better
throw enwrapped into 'std::exception' string literals or integers.
IOW do not build long texts at throw site. For example: If catch
site can't do anything with long text that explains in detail what
strange byte sequence did not let a parser to parse the input,
then do not build such thing at throw site and executable size is
immediately smaller too.
That seems to be easier said than done. I'm not sure who
does that.
Brian
Ebenezer Enterprises - In G-d we trust.
http://webEbenezer.net