Para a questão do U-Boot

Encontre um motivo para tudo e você entenderá muito.


Recentemente, observando o código U-Boot na parte da implementação do SPI, deparei-me com uma macro para ignorar a lista de dispositivos disponíveis, que após várias transições me redefinem para a macro container_of. O próprio texto macro estava presente e, com um leve espanto, vi que era um pouco diferente da versão que eu havia visto anteriormente. Uma pequena investigação foi realizada, o que levou a resultados interessantes.

A macro em si é conhecida há muito tempo e resolve um problema um tanto estranho: temos um ponteiro para um campo de alguma estrutura (ptr), sabemos o tipo de estrutura (tipo) e o nome do campo (membro), e precisamos obter um ponteiro para a estrutura como um todo. Eu realmente não entendo como essa tarefa poderia aparecer, talvez os autores "quisessem algo estranho", mas depois que a tarefa é definida, ela precisa ser resolvida. A solução é bem conhecida:

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

Tudo é transparente e claro, mas a implementação descoberta foi um pouco mais complicada:

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

A segunda linha é quase idêntica à primeira solução, mas o que a primeira linha da macro faz? Não, o que ele faz é apenas compreensível: ele cria um ponteiro local constante para o campo de estrutura e atribui a ele o valor do parâmetro macro - um ponteiro para o campo. Mas por que isso é feito não está claro.

Uma consideração óbvia em relação ao objetivo da primeira linha é verificar o primeiro parâmetro da macro que realmente é um ponteiro para um campo de estrutura no estilo:

  int *x = (X) 

que neste caso assume uma forma um tanto obscura:

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

já que o tipo necessário para verificação deve ser construído em tempo real.

Ok, vamos concordar, a coisa é útil, no entanto, eu tive que usar o ponteiro resultante na segunda linha para evitar avisos do compilador.

Mas por que o modificador de constância - a macro deve funcionar bem mesmo sem essa adição (tentei imediatamente e funcionou). Os autores não puderam colocá-lo por acidente.

Não é necessário olhar para lá
Devo admitir que eles me deram a resposta, eu mesmo não adivinhei.

Acontece que esse modificador é simplesmente necessário se tentarmos descobrir o endereço da estrutura constante, pois nesse caso o compilador precisará de uma invalid conversion from 'const xxx*' to `xxx*' .

É interessante que a constância do próprio campo dentro de uma estrutura comum não exija apenas coerção em uma macro, mas também elimine a necessidade de uma estrutura constante, ou seja, uma expressão como:

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

compila sem erros e sem um modificador no texto da macro e a expressão:

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

é necessário.

Para aqueles leitores de Habr que conhecem bem o padrão da língua, minhas descobertas parecerão ridículas no estilo de "Obrigado, capitão", mas, pelo resto, podem ser interessantes.

Source: https://habr.com/ru/post/pt466905/


All Articles