LINUX.ORG.RU

В чём смысл делать так_s постфиксить_t ?

 , , , ,


0

2
typedef struct name_s
{
   ....
}name_t

Сабж по постфиксам.

Я делаю всегда так

typedef struct name_t 
{
   ....
}name_t

И стараюсь не не дать так

typedef struct
{
   ....
}name_t

Ибо pahole и иже с ним не могут в анонимные. Но в чём практический смысл задавать и _s и _t одновременно просто постфиксы вносят ясность и смягчают уровень былого отвращения к typedef нивелируя тот упрёк что с typedef теряется ясность. Но вернёмся к постфиксам. Ну или префиксам для типов аля t_uint8 t_int64 вместо uint8_t int64_t может тут есть извращенцы я не знаю :D

ТайпдеТупедефаете вы как?

Именование типов с пробелами - это один из худших приемов в составе Си. Писатели стандартов со мной согласны и новые типы вводят без пробелов. Лично мне нет абсолютно никакой разницы, будет ли структура анонимна или обозвана - я предпочту всегда использовать однословный псевдоним. Да, в линуксе принято использовать всегда пробельные типы, вроде «struct device» и «unsigned long», так что если вы работаете с линуксом и пахомом, то у вас нет особого выбора - вам придется страдать. Хотя, даже там есть отщепенцы:
https://elixir.bootlin.com/linux/v3.6/source/tools/perf/util/types.h#L10

byko3y ★★ ()
  1. Конструкции с анонимной структурой typedef struct { ... } name_t; укачивают многие утилиты работающие с отладочной информацией.

  2. Конструкции с одним именем typedef struct name_t { ... } name_t; нередко укачивают утилиты навигации по исходному коду.

    Особенно плохо при forward-декларации структуры typedef struct name_t name_t; /* бла-бла-бла, много строк */ struct name_t { ... };. Многие навигаторы и IDE видят только typedef, напрочь забывая про определение самой структуры.

Deleted ()
Ответ на: комментарий от DllMain

это один из худших приемов в составе Си

Это вообще мелочь по сравнению с бедной стдлиб и неоднозначностями в стандартах

Вообще не понимаю аргумента по поводу бедности стандартной библиотеки. При чем тут она к языку? Вот проблема нуль-терминированных строк - намного более и серьезная, и из языка ее не так просто убрать, потому что нуль-терминированные строковые константы намертво приколочены к компилятору.

Неоднозначности? Размеры целых чисел/символов, или что? По сравнению с C++ стандарты Си просто нервно курят в сторонке.

Ну а по поводу пробелов мне кажется, что ты не понял фишки. Наличие пробела в идентификаторах приводит к тому, что парсер не может полагаться на пробел, как на элемент синтаксиса. Кроме того, что такой язык нельзя быстро распарсить, возникает более коварная беда - потеря возможности найти ошибку в коде там, где она произошла, вроде отсутствующей точки с запятой или скобки. Вместо этого ошибки проявляются спустя сотни строк и в других файлах. Или, может, я один с этим сталкиваюсь? Да, инклуды усугубляют проблемы, но без них проблема бы лишь ограничивалась одним файлом, и всё, что можно было бы сказать по ошибке компилятора - это что ошибка произошла между первой строкой и указанной в ошибке.

byko3y ★★ ()
Ответ на: комментарий от Deleted

Конструкции с анонимной структурой typedef struct { ... } name_t; укачивают многие утилиты работающие с отладочной информацией.
Конструкции с одним именем typedef struct name_t { ... } name_t; нередко укачивают утилиты навигации по исходному коду.
Особенно плохо при forward-декларации структуры

Да, для Си нельзя написать эффективный парсер. Потому целый ряд утилит срезает углы, получая проблемы. Лучше всего работает софт, использующий компилятор для парсенья, вроде основанных на clang или той же Visual Studio (последняя компилирует код без кодогенерации с целью навигации и выдачи ошибок).

byko3y ★★ ()

Обычно пишу так

typedef struct
{
   ....
}name_t;

Пока что проблем с этим не встречал.

Но в чём практический смысл задавать и _s и _t одновременно просто постфиксы вносят ясность и смягчают уровень былого отвращения к typedef

Почему typedef должен вызывать отвращение?

snake266 ()
Ответ на: комментарий от snake266

Я вообще не понимаю, зачем лично вам эти постфиксы. Зато, я понимаю, зачем они писателям библиотек и компиляторов - чтобы короткие идентификаторы, вроде «wchar», не смешались с объявленными в существующем софте. Свой софт я пишу сам, потому когда мне нужна структура точки, то я объявляю «typedef struct {...} point».

byko3y ★★ ()

t_uint8 t_int64

Как мне кажется такой способ записи усложняет работу с автодополнением, потому что с uint8_t int64_t ты ввёл тип и автодопление за тебя поставит _t, иначе придётся каждый раз тянутся к _

snake266 ()
Ответ на: комментарий от byko3y

При чем тут она к языку?

Как минимум потому, что приходится искать замену, коих есть в количестве более одной. Неясно, нафига эти сложности

Размеры целых чисел/символов, или что?

Я тебя умоляю. Например, использование _Alignas(T) char[sizeof(T)] для размещения объекта, алиасинг типов наподобие struct h { void* p; } и struct d { void* p; T d[N]; }(с union-ом хотя бы), memory model в многопоточном окружении и подобное.

По сравнению с C++ стандарты Си просто нервно курят в сторонке

Ну да, с std::launder особенно эпично было

Наличие пробела в идентификаторах приводит к тому, что парсер не может полагаться на пробел, как на элемент синтаксиса

Возможно неприятно, но в целом мелочь

DllMain ()
Ответ на: комментарий от t184256

Копирование более чем достаточный пример, ибо самая частая, наверное, операция. А так, выигрыш в любой операции, которой необходимо быстро получить размер стореджа под строку(та же конкатенация). Возможность bound check, исключение ошибок вида «забыли терминатор поставить и указатель побежал хз куда» и пр.

DllMain ()
Ответ на: комментарий от DllMain

Как минимум потому, что приходится искать замену, коих есть в количестве более одной. Неясно, нафига эти сложности

У Си есть удивительная возможность - работать без стандартной библиотеки. То есть, она как бы опциональна.

memory model в многопоточном окружении

Засчитываю.

использование _Alignas(T) char[sizeof(T)] для размещения объекта

В чем заключается неоднозначность? В том, что компиляторы не спешат реализовывать продвинутую поддержку выравнивания? Простые объявления ведь правильно выравниваются автоматически.

алиасинг типов наподобие struct h { void* p; } и struct d { void* p; T d[N]; }(с union-ом хотя бы)

Не понимаю, о чем речь.

Наличие пробела в идентификаторах приводит к тому, что парсер не может полагаться на пробел, как на элемент синтаксиса

Возможно неприятно, но в целом мелочь

И, тем не менее, в этом треде кучу ссылок на кривой софт, который не может правильно распарсить код на Си. Да, проблема вызвана более чем одной особенностью, но в том числе трудностью выделения идентификатора типа из кода.

byko3y ★★ ()
Ответ на: комментарий от t184256

проблема нуль-терминированных строк

Давай-ка поподробнее

Что именно поподробнее? Нуль-терминированные строки вшиты в компилятор? Вшиты. Кроме этого, они еще и вшиты в стандартную библиотеку, которая не заставляет, но все же активно подталкивает к их использованию. Конечно, можно сделать свои собственные строки - много кто делал, Plan 9 в том числе. Правда, про широкое распространение альтернативных строк я не слышал. В частности, потому что сторонние библиотеки продолжают линию стандартной библиотеки с нуль-терминированными строками.

Или тебя интересует, почему нуль-терминированные строки - это устаревшая еще в 80-х годах технология? Потому что низкая производительность конкатенции и выборки по индексу, склонность к переполнению буфера.

byko3y ★★ ()
Ответ на: комментарий от t184256

Как именно предлагаешь хранить/вычислять количество символов? количество байт?

Проблема давным-давно решена в юникодных строках: длина определяется чтением заголовка.

byko3y ★★ ()
Ответ на: комментарий от byko3y

У Си есть удивительная возможность - работать без стандартной библиотеки

Это хорошо, но с другой стороны я хотел бы избежать чего-то подобного(и это ещё кресты, там чуть получше)

_Alignas(T) char[sizeof(T)]

В чем заключается неоднозначность?

Я не нашёл подтверждения того, что

void init(storage_t* s)
{
  type_t t = {};
  memcpy(s, &t, sizeof(type_t));
}

void perform(const storage_t* s)
{
  const type_t* t = (const type_t*)s;
  t->...  //use object of type 'type_t'
}
является валидным кодом. С другой стороны - распространённая практика

Простые объявления ведь правильно выравниваются автоматически

Было бы всё так просто... Вообще, на C/C++ нет смысла писать, если не байто*бить, а здесь простыми объявлениями не обойдёшься

struct h { void* p; } и struct d { void* p; T d[N]; }

Не понимаю, о чем речь

Доступ к d::p через указатель на тип h

DllMain ()
Ответ на: комментарий от DllMain

Я не нашёл подтверждения того, что
void init(storage_t* s)
...
является валидным кодом. С другой стороны - распространённая практика

Это игра с огнем, и результат зависит лишь от понимания кодером того, что storage_t и type_t могут иметь разные размеры, в том числе относительно самих себя на разных платформах.

Простые объявления ведь правильно выравниваются автоматически

Было бы всё так просто... Вообще, на C/C++ нет смысла писать, если не байто*бить, а здесь простыми объявлениями не обойдёшься

Хорошо, есть некоторые подводные камни, из-за чего создатели компиляторов пришли к явному определению выравнивания и упакованным структурам.

struct h { void* p; } и struct d { void* p; T d[N]; }

Не понимаю, о чем речь

Доступ к d::p через указатель на тип h

Это связано с предыдущим, или является отдельной проблемой Си? Потому что в такой записи я и вовсе не вижу проблемы.

byko3y ★★ ()
Ответ на: комментарий от byko3y

игра с огнем ...

Размеры здесь ни при чём - с ними сложности нет

Потому что в такой записи я и вовсе не вижу проблемы

struct h { void* p; };
struct d { void* p; float fs[250]; };

void ururu(void)
{
  struct h h = {NULL};
  struct d d = {NULL};
  const struct h* ph = (struct h*)&d;
  ph->p;  //UB?
}
DllMain ()
Ответ на: комментарий от Sorcerer

А ты со случаем, когда одна структура содержит другую, не путаешь? Потому что тут явное нарушение strict aliasing rule.

hateyoufeel ★★★★★ ()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от DllMain

С другой стороны - распространённая практика

Это распространенная практика, потому что большая часть программистов на C, в том числе ярых фанатиков этого языка уровня Iron_bug, легко валятся на собеседовании вопросом про strict aliasing. И я вот даже не знаю, их ли это вина, потому что в учебниках это мало упоминают, а стандарт написан как будто бы жопой.

Поэтому да, лучше бы этот недоязык уже закопать и повсюду пользовать растохацкеллы и прочие жабокамлы.

hateyoufeel ★★★★★ ()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

легко валятся на собеседовании вопросом про strict aliasing

Легко валятся всякие самоуверенные недоучки, думающие, что тип выражения E1 в выражениях вида E1.E2 имеет какое-то значение для strict aliasing rule.

anonymous ()
Ответ на: комментарий от DllMain

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

AKonia ()
Ответ на: комментарий от DllMain

const struct h* ph = (struct h*)&d;
ph->p; //UB?

Вcё, я понял. Действительно, стандарт формально заявляет UB. Правда, ни один компилятор не выдаст здесь UB фактически. О чем думал комитет - мне не совсем ясно. Может быть, они решили, что слишком уж сложно будет описывать все подводные камни разыменования указателей после превращения указателей на несовместимые типы. При этом, сами-то указатели при условии совместимого выравнивания превращать можно.

byko3y ★★ ()
Ответ на: комментарий от hateyoufeel

Это распространенная практика, потому что большая часть программистов на C ... уровня Iron_bug

Ну конкретно касты вида char* -> struct_type*, выглядят корректными, но не прописанными в стандарт(каст struct_type* -> char* всегда норм)

стандарт написан как будто бы жопой

Дык, почти так и есть - в терминах частных случаев, и нужно по всему документу пробежаться, чтобы тот же strict aliasing понять

DllMain ()