Нашли или выдавили из себя код, который нельзя назвать нормальным,
на который без улыбки не взглянешь?
Не торопитесь его удалять или рефакторить, — запостите его на
говнокод.ру, посмеёмся вместе!
// https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
// methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
// ...
};
> The Microsoft Implementation of CRTP in Active Template Library (ATL) was independently discovered, also in 1995 by Jan Falkin who accidentally derived a base class from a derived class. Christian Beaumont, first saw Jan's code and initially thought it couldn't possibly compile in the Microsoft compiler available at the time. Following this revelation that it did indeed work, Christian based the entire ATL and Windows Template Library (WTL) design on this mistake.
А какая ошибка по-вашему положена в основу всего дизайна языка C++?
> The Microsoft Implementation of CRTP in Active Template Library (ATL) was independently discovered, also in 1995 by Jan Falkin who accidentally derived a base class from a derived class. Christian Beaumont, first saw Jan's code and initially thought it couldn't possibly compile in the Microsoft compiler available at the time.
Может суть языка C++ это использование (открытие) каких-то ебанутых багофич, по поводу которых даже возникают сомнение, соберется ли вообще это говно, или нет?
Шизофрения. Выдержка из интервью с Александром Степановым:
"It was not any particular function, but the number of them. Bjarne is personally responsible for reducing the number of components in STL by a factor of two. He was trying to make it as small as possible to placate the opposition. " http://www.stlport.org/resources/StepanovUSA.html
В с++17 добавили приснопамятные функции Бесселя.
Главная проблема дизайна с++ - это желание угодить всем, что вытекает в очень противоречивые и политически ангажированные решения по поводу добавления новых фич.
Забавно, что среди популярных языков вырисовываются три основных подхода к созданию стандартных библиотек:
1) JS-like: в стандартной библиотеке нет ровным счётом нихуя. Однако присутствует мощный и крайне простой пакетный менеджер с большим архивом библиотек, для использования которого достаточно просто написать «import».
2) Python-like: в стандартную либу напихано огромное количество модулей на любые случаи жизни; сторонние библиотеки нужны гораздо реже и только для сильно специфических (по сравнению с предыдущим пунктом) задач.
3) ГовноC++-like: в стандартной либе нет почти нихуя (а то, что есть — неюзабельное говно). При этом пакетного менеджера тоже нет и не предвидится, а установка сторонних библиотек — ёбанный ад.
если есть CMake то собрать может даже ребенок. Но я против егой ебалы типа С++. C++ модули решают проблему но только тем что прячут проблему от нас, но не решают ее. Я давно говорил что все языки должны компилить в независимый байткод и тогда уже юзаться любым компайлером
Вот кстати, я долго не мог научиться работать с указателями в сишке. Потом наконец вдруг понял, что сишка не различает указатель на чар и указатель на массив чаров. Не существует не только способа ограничить размер массива, передаваемого по указателю, но и даже отличить массив от скаляра. После «Паскаля» это было дико.
Не по этой ли причине некоторые боятся звёздочки в сишке?
Но структуры при этом есть. И массивы, завёрнутые в структуру, существуют (что раздражает сишников, привыкших к тому, что массивов в сишке нет). Однако, если не ошибаюсь, по традиции там тоже не производится никаких проверок выхода за границы.
Для кокококонсистентности надо было вместо структур тоже сделать указатели.
Такие структуры могут быть полезны, например, для чтения каких-нибудь форматов файлов, я такое много раз видел. Только я не понял, как такое может сочитаться с паддингом.
Паддинг важен для sizeof и для размещения переменной в памяти (чтобы выделить под неё место), а также для вычисления зазоров между полями.
Зазоров не будет, если size_t совпадает по размеру с паддингом (обычно достаточно кратности машинному слову) или если указаны директивы для упаковки структуры (#pragma, __attribute__ –— вот это всё).
Что же касается паддинга в самом конце, то обычно такие структуры применяют к уже выделенной памяти, поэтому на sizeof нам плевать.
Но вообще да, какое-то чувство незавершённости остаётся.
В «Windows» такие структуры используются для возвращения инфы о файлах, например, функцией NtQueryDirectoryFile: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/ns-ntifs-_file_directory_information. Особый цимес в том, что функция эта возвращает нечто вроде связного списка структур переменного размера (в котором роль указателя на следующий элемент играет размер текущей структуры в байтах, блядь), в результате чего работа с ней превращается в жуткое траханье мозгов.
P.S. Как принято определять размер FileName: она zero-terminated, как все строки в сишке, или вычитать из NextEntryOffset суммарный размер всех предыдущих полей?
>указатель на массив чаров
Скорее указатель на первый элемент:)
Смотри, вот у тебя на стеке лежит массив.
char petuh[42] -- реально 42 чара занимает.
Представь себе кусок памяти в 42 чара. Вот тут лежит первый, а если продвинуться на чар то будет второй итд.
Таким образом если мы пойдем в память где лежит первый элемент и продвинимся на 2 чара то окажемся в том месте где лежит второй элемент.
Точно так же работают указатели. Но тошько когда ты дефенируешь массив ты реально создаешь 42 чара, а указатель это просто указатель.
char *p_petuh -- не создаст массива
Массивы нельзя возвращать или передавать: при этом передается/возвращается адрес первого элемента. Как если бы ты передал указатель.
Но можно завернуть их структуру и там правда будут физиески массивы и станет можно их передавать (хотя наверное я не стал бы передавать массив размером в гигабайт -- лучше передать все таки указатель и рядышком размер)
>> char petuh[42] -- реально 42 чара занимает.
Верно. Есть даже такой прикол:
int foo() {
char petuh[42] = "Ololo"; // реально разместит в стеке 42 элемента.
char *kurochka = "Trololo"; // может разместить где угодно
// например, в секции .data, а сюда вставит адрес первого элемента
}
Так что в некоторых контекстах массивы действительно есть.
Но вот конструкции вроде [42]petuh, petuh+42 (и для «petuh», объявленного как указатель, и для «petuh», объявленного как массив) и отсутствие разделения указателя на массив и указателя на скаляр сбивают с толку новичков.
Конструкции *petuh и petuh[0] работают одинаково, причём всё в силе как для char *petuh, так и для char petuh[42].
причем в первом случае строка будет мумутабельна
а во втором может и не быть:)
новички путаются потому что массивы нельзя возвращять, и это конечно полный фейл языка, но его уже не исправить.
Очень жалко иногда джавистов которые пытаются писать на сях
void foo(char petuh[4] p) {
p[2]..
}
С другой стороны достаточно ОДИН раз это хорошо понять (для этого полезно физически посмотреть на память, вот прямо дебагером походить по памяти и посмотреть как она выглдет) и все станет на свои места. главное ходить по чарам а не по интам чтобы не разбираться еще и с байтордером.
Не заставляет писать const (чтобы не сломать старые проги?) но при этом писать в получившийся указатель давным-давно нельзя (т.е. эти старые проги потенциально могут пиздануться, но ты об этом не узнаешь до запуска). Нахуй так жить?
Да в защищённом тоже можно. Подкрути атрибуты секции и радуйся. Ну или в рантайме попроси ось поменять защиту на странчиках. Правда конпелятор мёржит одинаковые литералы (но не обязан). Будет весело.
Я верно понимаю что загрузчик ОСы по умолчанию секцию .data (ну или как она там называется в разных осах) грузит в страницы с ключом R/O, и (по крайней мере в x64) такие страницы хардварно защищены от записи?
А как поменять ключ секции? Линкером?
Как поменять страницу я понимаю, хотя это и не оч секурно
Конпелятору можно сказать: "высри вот эти функции и переменные вот в эту секцию".
Линкеру можно сказать: "навесь на эту секцию вот такие атрибуты".
Загрузчик мапает секции так, как ему сказал линкер.
У «gcc» ты можешь указать имя секции, в которую совать переменную. А вот секцию создаёт линкер. Вероятно, ему нужно скормить объектный файл, в котором эта секция уже создана ассемблером.
Тут не только в ОС дело, а ещё в кококомпиляторе. «Linux» –— это не только «gcc», а «Windows» —– это не только «MSVC».
В сишные и крестовые стандарты добавляют всякую всячину, но не стандартизировали прагмы.
В общем, нужно посмотреть две вещи:
1. Какие прагмы есть в разных компиляторах.
2. Какие секции поддерживает загрузчик ОС.
*****
Виндовому загрузчику PE-файлов, если не ошибаюсь, на количество и названия секций насрать. Он смотрит атрибуты каждой секции. Ты можешь, например, создать секцию PETUH с атрибутами R-X и секцию KUROCHKA с атрибутами RW.
*****
В «a.out» количество секций фиксировано: есть .text (r-x, инициализированная), .data (rw, инициализированная), .bss (rw, неинициализированная) с заранее известными всем атрибутами. То есть даже для кокококонстант секции нет.
Если важно защитить константы от исполнения, их кладут в секцию .data (насрав на отсутствие защиты от записи). Если важно защитить константы от записи, их кладут в секцию .text (насрав на отсутствие защиты от исполнения).
*****
В «ELF», как и в «PE», можно создать 100500 секций с произвольными атрибутами.
У «XCOFF» количество секций произвольно, однако, произвольные атрибуты выставлять нельзя, можно выбирать только стандартные шаблоны (секция типа «.text», секция типа «.data») и т. п.
У «PEF» («Mac OS Classic») такая же ерунда, однако, есть секция для констант, а у «XCOFF» я её не нашёл.
У «Mach-O» («Mac OS X») похоже, что тоже стандартный набор шаблонов, причём шаблоны привязаны к названию. Шаблонов стало больше, но свои атрибуты секции создавать нельзя. Нужно искать секцию с подходящим названием.
У «NE» (16-битные приложения «Windows» и «OS/2») можно создавать произвольное количество секций с произвольными атрибутами (разрешения на чтение, запись, выполнение выставляются отдельно, как и у «PE» и «ELF»).
Итого можно выделить три типа платформ:
1. Можно создать секцию с произвольными атрибутами: «ELF», «Windows», «OS/2».
2. Можно создать секцию, но из заранее заготовленных шаблонов: системы типа «Макоси».
3. Можно только выбирать из готовых секций: «a.out».
> [42]petuh
Вы про 42[petuh]?
Кстати, зачем эта питушня нужна? Не могу придумать ситуации, где бы она понадобилась. (Реальной ситуации, а не заседания клуба задротов или "креативных" вопросов на собеседованиях)
Там же, в отличие от malloc, статическая питуизация учитывает размер пера:
"оффсет петуха плюс 42 на размер элемента петуха"
или
"42 плюс оффсет питуха на размер элемента 42"
А это уже не симметричная питушня.
Я понял, как это объяснить. В данном случае «+» является перегруженным оператором (перегрузка в том смысле, как в «C++»). Перегружен он для ситуации, когда один аргумент является указателем, другой целым числом. Тогда именно целое число умножается на размер элемента (если указатель типизированный; для void * же такая операция не имеет смысла).
Для других комбинаций типов слагаемых аналогичной перегрузки нет (есть только сложение скаляров), поэтому к неоднозначности это не приводит.
Самое страшное место сишной арифметики указателей: одно из слагаемых нужно умножить на размер элемента массива. Компилятор должен угадать, какое из слагаемых является элементом, а какое –— индексом, чтобы кодогенератор выдал
>> хотя наверное я не стал бы передавать массив размером в гигабайт -- лучше передать все таки указатель и рядышком размер
Сравним, как сделали в «Турбо Паскале», «Delphi» и в более поздних проектах вроде «FPC».
1. Если в функцию передаёшь массив, то пушится не сам массив, а указатель на его копию в стеке («копирующий конструктор»; если передаёшь литерал, то он где-нибудь создаётся и тоже передаётся указатель). Но точно так же он поступает и с другими огромными данными вроде структур.
Если формальный параметр имеет атрибут const, то копирование не производится, передаётся только указатель на оригинал, но при этом статический анализатор проверяет, что функция не изменяет данные по этому указателю. Если формальный параметр имеет атрибут var, то ещё проще: ни копирования, ни проверки. Фактически это концепция ссылки.
Если тип параметра является статическим массивом, то размер передавать не нужно, он известен на этапе компиляции.
Есть более сложные сущности: открытые массивы (array of integer, например), динамические массивы и array of const. В этом случае после указателя на массив компилятор пушит ещё его размер.
2. Если же вызываемая функция должна вернуть массив или структуру, то он выделяет место в стеке под результат и самым последним аргументом передаёт указатель на это место. Т. е. результат живёт там же, где живут локальные переменные вызывающего кода (никакой возни с ручным выделением/освобождением).
Да, эти два приёма по сути являются хаками. Однако, структуры и массивы обрабатываются единообразно.
Как light user data в Lua (кажется, такой там термин). Нам не важно, что там внутри, это просто какой-то указатель на что-то. Полезно при создании обёрток вокруг сишных/C++'овых библиотек. Допустим, CreateButton() возвращает Button*, и все действия в оригинальном API происходят через функции вот так: SetButtonText(Button*, ...) Тогда нам не важно, что это за конкретно тип и что там внутри, можно просто описать функции, как возвращающие IntPtr и принимающие IntPtr.
Может суть языка C++ это использование (открытие) каких-то ебанутых багофич, по поводу которых даже возникают сомнение, соберется ли вообще это говно, или нет?
Например я в детстве открыл для себя дрочку, хотя природой она явно не была задумана.
Так же и с С++.
"It was not any particular function, but the number of them. Bjarne is personally responsible for reducing the number of components in STL by a factor of two. He was trying to make it as small as possible to placate the opposition. "
http://www.stlport.org/resources/StepanovUSA.html
В с++17 добавили приснопамятные функции Бесселя.
Главная проблема дизайна с++ - это желание угодить всем, что вытекает в очень противоречивые и политически ангажированные решения по поводу добавления новых фич.
https://www.npmjs.com/package/bessel
1) JS-like: в стандартной библиотеке нет ровным счётом нихуя. Однако присутствует мощный и крайне простой пакетный менеджер с большим архивом библиотек, для использования которого достаточно просто написать «import».
2) Python-like: в стандартную либу напихано огромное количество модулей на любые случаи жизни; сторонние библиотеки нужны гораздо реже и только для сильно специфических (по сравнению с предыдущим пунктом) задач.
3) ГовноC++-like: в стандартной либе нет почти нихуя (а то, что есть — неюзабельное говно). При этом пакетного менеджера тоже нет и не предвидится, а установка сторонних библиотек — ёбанный ад.
4) Вроде пакетный менеджер есть, но всё сломано и нихрена не ставится (Haskell-like).
https://www-users.cs.york.ac.uk/susan/joke/cpp.htm
s: https://habr.com/ru/company/jugru/blog/438866/
DLL/so чем не поздний линкер?
Запросто. *a является синонимом a[0]. Можно явно писать [0] вместо звёздочки.
И даже так:
а в другом к нолю прибавил s
от перестановки мест слагаемых...
Не по этой ли причине некоторые боятся звёздочки в сишке?
Играю роль, такую роль,
И в этой роли, безусловно, я массив.
Для кокококонсистентности надо было вместо структур тоже сделать указатели.
А теперь переставь поля местами.
Зазоров не будет, если size_t совпадает по размеру с паддингом (обычно достаточно кратности машинному слову) или если указаны директивы для упаковки структуры (#pragma, __attribute__ –— вот это всё).
Что же касается паддинга в самом конце, то обычно такие структуры применяют к уже выделенной памяти, поэтому на sizeof нам плевать.
Но вообще да, какое-то чувство незавершённости остаётся.
можешь дать сырец? хочу на таблицу посмотреть
P.P.S. Торможу, есть же FileNameLength.
Запиливаешь шаблонный итератор и хоть няшным с++11 for'ом по этой хуйне бегай.
Это к слову о том, почему я люблю кресты для low-level.
Просто при передаче и возврате они не отличимы от указателей.
Скорее указатель на первый элемент:)
Смотри, вот у тебя на стеке лежит массив.
char petuh[42] -- реально 42 чара занимает.
Представь себе кусок памяти в 42 чара. Вот тут лежит первый, а если продвинуться на чар то будет второй итд.
Таким образом если мы пойдем в память где лежит первый элемент и продвинимся на 2 чара то окажемся в том месте где лежит второй элемент.
Точно так же работают указатели. Но тошько когда ты дефенируешь массив ты реально создаешь 42 чара, а указатель это просто указатель.
char *p_petuh -- не создаст массива
Массивы нельзя возвращать или передавать: при этом передается/возвращается адрес первого элемента. Как если бы ты передал указатель.
Но можно завернуть их структуру и там правда будут физиески массивы и станет можно их передавать (хотя наверное я не стал бы передавать массив размером в гигабайт -- лучше передать все таки указатель и рядышком размер)
Верно. Есть даже такой прикол:
Так что в некоторых контекстах массивы действительно есть.
Но вот конструкции вроде [42]petuh, petuh+42 (и для «petuh», объявленного как указатель, и для «petuh», объявленного как массив) и отсутствие разделения указателя на массив и указателя на скаляр сбивают с толку новичков.
Конструкции *petuh и petuh[0] работают одинаково, причём всё в силе как для char *petuh, так и для char petuh[42].
а во втором может и не быть:)
новички путаются потому что массивы нельзя возвращять, и это конечно полный фейл языка, но его уже не исправить.
Очень жалко иногда джавистов которые пытаются писать на сях
С другой стороны достаточно ОДИН раз это хорошо понять (для этого полезно физически посмотреть на память, вот прямо дебагером походить по памяти и посмотреть как она выглдет) и все станет на свои места. главное ходить по чарам а не по интам чтобы не разбираться еще и с байтордером.
Но ведь это не строка, а просто сахарок для массива из чаров с нулём в конце. И это тоже важный момент для понимания сишки.
"petux" в данном случае следует читать как {'p', 'e', 't', 'u', 'h', 0 }
Выбрось свой древний конпелятор. Он протух.
З.Ы. Хм, странно, в сишном режиме даже ворнингов нету на эту херню...
это одын из тех случаев которыеможно в C но лучше не в С++
тут мы в дату ложим строку а в курочку ложит поинтера на ее первый чар
хотя разумеется по стандарту это указатель на константные данные
И потому я за "реальный режим"
зы: из мейнстрима у кого мутабельные строки кроме Руби?
А как поменять ключ секции? Линкером?
Как поменять страницу я понимаю, хотя это и не оч секурно
Линкеру можно сказать: "навесь на эту секцию вот такие атрибуты".
Загрузчик мапает секции так, как ему сказал линкер.
#pragma section("petoox",read,write)
а у прыщей
__attribute__ ((section ("petoox")));
а как им RW сказать? или просто выбрать секнцию которая и так RW, ну типа data?
Блин, посмотри в доке от соответствующего копулятора. Я флешку паяю, некогда гуглить.
Можешь готовую секцию взять (и подкрутить ей атрибуты при желании). Можешь в новую высрать.
> у прыщей
Ну и терминология!
У «gcc» ты можешь указать имя секции, в которую совать переменную. А вот секцию создаёт линкер. Вероятно, ему нужно скормить объектный файл, в котором эта секция уже создана ассемблером.
Сори.
В общем я понял. У прыщеблядей я могу только сунуть в существуюзую RW сексию: .data например
У спермоблядей я могу создать новую секцию прямо из си.
Интересно, как с этим у шлангососов/бздунов?
В сишные и крестовые стандарты добавляют всякую всячину, но не стандартизировали прагмы.
В общем, нужно посмотреть две вещи:
1. Какие прагмы есть в разных компиляторах.
2. Какие секции поддерживает загрузчик ОС.
*****
Виндовому загрузчику PE-файлов, если не ошибаюсь, на количество и названия секций насрать. Он смотрит атрибуты каждой секции. Ты можешь, например, создать секцию PETUH с атрибутами R-X и секцию KUROCHKA с атрибутами RW.
*****
В «a.out» количество секций фиксировано: есть .text (r-x, инициализированная), .data (rw, инициализированная), .bss (rw, неинициализированная) с заранее известными всем атрибутами. То есть даже для кокококонстант секции нет.
Если важно защитить константы от исполнения, их кладут в секцию .data (насрав на отсутствие защиты от записи). Если важно защитить константы от записи, их кладут в секцию .text (насрав на отсутствие защиты от исполнения).
*****
В «ELF», как и в «PE», можно создать 100500 секций с произвольными атрибутами.
У «XCOFF» количество секций произвольно, однако, произвольные атрибуты выставлять нельзя, можно выбирать только стандартные шаблоны (секция типа «.text», секция типа «.data») и т. п.
У «PEF» («Mac OS Classic») такая же ерунда, однако, есть секция для констант, а у «XCOFF» я её не нашёл.
У «Mach-O» («Mac OS X») похоже, что тоже стандартный набор шаблонов, причём шаблоны привязаны к названию. Шаблонов стало больше, но свои атрибуты секции создавать нельзя. Нужно искать секцию с подходящим названием.
У «NE» (16-битные приложения «Windows» и «OS/2») можно создавать произвольное количество секций с произвольными атрибутами (разрешения на чтение, запись, выполнение выставляются отдельно, как и у «PE» и «ELF»).
1. Можно создать секцию с произвольными атрибутами: «ELF», «Windows», «OS/2».
2. Можно создать секцию, но из заранее заготовленных шаблонов: системы типа «Макоси».
3. Можно только выбирать из готовых секций: «a.out».
В x64 они дополнительно защищены от исполнения. В x32 без DEP они защищены только от записи.
З.Ы. Хотя на тех же stm'ках можно снять блокировку с флеша и менять единички на нолики...
Вы про 42[petuh]?
Кстати, зачем эта питушня нужна? Не могу придумать ситуации, где бы она понадобилась. (Реальной ситуации, а не заседания клуба задротов или "креативных" вопросов на собеседованиях)
"оффсет петуха поюс 42"
или
"42 плюс оффсет питуха"
и все
не то чтобы ее как-то специально делали
почему оно тогда во всех компиляторах работает?
"оффсет петуха плюс 42 на размер элемента петуха"
или
"42 плюс оффсет питуха на размер элемента 42"
А это уже не симметричная питушня.
орифметика укойзателей в сишечке так и работает
"указатель_на_питуха + 32" следует читать как "указатель_на_питуха + 32 питуха"
Для других комбинаций типов слагаемых аналогичной перегрузки нет (есть только сложение скаляров), поэтому к неоднозначности это не приводит.
Ты хоть понимаешь, что тебе лечиться нужно?
Сравним, как сделали в «Турбо Паскале», «Delphi» и в более поздних проектах вроде «FPC».
1. Если в функцию передаёшь массив, то пушится не сам массив, а указатель на его копию в стеке («копирующий конструктор»; если передаёшь литерал, то он где-нибудь создаётся и тоже передаётся указатель). Но точно так же он поступает и с другими огромными данными вроде структур.
Если формальный параметр имеет атрибут const, то копирование не производится, передаётся только указатель на оригинал, но при этом статический анализатор проверяет, что функция не изменяет данные по этому указателю. Если формальный параметр имеет атрибут var, то ещё проще: ни копирования, ни проверки. Фактически это концепция ссылки.
Если тип параметра является статическим массивом, то размер передавать не нужно, он известен на этапе компиляции.
Есть более сложные сущности: открытые массивы (array of integer, например), динамические массивы и array of const. В этом случае после указателя на массив компилятор пушит ещё его размер.
2. Если же вызываемая функция должна вернуть массив или структуру, то он выделяет место в стеке под результат и самым последним аргументом передаёт указатель на это место. Т. е. результат живёт там же, где живут локальные переменные вызывающего кода (никакой возни с ручным выделением/освобождением).
Да, эти два приёма по сути являются хаками. Однако, структуры и массивы обрабатываются единообразно.
(троллфейс) он же reference
ps: opaque это такой, который сам C# не понимает, и только в какой-нить P/Invoke можно?
как userdata в lua?