Как стать автором
Обновить

К вопросу о U-Boot

Время на прочтение2 мин
Количество просмотров4.1K

Найди всему причину и ты многое поймешь


Недавно, просматривая код U-Boot в части реализации SPI, наткнулся на макрос обхода списка доступных устройств, которые после нескольких переходов сбросил меня на макрос container_of. Сам текст макроса наличествовал и я с легким изумлением увидел, что он несколько отличается от ранее виденной мною версии. Было проведено небольшое расследование, которые привело к интересным результатам.

Сам по себе макрос известен давно и решает несколько странную задачу: у нас имеется указатель на поле некоторой структуры (ptr), нам известен тип структуры (type) и название поля (member) и необходимо получить указатель на структуру в целом. Я не очень понимаю, как такая задача могла появиться, возможно, авторам «хотелось странного», но, раз задача поставлена, ее надо решить. Решение общеизвестно:

#define container_of(ptr, type, member) \
  ((type *)((char *)(ptr)-offsetof(type,member)))

Все прозрачно и понятно, но обнаруженная реализация была несколько сложнее:

#define container_of(ptr, type, member) ({			\
  const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
  (type *)( (char *)__mptr - offsetof(type,member) );})

Вторая строка почти идентична первому решению, но что делает первая строка макроса? Нет, что она делает, как раз понятно: создает константный локальный указатель на поле структуры и присваивает ему значение параметра макроса — указателя на поле. А вот зачем это делается, неясно.

Очевидное соображение относительно назначения первой строки — проверка первого параметра макроса в том, что он действительно представляет собой указатель на поле структуры в стиле:

 int *x = (X)

которая в данном случае принимает несколько заумный вид:

typeof( ((type *)0)->member ) *__mptr = (ptr)

поскольку требуемый для проверки тип приходится конструировать «на лету».

Ладно, согласимся, вещь полезная, правда, пришлось использовать полученный указатель во второй строке, чтобы избежать предупреждений компилятора.

Но зачем модификатор константности — макрос должен прекрасно работать и без этого дополнения (я тут же попробовал и получилось). Не могли же авторы поставить его случайно.

Туда смотреть не обязательно
Должен признаться, что мне подсказали ответ, я сам не догадался.

Оказывается, данный модификатор просто необходим, если мы попытаемся узнать адрес константной структуры, поскольку в этом случае от компилятора потребуется invalid conversion from 'const xxx*' to `xxx*'.

Интересно, что константность самого поля внутри обычной структуры не только не требует приведения в макросе, но и снимает его необходимость при константной структуре, то есть выражение типа:

struct s1 {
	int data;
	const int next;
};
	const struct s1 ss1 = {314,0};
container_of(&(ss1.next),struct s1,next);

компилируется без ошибок и без модификатора в тексте макроса, а выражение:

struct s1 {
	int data;
	int next;
};
	const struct s1 ss1 = {314,0};
container_of(&(ss1.next),struct s1,next);

его обязательно требует.

Для тех читателей Хабра, кто хорошо знает стандарт языка, мои открытия покажутся смешными в стиле «Спасибо, капитан», но для остальных могут быть небезынтересны.
Теги:
Хабы:
+12
Комментарии2

Публикации

Изменить настройки темы

Истории

Ближайшие события