在socket编程中,经常会需要判断某函数是否成功,若不成功,则退出的操作。
如:
if (socket(
domain , type, protocol ) == -1) {
exit(-1);
}
为了不用每次调用socket都加上这句判断,可以将这种函数封装,调用封装函数。
void checkSocket(int domain, int type, int protocol) {
if (socket( domain , type, protocol ) == -1) {exit(-1);}
}
但问题是像这种函数有不少,如connect、listen等等。每个函数的参数也会有不同,且错误条件不同,有的是返回-1,有的是返回小于0的数。每个函数都去封装一次还是很麻烦。
利用C++变长模板参数和lambda表达式可以比较方便的解决问题,大体想法如下所示。
template <typename F, typename Cond, typename... Args>
void check(F f, Cond cond, Args... args) {
if (cond(f(args...))) {
cout << "error!\n";
}
}
int openFile(int, int, int) {
return -1;
}
int openSocket(int, const char*) {
return -2;
}
int main()
{
check(openFile, [](int x) {return x == -1;}, 1, 1, 1);
check(openSocket, [](int x) {return x < 0;}, 1, "aha");
return 0;
}
openFile和openSocket是两个参数类型、数量以及返回值都不同的函数,我们想实现的操作时
if (openFile(x, y, z) == -1) {
cout << "error";
}
以及
if (openSocket(x, y) == -2) {
cout << "error";
}
本来需要封装两次的,但现在只需要checkF就只需要封装一次即可。
checkF有三个模板类型,F、Cond以及“类型包”Args。
F是待封装的函数,如openFile;
Cond是测试调价,如 "== -1",可以是函数指针或者是C++中的函数对象,也可以是C++11引入的lambda表达式(lambda表达式是匿名函数);
Args是F的所有参数,长度可变,当我们调用
check(openFile, [](int x) {return x == -1;}, 1, 1, 1);
时,编译器将把Args处理为三个int类型的参数,并在
if (cond(f(args...)))
中传给f。
上面方法的不足之处在于,checkF对失败的处理都是一样的(cout << "error\n";),
有时候需要不一样的处理。可以考虑再加入一个参数。