What you want to do has been tried before by many people. Of course it's possible but requires some tricks (similar to the ones required to make streaming loggers).
It also turns out to be a bad idea because:
it couples the concept of streaming to the concept of an exception.
It can be done more simply with a single template function
In fact, here are 3 very simple alternatives:
#include <iostream>
#include <sstream>
#include <exception>
#include <stdexcept>
#include <boost/format.hpp>
template<class...Args>
std::string collect(Args&&...args)
{
std::ostringstream ss;
using expand = int[];
void(expand{0, ((ss << args), 0)...});
return ss.str();
}
struct collector : std::ostringstream
{
operator std::string() const {
return str();
}
};
// derive from std::runtime_error because a network exception will always
// be a runtime problem, not a logic problem
struct NetworkException : std::runtime_error
{
using std::runtime_error::runtime_error;
};
int main()
{
try {
throw NetworkException(collect("the", " cat", " sat on ", 3, " mats"));
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
try {
throw NetworkException(collector() << "the cat sat on " << 3 << " mats");
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
try {
throw NetworkException((boost::format("the cat sat on %1% mats") % 3).str());
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
expected output:
the cat sat on 3 mats
the cat sat on 3 mats
the cat sat on 3 mats
And finally, probably the most stream-like solution:
template<class Exception>
struct raise
{
[[noreturn]]
void now() const {
throw Exception(_ss.str());
}
std::ostream& stream() const { return _ss; }
mutable std::ostringstream _ss;
};
template<class Exception, class T>
const raise<Exception>& operator<<(const raise<Exception>& r, const T& t)
{
using namespace std;
r.stream() << t;
return r;
}
struct now_type {};
static constexpr now_type now {};
template<class Exception>
void operator<<(const raise<Exception>& r, now_type)
{
r.now();
}
call site example:
raise<NetworkException>() << "the cat " << "sat on " << 3 << " mats" << now;
I've used the sentinel now
in order to avoid any nasty destructor jiggery-pokery.