我正在尝试开发一个Exception类,它允许收集相关的数据流样式。
关注Custom stream to method in C++?我扩展了自己的课程:
class NetworkException : public std::exception, public std::ostream
从流中选择错误数据,然后返回通过.what()
获取的任何内容。
然后我尝试了这样的事情:
try {
ssize_t ret = send(sock, headBuffer, headLength, MSG_MORE);
if (ret <= 0) throw NetworkException() << "Error sending the header: " << strerror(errno);
// much more communication code
} catch (NetworkException& e) {
connectionOK = false;
logger.Warn("Communication failed: %s",e.what());
}
但编译器会产生错误:
HTTPClient.cpp:246:113: error: use of deleted function 'std::basic_ostream<char>::basic_ostream(const std::basic_ostream<char>&)'
(那是throw
的行。)
我意识到流没有复制构造函数,但我认为捕获引用而不是对象就足够了。我怎样才能克服这个问题 - 我可以抛弃一个吞下的物体吗?一条小溪?
答案 0 :(得分:2)
许多人以前曾尝试过你想做的事情。当然这是可能的,但需要一些技巧(类似于制作流式记录器所需的技巧)。
这也是一个坏主意,因为:
它将流媒体的概念与异常的概念结合起来。
使用单一模板功能
事实上,这里有3个非常简单的选择:
#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;
}
预期产出:
the cat sat on 3 mats
the cat sat on 3 mats
the cat sat on 3 mats
最后,可能是最像流的解决方案:
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();
}
致电网站示例:
raise<NetworkException>() << "the cat " << "sat on " << 3 << " mats" << now;
我使用了哨兵now
以避免任何讨厌的析构者jiggery-pokery。
答案 1 :(得分:1)
异常总是至少复制一次。通过引用捕获异常避免了第二个副本。
只是一个想法:也许你可以将你的流嵌入一个智能指针,如@Before
,然后抛出那个智能指针。
我个人通常在任何地方使用https://en.wikipedia.org/w/api.php?action=query&prop=pageimages&titles=Jaguar&pithumbsize=500
。
答案 2 :(得分:1)
除了ZunTzu的回答:改为使用ostringstream。
::std::ostringstream what;
what << "Error sending the header: " << strerror(errno);
throw ::std::exception(what.str());
实际上,:: std :: exception没有构造函数接受char const *或:: std :: string const&amp;,因此您需要使用适当的现有subclass或创建自己的{。}}。
答案 3 :(得分:1)
不,你不能。抛出异常时,将从 exception-expression 构造临时对象。由于无法从另一个流对象构造流对象,因此无法抛出流对象。我可以投一个流吗?
来自C ++ 11标准:
15.1抛出异常
3 throw-expression 初始化一个名为异常对象的临时对象,其类型通过删除任何顶级 cv-qualifiers来确定来自
throw
的操作数的静态类型,并将类型从“T的数组”或“返回的函数T”调整为“指向T的指针”或“指向函数返回T的指针”。临时值是一个左值,用于初始化匹配的处理程序(15.3)中命名的变量。
答案 4 :(得分:1)
您不能使用默认复制构造函数复制包含流对象的对象,但您可以编写自己的复制构造函数来复制流的内容。
#include <iostream>
#include <sstream>
struct StreamableError : public std::exception {
template <typename T>
StreamableError& operator << (T rhs) {
innerStream << rhs;
return *this;
}
StreamableError() = default;
StreamableError(StreamableError& rhs) {
innerStream << rhs.innerStream.str();
}
virtual const char* what() const noexcept {
str = innerStream.str(); //this can throw
return str.c_str();
}
private:
std::stringstream innerStream;
mutable std::string str;
};
int main() {
try {
throw StreamableError() << "Formatted " << 1 << " exception.";
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
}
上述解决方案并不完美。如果str = innerStream.str()
抛出,则会调用std::terminate
。如果您想让what
方法成为noexcept
,那么您有两种选择:
str
变量。然后,在抛出之前,您将无法调用what
方法来获取异常消息。str
运算符中的<<
变量。在这种情况下,您将能够在throw之前获取异常消息,但是每次调用operator时您都将复制消息,这可能是一种过度杀伤。答案 5 :(得分:1)
更简单的做法是不存储std::ostringstream
,如下所示:
struct NetworkException : std::exception
{
using std::exception::exception;
template <class T>
NetworkException& operator<<(const T& arg) {
std::ostringstream oss;
oss << arg;
err.append(oss.str());
return *this;
}
const char* what() const noexcept override {
return err.c_str();
}
private:
std::string err;
};