手把手写C++服务器(13):C++11新特性之静态断言static_assert
Posted 沉迷单车的追风少年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手写C++服务器(13):C++11新特性之静态断言static_assert相关的知识,希望对你有一定的参考价值。
前言:我们学过很多断言,动态断言、零宽断言等等,这些在我之前的blog里都有总结。静态断言是C++11中的新技术,在服务端编程中经常使用,这里做一个学习总结。
目录
语法
static_assert(常量表达式,提示字符串)
如果第一个参数常量表达式的值为真(true或者非零值),那么static_assert不做任何事情,就像它不存在一样,否则会产生一条编译错误,错误位置就是该static_assert语句所在行,错误提示就是第二个参数提示字符串。
因为static_assert是编译过程中可计算的表达式,所以不能使用变量等,这样会造成编译报错。例如:
int func(int n) {
static_assert(n < 0, "n is over 0");
return 0;
}
因此,static_assert经常用于在代码的开头,预处理代码之后,用于检查一些编译支持错误、检查模板参数等。例如在编译前检查机器的位数是否是64位:
//该static_assert用来确保编译仅在32位的平台上进行,不支持64位的平台
//该语句可放在文件的开头处,这样可以尽早检查,以节省失败情况下耗费的编译时间
static_assert(sizeof(int) == 4, "64-bit code generation is not supported.");
优点
1、由于static_assert是编译期间断言,不生成目标代码,因此static_assert不会造成任何运行期性能损失。
2、使用static_assert,可以在编译期发现更多的错误,用编译器来强制保证一些契约,帮助我们改善编译信息的可读性,尤其是用于模板时。
3、static_assert可以用在全局作用域中,命名空间中,类作用域中,函数作用域中,几乎可以不受限制的使用。
示例
在网络编程中,不同平台上消息类型长度可能不一样,而我们进行消息通信的时候,会检查消息长度(参考UDP/TCP保证可靠性问题),此时如果平台的标度不统一,就会造成验证消息长度失败,连接往往建立失败。因此在编译期间,检查消息类型的长度,如果失败直接退出,使用static_assert抛出异常。
这是一个roundtrip轮转的demo,使用UDP测量两个机器之间网络的延迟,static_assert在19行。具体的业务逻辑会在下一讲详细分析。
#include "InetAddress.h"
#include "Socket.h"
#include <thread>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
const int g_port = 3123;
struct Message
{
int64_t request;
int64_t response;
} __attribute__ ((__packed__));
static_assert(sizeof(Message) == 16, "Message size should be 16 bytes");
int64_t now()
{
struct timeval tv = { 0, 0 };
gettimeofday(&tv, NULL);
return tv.tv_sec * int64_t(1000000) + tv.tv_usec;
}
void runServer()
{
Socket sock(Socket::createUDP());
sock.bindOrDie(InetAddress(g_port));
sock.bindOrDie(InetAddress(g_port));
while (true)
{
Message message = { 0, 0 };
struct sockaddr peerAddr;
bzero(&peerAddr, sizeof peerAddr);
socklen_t addrLen = sizeof peerAddr;
ssize_t nr = ::recvfrom(sock.fd(), &message, sizeof message, 0, &peerAddr, &addrLen);
if (nr == sizeof message)
{
message.response = now();
ssize_t nw = ::sendto(sock.fd(), &message, sizeof message, 0, &peerAddr, addrLen);
if (nw < 0)
{
perror("send Message");
}
else if (nw != sizeof message)
{
printf("sent message of %zd bytes, expect %zd bytes.\\n", nw, sizeof message);
}
}
else if (nr < 0)
{
perror("recv Message");
}
else
{
printf("received message of %zd bytes, expect %zd bytes.\\n", nr, sizeof message);
}
}
}
void runClient(const char* server_hostname)
{
Socket sock(Socket::createUDP());
InetAddress serverAddr(g_port);
if (!InetAddress::resolve(server_hostname, &serverAddr))
{
printf("Unable to resolve %s\\n", server_hostname);
return;
}
if (sock.connect(serverAddr) != 0)
{
perror("connect to server");
return;
}
std::thread thr([&sock] () {
while (true)
{
Message message = { 0, 0 };
message.request = now();
int nw = sock.write(&message, sizeof message);
if (nw < 0)
{
perror("send Message");
}
else if (nw != sizeof message)
{
printf("sent message of %d bytes, expect %zd bytes.\\n", nw, sizeof message);
}
::usleep(200*1000);
}
});
while (true)
{
Message message = { 0, 0 };
int nr = sock.read(&message, sizeof message);
if (nr == sizeof message)
{
int64_t back = now();
int64_t mine = (back + message.request) / 2;
printf("now %jd round trip %jd clock error %jd\\n",
back, back - message.request, message.response - mine);
}
else if (nr < 0)
{
perror("send Message");
}
else
{
printf("received message of %d bytes, expect %zd bytes.\\n", nr, sizeof message);
}
}
}
int main(int argc, const char* argv[])
{
if (argc < 2)
{
printf("Usage:\\nServer: %s -s\\nClient: %s server_hostname\\n", argv[0], argv[0]);
return 0;
}
if (strcmp(argv[1], "-s") == 0)
{
runServer();
}
else
{
runClient(argv[1]);
}
}
参考
以上是关于手把手写C++服务器(13):C++11新特性之静态断言static_assert的主要内容,如果未能解决你的问题,请参考以下文章
手把手写C++服务器:C++编译常见问题编译优化方法C++库发布方式