Hacker News Digest

Тег: #macros

Постов: 5

Recursive macros in C, demystified (once the ugly crying stops) (h4x0r.org)

В статье разбираются рекурсивные макросы в C, которые автор называет самой неприятной частью языка, несмотря на его 60-летнюю историю успеха. Макросы критически важны для многих систем, так как позволяют абстрагировать сложность, добавлять проверки и обеспечивать безопасность, но их система имеет серьёзные ограничения. Основная проблема: макросы в C не поддерживают рекурсию, что, по мнению автора, могло быть как случайностью эволюции системы, так и сознательным решением для предотвращения бесконечных циклов компиляции.

Автор мотивирует необходимость рекурсивных макросов возможностью обрабатывать переменное количество аргументов, что особенно ценно для создания безопасных вариадических функций. Цель статьи — создать макрос, считающий количество аргументов, что позволит автоматически добавлять проверки и избегать ошибок, связанных с ручным подсчётом. Хотя система макросов кажется устаревшей, она остаётся единственным механизмом компиляции времени в C, делая изучение этих техник важным для разработчиков.

by eatonphil • 06 ноября 2025 г. в 01:09 • 123 points

ОригиналHN

#c#macros#preprocessor#metaprogramming#recursion#variadic-functions

Комментарии (58)

  • Обсуждение охватывает историю и ограничения препроцессора C, включая его влияние на языки C и C++, а также затрагивает вопросы безопасности (DoS-риски) и читаемости кода.
  • Участники обсуждают, что препроцессор не предназначен для сложной метапрограммирования, и что современные языки предлагают более безопасные и выразительные альтернативы.
  • Также обсуждается, что хотя препроцессор и может быть использован для продвинутых задач метапрограммирования, он требует нестандартных и сложных приемов, что делает его использование непрактичным и потенциально опасным.
  • Некоторые участники упоминают, что препроцессор может быть использован для создания сложных макросов, которые могут быть использованы для создания DSL или для экспериментов с синтаксисом языка, хотя это может быть опасным.
  • В целом, обсуждение подчеркивает, что хотя препроцессор может быть использован для сложных задач, он не предназначен для этого и что современные языки предлагают более безопасные и выразительные средства для решения таких задач.

Advanced Scheme Techniques (2004) [pdf] (people.csail.mit.edu)

- PDF-файл повреждён: вместо текста — поток сжатых байтов.  
- Содержимое невозможно прочитать без распаковки и декодирования.  
- Перевод и сокращение не применимы: исходных данных нет.  

by mooreds • 12 сентября 2025 г. в 15:44 • 92 points

ОригиналHN

#scheme#continuations#closures#cps#macros

Комментарии (15)

  • Scheme обманчиво прост, но сложен для компиляции: гигиеничные макросы и продолжения первого класса требуют особых подходов.
  • CPS-преобразование — популярный, но не единственный способ реализовать continuations; подходят и байт-код/виртуальные машины, и другие схемы.
  • Реализация closures и правильная работа с пустым списком (не «ложь» в Scheme) добавляют сложности.
  • Полезны материалы: диссертация Dybviga, доклад Олега Киселёва, статья о секвентном исчислении как IR.

Why Nim? (undefined.pyfy.ch) 💬 Длинная дискуссия

by TheWiggles • 17 августа 2025 г. в 13:28 • 165 points

ОригиналHN

#nim#d#rust#go#zig#macros#compilation#memory-management

Комментарии (175)

  • Участники жалеют, что выразительные языки с нативной компиляцией и автоматическим управлением памятью (Nim, D) не стали массовыми.
  • Любители Nim хвалят его скорость, надёжность компилятора и эргономику, но жалуются на малую экосистему, устаревшую документацию и сложность кросс-компиляции.
  • Скептики считают, что «выразительность» и макросы делают язык нишевым, требуют больше знаний и усложняют чтение чужого кода.
  • Многие отметили, что успех языка определяют не фичи, а деньги, стандартная библиотека, тулинг и сообщество; Rust выиграл именно этим.
  • Часть разработчиков ушла из Nim в Rust, Go или Zig из-за зрелости инструментов и богатой экосистемы, но продолжают следить за Nim и надеются на его рост.

Generic Containers in C: Safe Division Using Maybe (uecker.codeberg.page)

Показываю, как в C сделать обобщённый контейнер maybe, который безопасно возвращает результат, если он есть, и сообщает об ошибке, если её нет.

#define maybe(T) struct { bool ok; T value; }
#define maybe_just(T,x)  { .value = (x), .ok = true }
#define maybe_nothing(T) { .value = (T){}, .ok = false }

static maybe(int) divide(int a, int b) {
    return (b != 0) ? maybe_just(int, a / b) : maybe_nothing(int);
}

Вызов:

maybe(int) p = divide(6, d);
if (p.ok) printf("%d\n", p.value);
else       puts("division by zero");

Чтобы не забыть проверку, добавляем макрос maybe_value, который при ошибке возвращает нулевой указатель, ловим его санитайзером:

#define maybe_value(x) \
    (*({ auto _p = &(x); _p->ok ? &_p->value : (void*)0; }))

Но деление на ноль — не единственная проблема. При делении INT_MIN / -1 возникает переполнение. Исправляем:

maybe(int) safe_divide(int a, int b) {
    if (b == 0 || (b == -1 && a == INT_MIN))
        return maybe_nothing(int);
    return maybe_just(int, a / b);
}

Компилируем с -fsanitize=signed-integer-overflow,integer-divide-by-zero -fsanitize-trap=undefined -O2. В ассемблере не остаётся пути к ud2, то есть оптимизатор доказал: переполнения и деления на ноль нет.

Это не делает весь C «безопасным» (жизненный цикл указателей и арифметика не покрыты), но для ограниченных задач подход работает.

by uecker • 11 августа 2025 г. в 05:14 • 91 points

ОригиналHN

#c#containers#error-handling#macros

Комментарии (46)

  • Критика: реализация не заставляет проверять результат, теряя главное преимущество Maybe.
  • Ассемблер: GCC выдаёт почти тот же код, что и для std::optional, но не возвращает результат в регистре.
  • UB: «пустой» lvalue в случае ошибки вызывает неопределённое поведение; автор полагается на null-sanitizer.
  • Эргономика: предлагают добавить and_then/or_else и сделать тип непрозрачным через макросы.
  • Почему не другой язык: встраиваемые/фирменные проекты часто ограничены только C или ASM.

Comptime.ts: compile-time expressions for TypeScript (comptime.js.org)

Простой компилятор TypeScript для вычисления выражений с пометкой comptime на этапе сборки. Полезно для переноса вычислений из рантайма в компиляцию. Вдохновлено Bun macros и Zig comptime.

Внимание: вы сами отвечаете за безопасность выражений, вычисляемых на этапе компиляции. Изоляции нет. Импорты comptime допускаются только в файлах проекта (не в node_modules), но можно импортировать из node_modules как comptime.

Содержание

  • Что такое comptime.ts?
  • Примеры: 1) простая сумма; 2) CSS без рантайма; 3) константы во время сборки
  • Установка
  • Использование: Vite, Bun, CLI, API
  • Принудительная оценка и промисы, отказ от «вирусности»
  • Запуск кода после comptime, как работает, ограничения, практики, отладка, поддержка, лицензия

Что это comptime.ts вычисляет выражения при компиляции, сокращая работу в рантайме.

Примеры

  1. Простая сумма import { sum } from "./sum.ts" with { type: "comptime" }; console.log(sum(1, 2)); // => console.log(3);

  2. Emotion CSS без рантайма import { css } from "@emotion/css" with { type: "comptime" }; const style = csscolor: red; font-size: 16px;; div({ class: style }); // => const style = "css-x2wxma"; div({ class: style });

Примечание: импорт @emotion/css удаляется. Стили нужно вывести отдельно (после comptime или плагином бандлера).

  1. Константы на этапе сборки import { ms } from "ms" with { type: "comptime" }; const HOUR = ms("1 hour"); // => const HOUR = 3600000;

Поддерживаются многие выражения (включая индексацию и импортированные константы), результат должен быть сериализуем в JSON. Импорты с type: "comptime" удаляются; лишнее убирает ваш бандлер.

Установка bun add comptime.ts pnpm add comptime.ts npm install comptime.ts

Использование

  • Vite: import { comptime } from "comptime.ts/vite"; export default defineConfig({ plugins: [comptime()] });

Только в прод-сборке, если поведение совпадает с рантаймом: export default defineConfig({ build: { rollupOptions: { plugins: [comptime()] } } });

  • Bun: import { comptime } from "comptime.ts/bun"; await Bun.build({ entrypoints: ["./index.ts"], ou ... })

by excalo • 03 августа 2025 г. в 19:11 • 139 points

ОригиналHN

#typescript#compilation#vite#bun#zig#macros

Комментарии (30)

  • Обсуждение крутится вокруг идеи “comptime”/макросов в JS: часть хочет Rust‑подобные макросы и proc‑макросы (вплоть до JSX как jsx! или вообще писать фронт на Rust/wasm), другая сторона категорически против макросов в TS/JS.
  • Есть путаница в терминах: “макросы” vs “comptime”; участники критикуют переиспользование терминов и вспоминают неудачный опыт sweet.js.
  • Практические вопросы: можно ли делать агрессивный dead‑code elimination через if (comptime …) как в C препроцессоре? Ответ: само comptime подставит true/false, но для выкидывания веток нужен отдельный шаг минификатора/бандлера (Vite/Bun поддержат).
  • Дискуссия об импорте with { type: 'comptime' }: одни считают это неправильным использованием атрибута type (ожидается соответствие MIME), другие указывают, что спецификация оставляет семантику type открытой.
  • Обсуждают границы возможности: поддержка типов/генериков на уровне comptime (как в Zig) пока ограничена; возврат именованных функций и сложные случаи с замыканиями не поддерживаются из‑за требований к гарантиям и сохранению функций между процессами.
  • Альтернативы: настроить сборку для JSX без макросов; использовать библиотеки вроде lite-jsx; для Rust‑фронта рекомендуют Dioxus/Leptos; спорят о реальной применимости wasm и памяти/управления ей в вебе.
  • Применимость: идея удобна для предсборки (например, markdown) и константной подстановки, но не заменяет полноценных препроцессоров/макросистем уровня Rust/Zig.