C++11 lambdas作为std::function params-dispatching on return ty
C++11 lambdas passed as std::function params - dispatching on return type
场景:
我们有一个API,它有一个通用的错误处理接口。它是一个C API,所以它告诉我们,在调用每个API函数后,我们需要执行一些样板文件,如
if (APIerrorHappened()) {
APIHandle h;
while(h = APIgetNextErrorContext(...)) {
cerr << APIgetText(h) << endl;
}
}
你显然不喜欢重复自己,所以你想把这种处理封装在一个宏中,允许你写这样的代码:
...
// this API call returns something
APItype foo = MYMACRO(APIsomeFunction1(...));
// this one doesn't
MYMACRO(APIsomeFunction2(...));
// neither does this (glCompileShader)
MYMACRO(APIsomeFunction3(...));
...
你也可以从面向方面的编程的角度来考虑这一点——想象一下,宏添加日志记录,向远程监视器发送信息,等等。。。重点是,它应该封装一个表达式,对其执行任何操作,并返回表达式返回的任何类型-当然,表达式可能不会返回任何。
这意味着你不能只做
#define MYMACRO(x) { auto x = expr(); ... }
因为在某些情况下,表达式不会返回任何内容!
所以。。。你会怎么做?
请不要建议将完整的语句封装在宏中。。。
#define MYMACRO(x)
{
/* ... stuff ... */
x;
// ... stuff
}
因为这对这样的东西永远不起作用
if (foo() || APIfunctionReturningBool(...) || bar()) {
...
APIfunction1();
...
} else if (APIOtherfunctionReturningBool() || baz()) {
...
APIfunction2();
...
}
你吞没了所有if语句?它的操作包括其他API调用,所以…宏中的宏?调试简直成了地狱。
我自己的尝试如下,使用lambdas和std::函数,但可以说,它很丑陋。。。我无法将表达式的lambda直接传递给采用std::函数的模板(根据lambda的返回类型进行专门化),因此代码变得相当糟糕。
你能想出更好的办法吗?
void commonCode(const char *file, size_t lineno) {
// ... error handling boilerplate
// ... that reports file and lineno of error
}
template <class T>
auto MyAPIError(std::function<T()>&& t, const char *file, size_t lineno) -> decltype(t()) {
auto ret = t();
commonCode(file,lineno);
return ret;
}
template<>
void MyAPIError(std::function<void(void)>&& t, const char *file, size_t lineno) {
t();
commonCode(file,lineno);
}
template <class T>
auto helper (T&& t) -> std::function<decltype(t())()>
{
std::function<decltype(t())()> tmp = t;
return tmp;
}
#define APIERROR( expr )
return MyAPIError( helper( [&]() { return expr; } ), __FILE__, __LINE__);
UPDATE,KennyTM卓越解决方案的附录
我在这里放置了引发这个问题的实际OpenGL代码。正如您所看到的,错误检查代码不仅仅是打印,它还引发了一个异常,用户代码可以处理该异常。我添加这个附录是为了注意,使用KennyTM的解决方案,您最终会从析构函数中抛出这个异常,并且这是可以的(继续阅读):
struct ErrorChecker {
const char *file;
size_t lineno;
~ErrorChecker() {
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
while (err != GL_NO_ERROR) {
std::cerr <<
"glError: " << (char *)gluErrorString(err) <<
" (" << file << ":" << lineno << ")" << std::endl;
err = glGetError();
}
throw "Failure in GLSL...";
}
}
};
从这个析构函数抛出是可以的原因,在C++FAQ:中有解释
C++规则是,决不能从另一个异常的"堆栈展开"过程中调用的析构函数中抛出异常。。。您可以说,在处理另一个异常时,永远不要从析构函数抛出异常。
在我们的例子中,我们希望用户代码(调用特殊宏)来处理异常;所以我们需要确定,我们在ErrorChecker的析构函数中的"抛出"是第一个,也就是说,实际调用的C API永远不能抛出。这很容易通过以下形式实现:
#define GLERROR(...)
([&]() -> decltype(__VA_ARGS__)
{
ErrorChecker _api_checker {__FILE__, __LINE__};
(void) _api_checker;
try {
return __VA_ARGS__;
} catch(...) {}
} ())
这种形式的宏保证了实际的C API(通过VA_ARGS调用)永远不会抛出-因此,ErrorChecker的析构函数中的"抛出"将始终是第一个执行此操作的对象
因此,这个解决方案涵盖了我最初问题的所有角度-非常感谢Alexander Turner提供的解决方案。
将日志记录代码放入某个类的析构函数中——假设记录器不会抛出——然后在宏中创建该类的实例。滥用逗号运算符,我们有:
struct Logger
{
const char* file;
int lineno;
~Logger()
{
// The APIerrorHappened stuff.
}
};
#define APIERROR(...) (Logger{__FILE__, __LINE__}, (__VA_ARGS__))
演示:http://ideone.com/CiLGR
- 瓦尔格林德:数学函数"Conditional jump or move depends on uninitialised value(s)"
- 无法理解此 return 语句的功能,没有它就会发生运行时错误
- C++函数中的精确"return"矩
- '[](std::list& list)<int>{return std::move(list)}(list)' 是否保证将 'list' 留空?
- 方法错误"not all control paths return a value"和方法不返回值
- 循环挂起迭代的 std::擦除 on std::list
- SIGSEGV on Boost UDP 套接字关闭 - tcache_get at malloc.c.
- CPP 中的瓦尔格林德和记忆泄漏:"Conditional jump or move depends on uninitialised values"
- GLFW glfwCreateWindowSurface return -7
- "Missing type specifier - int assumed"无法通过向主函数添加"return 0"来解决
- std::bind on statd::array 的运算符 []
- 您将如何连接"on the fly"文本+整数并将其传递给函数?
- 如何修复输出日志中的"EnableInput can only be specified on a Pawn for its Controller"错误
- 如何解决类成员函数中的"return a value"错误?
- VS2019 - Sudo Remote Debugging on Linux with Cmake project
- C++概念assignable_from不接受 const&-return 运算符=
- Malloc void return char 数组有时不起作用(Terry Davis 对 C++);
- Marshal.PtrToStructure on Android return null
- "Control reaches end on non-void function" with do { return result; } while(condition);
- C++11 lambdas作为std::function params-dispatching on return ty