1.83K subscribers
3.3K photos
132 videos
15 files
3.58K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
А теперь подумаем о том, как добавить определение константы. У нас есть имя, тип, и необходимые атрибуты, а также выражение для инициализации. Единственный недостающий ингредиент — это 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 компилятор не жалуется на неиспользованное определение. Также, очевидно, макрос позволяет определять константы разных типов.
Есть недостатки у нашего решения? Безусловно. Вот они в произвольном порядке:

* Как я уже говорил, идентификатор для 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`. В принципе, понятно, откуда это берётся: токены парсятся с исходными спанами, которые не меняются при преобразованиях макроса. К сожалению, это фундаментальное ограничение декларативных макросов, и я не вижу способа решить эту проблему.

Как всегда, весь код в гисте.
Forwarded from XYZ
Townscaper — это градостроительная песочница, в которой игра сама выбирает блоки и автоматически встраивает их в окружение. Проект создан на Unity.

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

Читать подробнее
Forwarded from Коза кричала
Разгадана одна из главных тайн мироздания: Почему вомбат срет кубиками?

Эта загадка будоражила умы ученых давно, но ответ был получен лишь теперь. Попутно доказали несостоятельность всех прежних гипотез, имевших в академических кругах широкое хождение. Одни говорили, что у вомбатов квадратный анус. Другие - что какашки приобретают необычную форму при прохождении костей таза. Третьи - что вомбаты лепят из какашек геометрически строгие куличики уже, так сказать, пост-фактум - посрамши.

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

Знание - сила!
Forwarded from Ofee
Если ты в душе плюсовик — ты и на JS уб понапишешь
Скажите что-то хорошее о канале
Скажите что-то плохое о канале
#prog #rust #meme

Какой же ор. Жаль, что те, кто не пишут на Rust, не поймут.
Forwarded from мне не нравится реальность (вафель 🧇🍓)
This media is not supported in your browser
VIEW IN TELEGRAM
Forwarded from мне не нравится реальность (вафель 🧇🍓)
вафель держит Илью 2021 год, фото в цвете