ЕМ>Но платить-то придется за весь банкет, поскольку любой throw или try/catch непременно потянет за собой все зависимости, предназначенные для обработки исключений.
А что это за зависимости, можно узнать? По-моему, там всё довольно минималистично
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Это относится к его применению. В тех же *nix, OS/2 и Windows весь API заточен под C и его ABI, поэтому любая неигрушечная реализация должна этому следовать.
Разве C? А не Паскаль? Я ёще помню те времена, когда в объявлениях практически всех виндовых функций использовался макрос PASCAL
Смотрел несколько дней назад, уже всего точно не помню, но вот некоторые моменты:
Посмотрел это видео, как много пустых слов. И товарищ основательно преувеличивает. Вариации на тему entity component system в геймдеве были лет на 15 раньше, чем он указывает. Я портировал на TypeScript игру 84го года, и там уже было подобие это entity component system
Ну и api операционных систем того времени это вобщем вариации на ту же тему.
Re[16]: The Big OOPs: Anatomy of a Thirty-five-year Mistake
Здравствуйте, Marty, Вы писали:
M>мне всегда проще было сделать что-то рекурсивно, чем итерационно.
Если алгоритм изначально рекурсивный — неудивительно.
M>Ты про каких-то неосиляторов
Я почему-то очень сильно сомневаюсь, что Вы хотя бы более-менее понимаете, как работают "магические" шаблоны в той же Loki. Не в смысле общих принципов, а в смысле внесения туда осмысленных правок с сохранением работоспособности.
M>что тебе сешает сделать свой компилятор своего "C++"?
То, что это весьма затратный процесс, особенно при том, что мне нужна хорошая оптимизация кода на выходе, а времени на занятия, не приносящие денег, остается все меньше и меньше.
M>Если выкинуть шаблоны
Я не смогу их выкинуть, ибо использую. Не на уровне магии, но все же.
M>такой коспилятор пишется за месяц, ну, окей, за три.
За три месяца он пишется до состояния "устойчиво работает, дает работоспособный код". Мне такого будет мало. Если б взялся лет 20-30 назад, то где-нибудь за год довел бы до ума. Но меня каждый раз останавливала мысль "зачем мне это делать, разработчики ж не дураки — скоро обязательно кто-нибудь добавит в существующие компиляторы удобные фичи". И ведь были к тому предпосылки — те же __if_exists/__if_not_exists, __is_xxx/__has_xxx в VC++. Но потом на все это забили — видать, "традиционный подход" возобладал и там.
M>Примеры помогут, по-моему, мало кто понимает, как твои макросы должны работать
Тому, кто намертво залип на принципах вроде "любой макрос обязан полностью раскрываться на стадии лексического анализа", примеры не помогут.
Здравствуйте, Sinclair, Вы писали:
S>>в более продвинутых случаях, на уровне AST не подвергшегося еще семантическому анализу (например, процедурные макросы Rust-а, которые получают и возвращают TokenStream-ы). S>Макросы раста работают не с синтаксическим материалом, а лексическим. То есть ещё до построения AST.
Могу предположить, что в случае Rust-а все не так просто.
Ведь для того, чтобы применить макрос из категории derive компилятору Rust-а нужно сперва завершить парсинг того участка (блока) к которому макрос применяется. А для этого недостаточно только разбиения входного потока на лексемы (токены), нужен еще и синтаксический анализ.
Для эксперимента возьмем простой процедурный макрос из категории derive:
use proc_macro2::{self, Ident, Span};
#[proc_macro_derive(MyTrait)]
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let demo = Ident::new("Demo", Span::call_site());
let first = input.into_iter().skip(3).next().unwrap();
match first {
proc_macro2::TokenTree::Ident(id) if demo == id => panic!("Only Demo is expected"),
ref _other => {}
};
format!("fn answer() -> String {{ \"{first}\".to_string() }}").parse().unwrap()
}
Он порождает ошибку компиляции, если будет применяться к структуре с именем Demo.
И простейший пример его использования:
use mytrait_derive::MyTrait;
#[allow(dead_code)]
#[derive(MyTrait)]
struct Demo {
x: i32, y: i32
}
fn main() {
let a = answer();
println!("The answer is {a}");
}
При компиляции получаем ожидаемое:
Compiling demo v0.1.0 (experiment-1\demo)
error: proc-macro derive panicked
--> src\main.rs:4:10
|
4 | #[derive(MyTrait)]
| ^^^^^^^
|
= help: message: Only Demo is expected
error[E0425]: cannot find function `answer` in this scope
--> src\main.rs:10:13
|
10 | let a = answer();
| ^^^^^^ not found in this scope
For more information about this error, try `rustc --explain E0425`.
error: could not compile `demo` (bin "demo") due to 2 previous errors
Теперь намеренно внесем в определение Demo синтаксическую ошибку:
struct Demo {
x: i32, y: /*i32*/
}
и при попытке компиляции у нас сперва получается сообщение об ошибке в декларации Demo, а уже затем сообщение об ошибке от макроса:
Compiling demo v0.1.0 (experiment-1\demo)
error: expected type, found `}`
--> src\main.rs:7:1
|
5 | struct Demo {
| ---- while parsing this struct
6 | x: i32, y: /*i32*/
7 | }
| ^ expected type
error: proc-macro derive panicked
--> src\main.rs:4:10
|
4 | #[derive(MyTrait)]
| ^^^^^^^
|
= help: message: Only Demo is expected
error[E0425]: cannot find function `answer` in this scope
--> src\main.rs:10:13
|
10 | let a = answer();
| ^^^^^^ not found in this scope
For more information about this error, try `rustc --explain E0425`.
error: could not compile `demo` (bin "demo") due to 3 previous errors
Т.е. выглядит все так, что сперва компилятор делает разбор (в том числе и синтаксический) фрагмента, для которого написан derive, только затем вызывает derived-макрос куда отдает набор лексем. Но при этом, полагаю, у самого компилятора вполне в распоряжении уже есть AST.
Re[17]: The Big OOPs: Anatomy of a Thirty-five-year Mistake
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Тому, кто намертво залип на принципах вроде "любой макрос обязан полностью раскрываться на стадии лексического анализа", примеры не помогут.
Вас уже тыкали фейсом в макросы Rust-а. Они как раз работают уже после лексического анализа.
Но фокус в том, что макросы манипулируют кусками исходного текста (как на входе, так и на выходе) и полученные в результате работы макроса куски текста затем должны обрабатываться компилятором.
В случае же шаблонов C++ или D, у нас уже не куски исходного текста -- по крайней мере на входе уже известные компилятору сущности языка, вроде типов или объектов.
Хотя в D все еще веселее: там на входе известные компилятору сущности, на выходе может быть текст, который скармливается компилятору через специальный вариант mixin. Хотя есть и особый mixin template.
Так что есть ощущение, что ваши собеседники и видели, и пробовали разное. А вот вы только какие-то свои пустопорожние грезы описываете. Поэтому и в примеры не можете.
Здравствуйте, Marty, Вы писали:
M>А что это за зависимости
Оно ж в любом случае тащит часть поддержки RTTI, чтоб catch мог понять, значение какого типа выброшено в throw.
M>По-моему, там всё довольно минималистично
Минималистичным оно могло бы быть, если б throw мог выбрасывать только значения интегральных типов (оптимально — указатель). Ну, или если б можно было сказать компилятору "мы будем выбрасывать только интегральные типы, и сами разберемся, что было выброшено по факту".
А так добавление SEH в минимальный main для VC++ дает прирост где-то в 3%, а добавление туда же исключений C++ (которые реализованы поверх SEH) — дополнительные 25%. По большому счету, это не так много, но это явное нарушение того самого принципа о плате и использовании.
Здравствуйте, Marty, Вы писали:
M>Разве C? А не Паскаль?
В виндовых SDK/DDK/WDK никогда не было объявлений интерфейсов для Pascal. Только для C/C++, MIDL и других стандартных средств от MS.
M>в объявлениях практически всех виндовых функций использовался макрос PASCAL
Макрос PASCAL раскрывался в сишный модификатор _pascal.
Это пошло еще с первых реализаций C/Pascal: в первом возможны функции с переменным количеством параметров, поэтому стек очищает вызывающий код, а во втором их нет, поэтому стек очищает вызываемая функция. Соответственно, модификаторы вроде _pascal были добавлены в C для подключения процедур/функций на Pascal. По объему кода второй вариант более компактен, поэтому в ABI последующих систем обоснованно решили использовать его, а модификатор был уже готовый.
Здравствуйте, so5team, Вы писали:
S>для того, чтобы применить макрос из категории derive компилятору Rust-а нужно сперва завершить парсинг того участка (блока) к которому макрос применяется. А для этого недостаточно только разбиения входного потока на лексемы (токены), нужен еще и синтаксический анализ.
Все это не обязательно жестко постулировать раз и навсегда для всего языка. Можно выбрать режим разбора/анализа по умолчанию, и менять его отдельно для каждого макроса или фрагмента кода.
Здравствуйте, Евгений Музыченко, Вы писали:
S>>для того, чтобы применить макрос из категории derive компилятору Rust-а нужно сперва завершить парсинг того участка (блока) к которому макрос применяется. А для этого недостаточно только разбиения входного потока на лексемы (токены), нужен еще и синтаксический анализ.
ЕМ>Все это не обязательно жестко постулировать раз и навсегда для всего языка. Можно выбрать режим разбора/анализа по умолчанию, и менять его отдельно для каждого макроса или фрагмента кода.
А еще в праздных разговорах можно наделить понятие "макроса" еще 100500 дополнительными смыслами.
Re[18]: The Big OOPs: Anatomy of a Thirty-five-year Mistake
Здравствуйте, so5team, Вы писали:
S>Вас уже тыкали фейсом в макросы Rust-а. Они как раз работают уже после лексического анализа.
Это я про то, как Вы несколько дней подряд бились головой о стену: "они работают ДО компилятора! ДО! ДО! ДО!".
S>Но фокус в том, что макросы манипулируют кусками исходного текста (как на входе, так и на выходе) и полученные в результате работы макроса куски текста затем должны обрабатываться компилятором. S>В случае же шаблонов C++ или D, у нас уже не куски исходного текста -- по крайней мере на входе уже известные компилятору сущности языка, вроде типов или объектов.
Вместо того, чтоб перебирать частные реализации, попробуйте взглянуть на это более обобщенно: в точке вызова "метаконструкции" — хоть макроса (если макропроцессор работает на стадии компиляции), хоть шаблона — компилятор переходит к выполнению некой "подпрограммы", правила выполнения которой определяются как языком, так и содержимым этой самой метаконструкции. Если это банальный "лексический" макрос в стиле C, то все сводится к чисто текстовым подстановкам. Если это шаблон C++, то он раскрывается по правилам, заранее (и достаточно скупо) определенным в языке для шаблонов.
А если позволить программисту записывать эти "метаконструкции" на языке, похожем на основной (как это и сделано в том же PL/1), то их функциональность будет практически не ограничена, и при этом не так уж сложно обеспечить понятность, прозрачность, поддержку отладки как на стадии компиляции, так и на стадии исполнения.
На стадии определения такого "макроса" можно было бы указать, как оно должно обрабатываться — минимально (как это делается для лексического), так и расширенно (как это делается в C++ для шаблонов), это позволит избежать откладывания диагностики ошибок.
S>в примеры не можете.
Я в примеры могу, но не хочу. Вы вот могли бы задавать уточняющие вопросы, но тоже не хотите, все ждете готовых примеров...
Здравствуйте, so5team, Вы писали:
S>можно наделить понятие "макроса" еще 100500 дополнительными смыслами.
Нет надобности его чем-то "наделять". Оно изначально предельно общее, буквально вытекающее из смысла слова "macro" (надеюсь, найдете самостоятельно). И оно испокон веку обозначало все, что подпадает под технологию "сгенерировать результат из указанной текстовой конструкции". Ну а потом выросли поколения, которые уже знали макросы исключительно по их реализациив в C/C++, m4 и подобных средствах, а про иные способы реализации даже не догадывались, и стали придумывать разные искусственные разделения.
Настаивая на том, чтоб называть обработку текста программ, выходящую за пределы чисто лексической, непременно "метапрограммированием", а термина "макрос" в данном контексте избегать, попробуйте представить, как будет выглядеть реализация языка с подобным разделением. Вот надо Вам сделать почти чисто текстовую подстановку, но так, чтоб текст содержал, скажем, имя текущей функции. Сейчас для этого есть встроенные макросы вроде __FUNCTION__, но это уже читерство — препроцессор (которым, по-Вашему, обязан быть любой макропроцессор) ни о каких функциях не знает. А в "метаконструкциях для рефлексии" Вам предложат богатый инструментарий для манипуляции типами, объектами, перебора методов классов и т.п., но банальной склейки двух подстрок не дадут, ибо это уже "типичная макрооперация", пожалте в препроцессор. И будете опять изворачиваться в попытках скрестить ежа с ужом, как тот комсомолец, который не может без трудностей.
Здравствуйте, Евгений Музыченко, Вы писали:
S>>Вас уже тыкали фейсом в макросы Rust-а. Они как раз работают уже после лексического анализа.
ЕМ>Это я про то, как Вы несколько дней подряд бились головой о стену: "они работают ДО компилятора! ДО! ДО! ДО!".
Я и продолжу на этом настаивать.
Компилятор занимается лексическим и синтаксическим разбором, после чего анализирует семантику, после чего делает оптимизации и генерирует объектный код.
Для метапрограммирования необходимо завершение стадии анализа семантики. Ведь именно тогда у компилятора появляется точная информация что есть что -- тот самый код который пойдет на вход метапрограмме в качестве параметра.
А пока семантики нет, пока все крутится на уровне лексики/синтаксиса (а то и до того, как в случае Си-ного препроцессора), то речь идет о макросах.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>И оно испокон веку обозначало все, что подпадает под технологию "сгенерировать результат из указанной текстовой конструкции"
Именно что из текстовой конструкции, а не из кода.
В текстовой конструкции мы может даже не знать что означает символ `>`.
А в коде мы точно знаем что это.
ЕМ>Настаивая на том, чтоб называть обработку текста программ, выходящую за пределы чисто лексической, непременно "метапрограммированием", а термина "макрос" в данном контексте избегать, попробуйте представить, как будет выглядеть реализация языка с подобным разделением. Вот надо Вам сделать почти чисто текстовую подстановку, но так, чтоб текст содержал, скажем, имя текущей функции. Сейчас для этого есть встроенные макросы вроде __FUNCTION__, но это уже читерство — препроцессор
Здравствуйте, Евгений Музыченко, Вы писали:
S>>Вас уже тыкали фейсом в макросы Rust-а. Они как раз работают уже после лексического анализа.
ЕМ>Это я про то, как Вы несколько дней подряд бились головой о стену: "они работают ДО компилятора! ДО! ДО! ДО!".
А ты с этим не согласен?
--
Справедливость выше закона. А человечность выше справедливости.
Re[20]: The Big OOPs: Anatomy of a Thirty-five-year Mistake
Здравствуйте, rg45, Вы писали:
S>>>Вас уже тыкали фейсом в макросы Rust-а. Они как раз работают уже после лексического анализа.
ЕМ>>Это я про то, как Вы несколько дней подряд бились головой о стену: "они работают ДО компилятора! ДО! ДО! ДО!".
R>А ты с этим не согласен?
Он про абстрактные макросы в вакууме своей головы.
Эти неведомые и никем не виданные звери могут все что угодно и работают там и тогда, когда г.Музыченко будет удобно.
Re[21]: The Big OOPs: Anatomy of a Thirty-five-year Mistake
Здравствуйте, so5team, Вы писали:
S>Он про абстрактные макросы в вакууме своей головы. S>Эти неведомые и никем не виданные звери могут все что угодно и работают там и тогда, когда г.Музыченко будет удобно.
Я всё меньше вижу смысла дискутировать с ним о чём-либо. У него там в голове такой зоопарк, что поди отдели бульдогов от носорогов.
--
Справедливость выше закона. А человечность выше справедливости.
Re[20]: The Big OOPs: Anatomy of a Thirty-five-year Mistake
Здравствуйте, so5team, Вы писали:
S>Компилятор занимается лексическим и синтаксическим разбором, после чего анализирует семантику, после чего делает оптимизации и генерирует объектный код. S>Для метапрограммирования необходимо завершение стадии анализа семантики. Ведь именно тогда у компилятора появляется точная информация что есть что -- тот самый код который пойдет на вход метапрограмме в качестве параметра.
Ну, если стоит задача сделать формальную игру наподобие шахмат, где конь ходит только буквой "Г", и никак иначе, то Вы на верном пути. Если же хочется сделать одновременно несложный, удобный и универсальный инструмент, то придется отступить от строгой идеологии в пользу разумности.