Pull to refresh

Comments 26

Очень увлекательно, но есть тонкий момент - почему компилятор оставил в данных весь Canvas, хотя мог сохранить только Canvas[0][0] ?

MSVC в общем не славится своими оптимизациями. Может, GCC или clang бы соптимизировал в один Canvas[0][0]

@AskePitВы можете расшарить код на godbolt.org, чтобы проверить теорию? (clang trunk, по идее, все используемые вами фичи должен поддерживать точно; может, и gcc trunk, но не уверен)

Так, вот, кажется, рабочая ссылка https://godbolt.org/z/3o55Y7dv3
Я всегда теряюсь в интерфейсе Compiler Explorer и плохо в нем ориентируюсь, поэтому если сможете, расскажите потом о результатах :)

xD
как объявится время, пойду обманывать компиляторы

Проще всего заменить main на какую-нибудь

Canvas foo()
{
    return newLife;
}

И собирать объектный файл, а не исполняемый.

На самом деле меня тоже волновал этот вопрос) Но не вырезал и не вырезал, я пошел дальше с тем, что есть.

На самом деле, мне кажется, обмануть компилятор не так сложно в этом деле, поэтому если какой-то из них вырежет, то условный volatile или std::cout может исправить ситуацию

Вообще константа должна быть помещена в rodata только в том случае, если где-то в коде необходим её адрес, то есть MSVC, судя по всему, перевёл Canvas[0][0] в *(*(Canvas + 0) + 0), заметил в выражении использование адреса константы и поместил её в бинарник вместо того, чтобы сразу подставить в return true, но это лишь предположение

Прикольная история, мне такое нравится, сам помешан на constexpr немного

C++26
#include <cstddef>

template <size_t N, size_t M>
class Field {
    static constexpr size_t SIZE = N * (M + 1);

    char s[SIZE];

    consteval char& _c(size_t i, size_t j) {
        return s[(M + 1) * i + j];
    }

public:
    consteval Field() {
        for (size_t i = 0; i < N; ++i) {
            _c(i, 0) = '\n';
        }
    }

    consteval char& c(size_t i, size_t j) {
        return _c(i, j + 1);
    }
    
    consteval size_t size() {
        return SIZE;
    }

    consteval const char* data() {
        return s;
    }
};

const size_t N = 16;
const size_t M = 16;

consteval auto message() {
    Field<N, M> f;
    
    for (size_t i = 0; i < N; ++i) {
        for (size_t j = 0; j < M; ++j) {
            f.c(i, j) = (i + j) % 4 ? '_' : '@';
        }
    }

    return f;
}

int main() {
    static_assert(false, message());
}

<source>: In function 'int main()':
<source>:49:19: error: static assertion failed: 
@___@___@___@___
___@___@___@___@
__@___@___@___@_
_@___@___@___@__
@___@___@___@___
___@___@___@___@
__@___@___@___@_
_@___@___@___@__
@___@___@___@___
___@___@___@___@
__@___@___@___@_
_@___@___@___@__
@___@___@___@___
___@___@___@___@
__@___@___@___@_
_@___@___@___@__
   49 |     static_assert(false, message());
      |                   ^~~~~

Не компилируется — не значит «не работает» :)

Вот то же самое хотел сказать. Правда, думал, как бы заставить компилятор человекочитаемую ошибку вывести, а до кастомного сообщения в static_assert, приводимого к char*, фантазии не хватило.

Ах да, это C++26. %-/

Там немного сложнее: в C++26 добавлена перегрузка для static_assert, позволяющая передать user-generated сообщение. До этого можно было передать только литерал. Однако ассерт ожидает не const char*, а объект с методами data() и size(). То есть, фактически, можно напрямую скормить ему std::vector<char> или std::string, но посимвольная конкатенация для строки немного медленно работает на большом цикле.

Вот, наговнокодил на С++20

https://gcc.godbolt.org/z/zTPGnEfzo

Вкратце

struct field { char xxx[Y][X]; };
// заполняем весь массив символами . и @

constexpr field get_my_field() { ..... }

template<field f> void bar() = delete;

int main() {
  bar< get_my_field() >();
}

только потом надо будет распарсить выхлоп

<source>:70:26: error: use of deleted function 'void bar() [with field f = field{char [16][16]{
"...............",
"...............",
"...............",
"....@..........",
"..@.@..........",
"...@@..........",
"...............",
"...............",
"...............",
"...............",
"...............",
"...............",
"...............",
"...............",
"...............",
"..............."
}}]'

Только clang показывает часть клеток, да и вообще в ascii, а MSVC вообще собрать в режиме /std:c++20 не может :(

В порядке оффтопа (но не совсем). Некогда на Спеке был прорывной редактор/макроассемблер Tasm 4 (минорную версию забыл) за авторством Rst7. И там на макросах был написан пример - питончик, в который можно было играть, запустив компиляцию (т.е. это был полноценный compile-time). Не помню, как именно это было реализовано, но на тот момент это здорово так впечатляло.

Статья понравилась, жалко у меня не хватает кармы, чтобы повысить вам карму.

Узнал немного нового о compile time. Даже появилась идея, как возможно можно решить одну старую задумку, которая у меня до этого не получалась.

Когда-то давно тоже делал игру в жизнь, но с возможностью менять правила жизни мира в процессе выполнения.

П.С. Спасибо за статью)

Если карма больше 50, то от ещё одного повышения ни жарко ни холодно, так что не стоит переживать. А вообще, карма - запретное слово на Хабре. Ну во всяком случае было раньше.

В C++ уже больше 10 лет существует constexpr, который позволяет программисту ушло возложить часть вычислений на компилятор. В свое время это взорвало мне мозг, ведь компилятор может посчитать какие-то достаточно сложные вещи еще до запуска программы!

У Анатолия Воробья весной был конкурс, победитель которого как раз использовал этот подход. Программа компилировалась 6 секунд, но зато результат выдавала мгновенно, заведомо опередив всех остальных участников :)
https://avva.livejournal.com/3657269.html

Очень классный пост, даже набрался смелости немного дополнить код: https://godbolt.org/z/z5Y5ban5c. Суть крайне проста - с помощью рекурсии собираем матрицу из "прожитых" циклов и компилируем и вытаскиваем не по одной за раз, а сразу несколько, в зависимости от глубины. Мой msvc22 и gcc годболта выдают лучший результат при глубине <32, около 12 циклов в секунду

Воу, хитро! Достойное продолжение дела)

Помним — мы на Windows, и наиболее логичный компилятор, который нам доступен — это компилятор, который использует Visual Studio — Microsoft Visual C++ или MSVC.

А clang на Windows разве не работает?

Прочитал от корки до корки, интересно, спасибо!

Афтор жжет! Безумно интересно и читается на одном дыхании. Спасибо!

Просто оставлю это здесь как родственную по духу статью.

В своем подходе автор-таки запускает exe, но лишь с целью отрендерить один кадр в файл. Луп крутит bash, а все действо происходит на Linux и компилируется g++. Из занятного - баш ловит ввод пользователя и передает его в g++ в качестве макро-definition

Sign up to leave a comment.

Articles