А теперь подумаем о том, как добавить определение константы. У нас есть имя, тип, и необходимые атрибуты, а также выражение для инициализации. Единственный недостающий ингредиент — это
Приступим же непосредственно к реализации. Для начала разберём случай с отсутствующим выражением для константы, поскольку он, кажется, выглядит несколько проще:
...Хотя нет, не всё. Ничто не мешает нам при помощи аналогичного макроса объявлять статические переменные, в том числе и мутабельные. К сожалению, я не нашёл способа избежать дупликации кода, не наталкиваясь на ограничения макросов, поэтому я просто тупо накопипастил ветки и поменял в нужных местах
Проверим теперь, как оно работает:
iota
(ну или как там это выражение назвал пользователь). Вместо того, чтобы вручную сканировать выражению и заменять iota
руками, мы просто предоставим его определение и сделаем его видимым в выражении. Иными словами, мы заведём отдельную константу с именем $iota
. При этом надо иметь в виду две вещи. Во-первых, имя, данное пользователем, почти наверняка не написано в SCREAMING_SNAKE_CASE
, которым обычно пишут имена констант. Во-вторых, это имя вполне может быть не использовано внутри выражения для константы. Для того, чтобы не вызывать предупреждений компилятора, мы заглушим их при помощи #[allow(dead_code, non_upper_case_globals)]
.Приступим же непосредственно к реализации. Для начала разберём случай с отсутствующим выражением для константы, поскольку он, кажется, выглядит несколько проще:
(Случай с наличием выражения для константы отличается не сильно:
$iota:ident($iota_value:expr, $prev_expr:expr):
$(#[$attr:meta])*
$vis:vis
const $name:ident : $ty:ty; //нет выражения
$($tt:tt)*
) => {
$(#[$attr])*
$vis
const $name: $ty = {
#[allow(dead_code, non_upper_case_globals)]
const $iota: $ty = $iota_value;
$prev_expr //переиспользуем предыдущее выражение
};
iota_consts!(
// vv--- инкрементируем передаваемое значение
$iota($iota_value + 1, $prev_expr):
// ^^- значение передаём без изменений
$($tt)*
);
};
(Ещё одна фича, которую надо поддержать — это пропуск константы вообще. На самом деле она даже проще предыдущих:
$iota:ident($iota_value:expr, $_prev_expr:expr):
$(#[$attr:meta])*
$vis:vis
const $name:ident : $ty:ty = $const_value:expr;
$($tt:tt)*
) => {
$(#[$attr])*
$vis
const $name: $ty = {
#[allow(dead_code, non_upper_case_globals)]
const $iota: $ty = $iota_value;
$const_value //используем текущее определение
};
iota_consts!(
// vv- и передаём его дальше
$iota($iota_value + 1, $const_value):
$($tt)*
);
};
(Ну и, как у любой рекурсии, у макроса должно быть условие завершения. В нашем случае им является пустой вход для обработки:
$iota:ident($iota_value:expr, $prev_expr:expr):
_; //ничего, совсем ничего! Намеренно игнорируем
$($tt:tt)*
) => {
iota_consts!(
// vv- но обязательно инкрементируем
$iota($iota_value + 1, $prev_expr):
$($tt)*
);
};
(В принципе, на этом всё. Ввиду наличия вывода типа выражение для
$_iota:ident($_iota_value:expr, $_prev_expr:expr):
) => {};
$iota
само корректно типизируется, поэтому $iota
получается эффективно безтиповым значением, как мы и хотели....Хотя нет, не всё. Ничто не мешает нам при помощи аналогичного макроса объявлять статические переменные, в том числе и мутабельные. К сожалению, я не нашёл способа избежать дупликации кода, не наталкиваясь на ограничения макросов, поэтому я просто тупо накопипастил ветки и поменял в нужных местах
const
на static
и static mut
соответственно.Проверим теперь, как оно работает:
iota_consts!(iota:Эта программа выводит:
const FOO: u32 = 1 << iota; // iota = 0, FOO = 1
static BAR: i32; // iota = 1, BAR = 2
_; // iota = 2
#[allow(dead_code)]
static mut BAZ: i8 = iota + 2; // iota = 3, BAZ = 5
const QUX: i16; // iota = 4, QUX = 6
);
macro_rules! print_consts {
($($name:ident),*) => {
$(println!("{} = {}", stringify!($name), $name);)*
};
}
fn main() {
print_consts! {
FOO,
BAR,
/* BAZ */
QUX
};
}
FOO = 1, как и ожидалось. Более того, ввиду аннотации
BAR = 2
QUX = 6
#[allow(dead_code)]
на BAZ
компилятор не жалуется на неиспользованное определение. Также, очевидно, макрос позволяет определять константы разных типов.Есть недостатки у нашего решения? Безусловно. Вот они в произвольном порядке:
* Как я уже говорил, идентификатор для
*
* В Go можно объявить больше одной константы в одной строке, при это значение
* В Go
* Ошибки в вычислениях показываются не там, где надо, и это совсем нехорошо. Поясню на примере:
Как всегда, весь код в гисте.
* Как я уже говорил, идентификатор для
iota
приходится давать самому. Неприятно, но не смертельно.*
const
повторяется для каждого определения. Если убрать возможность генерировать static (mut)?
, то можно облегчить синтаксис, убрав эти ключевые слова.* В Go можно объявить больше одной константы в одной строке, при это значение
iota
в выражениях для них будет одинаково. Мой макрос такого не умеет. Пока что неясно ни то, как это мешается на практике, ни то, как это поддержать.* В Go
iota
можно также использовать в выражениях с дробными числами. Вот это уже более существенный недостаток, но его, в принципе, можно обойти кастами по месту.* Ошибки в вычислениях показываются не там, где надо, и это совсем нехорошо. Поясню на примере:
iota_consts!(iota:Этот код вызывает следующую ошибку компиляции:
const A: u8 = iota + u8::MAX;
const B: u8;
);
error: any use of this value will cause an errorКак видите, не смотря на то, что ошибка произошла при вычислении
--> src/lib.rs:189:19
|
24 | / $vis
25 | | const $name: $ty = {
26 | | #[allow(dead_code, non_upper_case_globals)]
27 | | const $iota: $ty = $iota_value;
28 | | $prev_expr
29 | | };
| |__________-
...
189 | const A: u8 = iota + u8::MAX;
| ^^^^^^^^^^^^^^ attempt to compute `1_u8 + u8::MAX`, which would overflow
|
= note: `#[deny(const_err)]` on by default
B
, компилятор указывает на выражение для`A`. В принципе, понятно, откуда это берётся: токены парсятся с исходными спанами, которые не меняются при преобразованиях макроса. К сожалению, это фундаментальное ограничение декларативных макросов, и я не вижу способа решить эту проблему.Как всегда, весь код в гисте.
Блог*
#prog #rust #моё В Go есть такая фича, как iota. Это магический идентификатор, который можно использовать в блоке констант и который автоматически заменяется на порядковый номер в блоке констант. Наглядный пример: package main import ( "fmt" ) const…
Вот я наврал, а Вафель меня поправил, на константы гигиена не распространяется: https://t.iss.one/ihatereality/1878
Короче, он опять умница, няша и кошкодевочка
Короче, он опять умница, няша и кошкодевочка
Telegram
Мне не нравится реальность
Антон как обычно написал интересный пост, на этот раз про симуляцию iota из Go в Rust при помощи макросов (прочитайте его прежде, чем этот пост).
Однако в посте есть небольшая ошибка:
Хочу отметить, что добиться в полной мере той же эргономики, что и на…
Однако в посте есть небольшая ошибка:
Хочу отметить, что добиться в полной мере той же эргономики, что и на…
Forwarded from XYZ
Townscaper — это градостроительная песочница, в которой игра сама выбирает блоки и автоматически встраивает их в окружение. Проект создан на Unity.
Геймдизайнер Оскар Стальберг рассказал, какие алгоритмы он использовал, чтобы добиться столь продуманной процедурной генерации.
Читать подробнее
Геймдизайнер Оскар Стальберг рассказал, какие алгоритмы он использовал, чтобы добиться столь продуманной процедурной генерации.
Читать подробнее
XYZ
Townscaper — это градостроительная песочница, в которой игра сама выбирает блоки и автоматически встраивает их в окружение. Проект создан на Unity. Геймдизайнер Оскар Стальберг рассказал, какие алгоритмы он использовал, чтобы добиться столь продуманной процедурной…
#prog #article
В игре активно используется алгоритм wave function collapse, который неплохо описан, например, в этой (перевод) статье и который, не смотря на объяснения, для меня до сих пор выглядит, как чудо.
В игре активно используется алгоритм wave function collapse, который неплохо описан, например, в этой (перевод) статье и который, не смотря на объяснения, для меня до сих пор выглядит, как чудо.
BorisTheBrave.Com
Wave Function Collapse Explained
A simple guide to constraint solving Since developing DeBroglie and Tessera, I’ve had a lot of requests to explain what it is, how it works. The generation can often seem quite magical, but a…
Блог*
#prog #article В игре активно используется алгоритм wave function collapse, который неплохо описан, например, в этой (перевод) статье и который, не смотря на объяснения, для меня до сих пор выглядит, как чудо.
YouTube
Procedural generation from a single example with WaveFunctionCollapse
Procedural generation from a single example with the wave function collapse algorithm. GitHub repo: https://github.com/mxgmn/WaveFunctionCollapse
Forwarded from Коза кричала
Разгадана одна из главных тайн мироздания: Почему вомбат срет кубиками?
Эта загадка будоражила умы ученых давно, но ответ был получен лишь теперь. Попутно доказали несостоятельность всех прежних гипотез, имевших в академических кругах широкое хождение. Одни говорили, что у вомбатов квадратный анус. Другие - что какашки приобретают необычную форму при прохождении костей таза. Третьи - что вомбаты лепят из какашек геометрически строгие куличики уже, так сказать, пост-фактум - посрамши.
Так вот это не так. У вомбатов любопытный кишечник. Одни участки мышц в нем твердые, другие эластичные. И именно поэтому какашки получаются почти квадратными. Но возникает вопрос - а зачем им быть такими? Какой смысл заложила в это Природа? Дело в том, что вомбаты таким образом помечают территорию и общаются с сородичами. Какашка, выпав из жопы, должна остаться на месте, чтобы донести свой "месседж". А будь она круглой, то просто укатится хрен знает куда. Поэтому куб - идеальная форма.
Знание - сила!
Эта загадка будоражила умы ученых давно, но ответ был получен лишь теперь. Попутно доказали несостоятельность всех прежних гипотез, имевших в академических кругах широкое хождение. Одни говорили, что у вомбатов квадратный анус. Другие - что какашки приобретают необычную форму при прохождении костей таза. Третьи - что вомбаты лепят из какашек геометрически строгие куличики уже, так сказать, пост-фактум - посрамши.
Так вот это не так. У вомбатов любопытный кишечник. Одни участки мышц в нем твердые, другие эластичные. И именно поэтому какашки получаются почти квадратными. Но возникает вопрос - а зачем им быть такими? Какой смысл заложила в это Природа? Дело в том, что вомбаты таким образом помечают территорию и общаются с сородичами. Какашка, выпав из жопы, должна остаться на месте, чтобы донести свой "месседж". А будь она круглой, то просто укатится хрен знает куда. Поэтому куб - идеальная форма.
Знание - сила!
#prog #article
Статья (перевод) о том, как сбить с толку шахматный движок и заставить его крашнуться или предложить заведомо неверные ходы.
Статья (перевод) о том, как сбить с толку шахматный движок и заставить его крашнуться или предложить заведомо неверные ходы.
chess.resistant.tech
Win by Segfault and other notes on Exploiting Chess Engines
We document a number of paths that can be used to exploit the Stockfish chess engine, causing crashes when attempting to evaluate the next best move, or even outright tricking the engine into believing it has no valid moves (while preserving the illusion…
Forwarded from мне не нравится реальность (вафель 🧇🍓)
This media is not supported in your browser
VIEW IN TELEGRAM
Forwarded from мне не нравится реальность (вафель 🧇🍓)
вафель держит Илью 2021 год, фото в цвете