Уже неоднократно в блоге поднимал тему того, как использование возможностей C++ упрощает жизнь разработчику по сравнению с использованием теплой и ламповой "сишечки". Выдалось время показать еще один пример из недавней практики.
Временами при работе с сокетами нужно устанавливать те или иные опции. Если нужно просто дергать setsockopt, то в этом нет ничего сложного. Хотя, если нужно дернуть setsockopt много раз подряд для установки разных опций, то тиражирование простого кода методом копипасты наверняка приведет к тому, что где-то будут перепутаны значения level и optname.
Еще же веселее ситуация становится, когда кроме setsockopt приходится работать с msghdr, sendmsg и recvmsg. Тут мы быстро приходим к простому коду вида:
for (cm = CMSG_FIRSTHDR (&msg); cm; cm = CMSG_NXTHDR (&msg, cm)) { void *ptr = CMSG_DATA (cm); if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { struct timeval *tv = (struct timeval *) ptr; ... // bla-bla-bla } } else if (cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_RECVERR) { struct sock_extended_err * ee = (struct sock_extended_err *) ptr; ... // bla-bla-bla } } else if (...) ... } |
Код-то простой. Но, как мне представляется, писать и сопровождать его готовы люди, которые не боятся сложностей. Я, например, боюсь :)
Тут ведь есть не только грабли с постоянными проверками cmsg_type и cmsg_level. Гораздо веселее -- это расчет размера буфера, который нужен для приема cmsghdr структур. Тут совсем несложно заблудиться в трех соснах. Например, взять опцию SOL_SOCKET/SO_TIMESTAMP. При вызове setsockopt для ее установки нужно задействовать int, который будет работать как bool. А при расчете размера cmsghdr нужно отводить место под timeval. При этом не забывая еще и про CMSG_LEN.
В общем, колупаться с такими подробностями в стиле plain old C -- это то еще удовольствие, как по мне. Поэтому...