First of all you should flip template arguments. Source
can be auto-deduced on other hand Target
can't. So Target
must be explicitly provided and should come first in template parameter list, so Source
could be deduced anyway.
Second problem is that string literals can't be added like that (this comes from C). To build complex strings in C++ use std::ostringstream
.
To get type name information you can use typeid
. Since this name is mangled and you already use boost
, you can demagle that names using boost
and get nice human readable type name.
Last thing: direct use of std::runtime_error
is manifestation of lazy developer. It is a good practice to introduce own exception class for such scenarios.
#include <iostream>
#include <sstream>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/core/demangle.hpp>
#include <typeinfo>
#include <stdexcept>
template<typename T>
std::string typeName()
{
return boost::core::demangle(typeid(T).name());
}
// this is used to bind all exceptions related to local library
class MyLibExceptions : public std::exception
{};
class BadNumericCast : public MyLibExceptions
{
public:
template<typename Target, typename Source>
BadNumericCast(Source arg, Target, const char *extra)
{
std::ostringstream desc;
desc << extra << " from type: '" << typeName<Source>()
<< "' with value: " << arg
<< " to type: '" << typeName<Target>() << '\'';
mDesc = desc.str();
}
const char* what() const noexcept override
{
return mDesc.c_str();
}
private:
std::string mDesc;
};
template <typename Target, typename Source>
Target numeric_cast(Source arg)
{
try
{
return boost::numeric::converter<Target, Source>::convert(arg);
}
catch(boost::numeric::bad_numeric_cast& e)
{
throw BadNumericCast{arg, Target{}, e.what()};
}
}
Live demo