Задать вопрос
  • Приведите пример использования readObjectNoData() при десериализации?

    @Mercury13
    Программист на «си с крестами» и не только
    Так просто, одним куском кода, это сделать нельзя. readObjectNoData() работает только на враждебных данных, которые ещё надо ухитриться сфабриковать.

    // Пишет вот такой код
    class A implements Serializable { }
    class B extends A {}


    // А читает вот такой
    class A implements Serializable { }
    class AHalf extends A {
      private void readObjectNoData() throws ObjectStreamException {}
    }
    class B extends AHalf {}


    Если записать объект класса B первым кодом и прочитать вторым, должно сработать.

    Смысл readObjectNoData() — привести объект в подходящее состояние, когда 1) данных по промежуточному классу AHalf нет; 2) у него какие-то нехорошие инварианты, что так просто в свежеинициализированном состоянии он негодный: ну, к примеру, конструктор по умолчанию скрыт и служит только для десериализации, а так он просит объект для своего создания.
    Ответ написан
    Комментировать
  • Какой алгоритм можно применить при проверки числа на простое ли оно?

    @Mercury13
    Программист на «си с крестами» и не только
    Если нужно проверить а) точно, и б) одно; в) не очень большое число (миллион тоже небольшое) — ничего нет лучше, чем проверка нечётных чисел до корня из n. То есть до 1000.

    Если точно, много и небольшие — то придётся держать список простых чисел, пополняя его, когда попадётся слишком большое число. Список тоже до корня из n. Допустим, если предел — int4 (≈4 млрд), то нужно держать только список до 65535, это пара тысяч чисел.

    Если число совсем небольшое и может быть где-то в списке — ищем его хитрой разновидностью поиска: проверяем 1-е число, 2-е, 4-е и т.д., пока не определим диапазон, где может быть число. И в этом диапазоне ищем двоичным поиском.

    В криптографии востребован неточный поиск — «число, скорее всего, простое». Но об этом не будем, вы не настолько круты. Тут уже основано на том, что держим таблицу небольших простых чисел и делим на них, а затем гоняем неточные тесты.

    PERFECT number — это СОВЕРШЕННОЕ число. Это не то (сумма всех делителей равняется самому числу), и для теста на совершенное число тоже надо проверять до корня из n — если a делится на b, то добавляем и b, и a/b (кроме случаев, когда b=1 и b²=a, разумеется). Если есть простые числа до корня из n — тоже можно разбить на простые множители (один из множителей может быть больше корня из n!) и подключить комбинаторику, чтобы заполучить остальные.
    Ответ написан
    2 комментария
  • Можете посоветовать какой-нибудь задачник по патернам проектирования?

    @Mercury13
    Программист на «си с крестами» и не только
    Задачник невозможен, потому что…
    1. Паттерны проявляют себя в крупных программах. Даже 2000 строк можно написать по наитию.
    2. Нет единого ответа. И как вы будете сверяться, верно вы сделали или нет?
    3. Сильно зависит от языка: например, идиома pimpl — это чистый Си++.
    4. Даже зависит от того, библиотека встроенная или внешняя: внешние библиотеки в низкоуровневый код всегда стараются вносить очень осторожно.

    Пример: нам надо включать-выключать лог в консольной программе, простейший вопрос. Варианты…
    1. Просто переменная isLoggingOn, которая включает этот самый лог.
    2. Одиночка-стратегия-null object.

    И тут появляется вопрос: а что делать, если у нас тут ещё и форматирование на манер printf? Где находится утилита форматирования: в «волшебном круге» интерфейса Logger или нет? А если нет — то можно придумать модуль-утилиту за пределами этого «волшебного круга»? А если пишем на Си#, может, этот модуль-утилита — это метод-расширение? А если пишем на Си++, может, вообще можно не заморачиваться из-за того, что связанные с std::format функции шаблонные и если ни одна не расшаблонилась — с нас и взятки гладки?
    Ответ написан
    Комментировать
  • Почему строку можно изменить через массив, но через указатель нельзя?

    @Mercury13
    Программист на «си с крестами» и не только
    Потому что в Си++ — в отличие от Си — строковый литерал "hello" имеет тип const char[]. Как дополнительную подсказку, что даже если система (скажем, DOS) не имеет разделения памяти по типам и позволяет менять такие литералы — Windows имеет и не позволяет.

    РАЗ. У указателя и массива несколько разная семантика
    char hello1[] = "hello";   // массив длины 6, в изменяемом сегменте или стеке,
                             // данные скопированы из литерала, который
                             // сидит в неизменяемом сегменте
    const char* hello2 = "hello";   // указатель направлен прямо на литерал,
                             // и попытка изменить его под Windows — вылет


    ДВА. Компилятор имеет право спрессовать два литерала в один, и смена одного, скорее всего, сменит и другой. Не могу проверить на Windows — говорил же, что запись в строковый литерал под Windows приведёт к вылету — но, скорее всего, так будет. Что-то вроде
    char* hello1 = const_cast<char*>("hello");
    char* hello2 = const_cast<char*>("hello");
    hello1[1] = 'U';   // hello2 = "hUllo" в системах вроде DOS, где не вылетит
    Ответ написан
    4 комментария
  • Стоит ли использовать блоки кода для ограничения области видимости?

    @Mercury13
    Программист на «си с крестами» и не только
    Иногда, чтобы прикрыть теоретическую возможность важной ошибки.
    Чаще нужно в Си++ с его автодеструкторами.
    На чтение особо не влияет.

    Вот пример на Си…
    { char* buf = calloc(n, sizeof(char));  // Ради чего я создал блок — ставлю в той же строке
        snprintf(buf, n, some_shit);
        strcpy(dest, buf);
        free(buf);
    }


    Ну а это чистые «кресты».
    { std::ofstream os(fname);
       os << someData;
    }  // Тут файл закрыт
    Ответ написан
    Комментировать
  • Могу ли я активно ссылаться на видео с ютуба?

    @Mercury13
    Программист на «си с крестами» и не только
    Нет, не нарушите.
    Но сам YT может запрещать проигрывать видео на посторонних сайтах.
    Ответ написан
    2 комментария
  • Можно ли добиться постоянного O(nlogn) для квиксорта в любом случае?

    @Mercury13
    Программист на «си с крестами» и не только
    Наиболее распространённый метод — IntroSort. Если рекурсия ушла глубоко, переключиться на другой метод. Постоянные n log n и не теряются классные свойства QuickSort.

    Да, есть методы именно на быстрой сортировке, но, видимо, умные люди всё испытали и ничего лучшего не нашли.
    Ответ написан
    Комментировать
  • Почему методы length() и codePointCount() для знака копирайта (с) возвращают одинаковую длину?

    @Mercury13
    Программист на «си с крестами» и не только
    1F12F — это символ копилефта.
    А точно вы имеете дело с ним, а не вот с этим эмодзиком?
    6765db2f10ea3231097248.png
    В нём два символа базовой плоскости (codePointCount). Длина в единицах UTF-16 — тоже два (length).

    UPD. Да, вопрос
    Почему String.codePointCount() возвращает 2 для символа ©?
    ваш, и вы в нём отметили неправильный ответ. Второй символ - не пробел, а селектор начертания 16.

    Программа, если что, моя, и зовётся «Юникодия». Гуглите, она первая в выдаче.
    Ответ написан
    Комментировать
  • Что делать с переполнением дополнительного кода числа после умножения?

    @Mercury13
    Программист на «си с крестами» и не только
    В случае, если N=2b, тогда
    (N+y)(N+v) = N²−N(|y|+|v|)+yv ≡ yv (mod N), и, таким образом, можно множить дополнительные коды, растянув их до длины N².
    Как поступать, не растягивая? — ну, добавить N(|y|+|v|), наверное.
    Итак, ваши числа ДЕВЯТИбитные (N=512), и символизируют y=−217, v=−161. Ну, соответственно N+y=295, N+v=351.
    (N+y)(N+v) + N(|y|+|v|) = 295·351 + 512·(217+161) = 297081,
    N² = 262144,
    Выкинем один переполненный бит 297081−262144 = 34937,
    Ну что, есть у нас (−217)·(−161)?

    Что вы неправильно сделали…
    1. Я не понимаю, что здесь делают 2(|y|+|v|).
    2. Числа у нас ДЕВЯТИбитные, так что и начальные единицы нельзя выкидывать, и сдвиг должен быть на девять бит, а не на восемь или десять.

    Если попробовать отрубить знаковый бит и N²=65536…
    N=256, y=−217, v=−161, N+y=39, N+v=95.
    (N+y)(N+v) + N(|y|+|v|) = 39·95 + 256·(217+161) = 100473,
    вычтем переполненный бит 100473−65536=34937
    Но тогда придётся выкинуть ОБЕ начальных единицы, а сдвиг делать на 8.
    С отрубленными знаковыми битами только знание, что оба числа отрицательные, позволяет трактовать эти 34937 как 16-битное беззнаковое. А то мы же отрубили все признаки этого, верно?

    А то вы одну выкинули, а другую оставили.
    Ответ написан
    3 комментария
  • Как работает стирание типов?

    @Mercury13
    Программист на «си с крестами» и не только
    Потому что в коде
    Pair emp = person;
    …не угадываются шаблонные параметры, а просто берётся Pair<Object>.
    Ответ написан
    Комментировать
  • Почему объект не передается по ссылке?

    @Mercury13
    Программист на «си с крестами» и не только
    В BoxContainer, скорее всего, не происходит инициализация nbox.
    Поскольку NumberBox не имеет конструктора по умолчанию, компилятор ругается, что нельзя это поле инициализировать по умолчанию.
    Так что варианта два
    1. nbox инициализировать, наконец.
    BoxContainer::BoxContainer(NumberBox& nb) : nbox(nb) { ... }

    2. Придумать NumberBox конструктор по умолчанию
    class NumberBox {
    	...
    public:
    	NumberBox();
    	NumberBox(int i);
    	...
    };
    Ответ написан
    1 комментарий
  • Почему для добавления единичного бита используется именно 0x80?

    @Mercury13
    Программист на «си с крестами» и не только
    Мы обычно привыкли к порядку байтов в слове. Однако в криптографии и телекоммуникациях также играет роль порядок битов в байте — обычно у младшего бита номер 0, а у старшего 7. Это упрощает произвольный доступ к любому биту, передачу информации в порт по одному биту — я писал самодельные низкоуровневые коммуникационные протоколы, правда, давно и под DOS. Но тут, по-видимому, наоборот — байт начинается со СТАРШЕГО бита. Вижу в этом две причины.
    1. Межплатформенный стандарт двоичной передачи данных — передавать длинные числа, начиная со старшего байта, просто для удобства ручного исследования протоколов. Тут ручное исследование доведено до предела: байт делится на биты, которые также записываются от старшего к младшему — как мы бы записывали на бумаге.
    2. Криптографию по-чёрному оптимизируют, чтобы она оперировала не байтами, а самыми длинными единицами, доступными процессору: 64-битный — значит, блоками по 8 байтов.

    0x80 = 0b1000'0000. Вот вам и единичный бит и куча нулей.
    Ответ написан
    Комментировать
  • Как именовать два метода один из которых выбрасывает исключение?

    @Mercury13
    Программист на «си с крестами» и не только
    findById и requireById.
    В данном случае.
    Ответ написан
    Комментировать
  • Почему String.codePointCount() возвращает 2 для символа ©?

    @Mercury13
    Программист на «си с крестами» и не только
    Он не прав.
    Второй символ не пробел, а селектор начертания 16.
    Используется во многих символах базовой плоскости, чтобы превратить их в эмодзи.
    А уж в символах двойного назначения — и типографский, и эмодзи — и подавно.
    67253fc645640534308923.png
    Ответ написан
    Комментировать
  • Правильно ли вынесение метода проверки в другой сервис для упрощения тестирования?

    @Mercury13
    Программист на «си с крестами» и не только
    check1() иногда годится, если проверка многократно выполняется в разных функциях, по принципу «не повторяйся». Например, для const-корректности Си++, или для хитрых интерфейсов, когда есть objectId(i), objectName(i).

    А так зачем: тестирования с виду не упростит, ведь мы должны убедиться, что интерфейс верно реализован и действительно некорректные наборы данных (все или хотя бы некоторые) не проходят.

    UPD. Вынесение РАЗОВОЙ функциональности (если это реакция на некорректные данные, конечно) в отдельную функцию КРАЙНЕ РЕДКО делает код более тестопригодным: может быть, что реакция на некорректности так размыта, что вызов основной функции мало что даст. Может, реакция на некорректности так сложна, что стоило бы её проверить отдельно. Ну ХЗ, в большинстве случаев бессмысленно.

    UPD2. Тестируемость кода — это в первую очередь возможность выделить функциональность для тестирования и не тащить вместе с ней половину программы.
    Ответ написан
    Комментировать
  • Для чего нужен слой сервиса без логики?

    @Mercury13
    Программист на «си с крестами» и не только
    Нет, для автоматического построения связей Spring’ом.
    Почему-то этот товарищ сделал эту штуку очень тонкой обёрткой над предметной логикой: то ли имя findWriteLockedByName не нравится, то ли не знал, будет сделана служба на Spring или на чём-то другом.
    Ответ написан
    Комментировать
  • Расскажите пожалуйста как реализован sin в cmath?

    @Mercury13
    Программист на «си с крестами» и не только
    SET_RESTORE_ROUND_53BIT (FE_TONEAREST);
    Настройка сопроцессора. Это какая-то магия, имеющая две задачи: производительность и повторяемость.

    Дальше мы залезаем в устройство числа (причём для краткости имеем дело с 32-битными командами!) и проверяем на «малость» — если получилось малое, то sin x ≈ x.

    Дальше идёт проверка на |x|≲45° — идёт вычисление настоящего синуса. Опять-таки, проверка по верхним 32 битам числа (синус вычисляем по всем 64 битам ☺️).

    Если число не очень большое, мы загоняем его в диапазон ±45° и вычисляем синус или косинус.

    Если число побольше — идёт более злой загон в диапазон ±45° и то же самое.

    И последнее, что осталось,— ∞/NaN.

    Внутренние функции обозревать не буду, но что мы тут видим?
    1. Какие значения бывают чаще, какие реже?
    2. Для очень маленьких значений sin x ≈ x, cos x ≈ 1.
    3. Ещё одно — залезание во внутренний формат компьютерного дробного, причём даже на x64 имеем дело с 32-битными целыми.
    4. Даже функция приведения в ±45° есть в двух видах — упрощённом и «злом» в зависимости от абсолютной величины числа.
    Ответ написан
    4 комментария
  • Ошибка в вариативном шаблоне. Что не так?

    @Mercury13
    Программист на «си с крестами» и не только
    #include <iostream>
    #include <string>
    
    struct T {
        int x;
        std::string y;
    };
    
    T Deserialize(std::istream& stream, auto T::*... properties)
    {
        T object = {};
        auto FillObject = [&object, &stream] (auto property)
        {
            stream >> object.*property;
        };
    
        (FillObject(properties), ...);
        return object;
    };
    
    int main()
    {
        T r = Deserialize(std::cin, &T::x, &T::y);
        std::cout << "<" << r.x << "> <" << r.y << ">" "\n";
        return 0;
    }

    Что сделано: исправлен вызов FillObject (важно), уточнён тип properties (лучше).
    Ответ написан
    Комментировать
  • Как правильно организовать модули C++?

    @Mercury13
    Программист на «си с крестами» и не только
    Не использую просто потому, что модули плохо устаканились. Посмотрел по MinGW — всё ещё экспериментальная функциональность.
    Для стандартных модулей: если поддерживаете Си++23, то пожалуйста. Это обычно главный источник медленной компиляции. Мой проект пока только 20.
    Для сторонних библиотек: можно прямо сейчас, если они собраны в модуль. Ни одной такой пока не видел, тем более в G++, как я сказал, это пока эксперимент.
    Для собственных модулей: убедитесь, что их поддерживает система сборки. В Си++26 усилят требования к модулям, потому что взвыли разработчики этих самых систем. И, как я сказал, в G++ модули — всё ещё эксперимент.
    Ответ написан
    Комментировать
  • Используются ли контекстные меню в играх?

    @Mercury13
    Программист на «си с крестами» и не только
    По правой кнопке не помню, хотя вроде бывают.
    При щелчке показываются иконки вокруг, например, в Incredible Machine. Или во многих квестах. Или в Fallout 1/2.

    Что контекстному меню мешает.

    1. Плохая раскрываемость (discoverability, возможность найти всё это тыком и интуицией). Игры всё-таки должны собирать большую публику необученных пользователей, верно? Я когда-то учил одну даму ходить по Witness (у неё FPS-управление, но никакой реакции не нужно) — так и не смог. Сам прошёл с большим удовольствием, хотя посторонних головоломок нашёл от силы половину.

    Опытный пользователь рабочих программ просто привык — и то, например, в редакторе Unreal Tournament есть одно очень подлое контекстное меню, которое хрен раскроешь (на заголовке каждого из четырёх видов: спереди, сверху, слева, в перспективе).

    В том же Fallout 1/2 есть команда «оттолкнуть» — неписи ходят по своим делам, и если он загородил нужный проход и никуды, приходится на него действовать этой командой. Понятна только через инструкцию.

    2. Нежелание превращать игру в рабочую программу. Чем дальше интерфейс игры от программы, тем лучше иллюзия. Некоторые авторы — сейчас, правда, это сложно, с управлением из шести осей и десяти кнопок — вообще не делают интерфейса. Хоть «↑ = зайти» да и делают даже в играх вроде Braid и Fez.

    3. Если объект подвижный, бывают глюки, когда ты щёлкнул на него, а он ушёл или даже погиб. Если статус-панель опустела, пока мы нажимаем, мы жмём в никуда. Если меню исчезло — жмём куда-то на игровом поле. Подобное больше годится для чего-то неподвижного и прочного.

    4. Теряется правая кнопка мыши. У опытного игрока, который использует всю клавиатуру и даже больше, вдруг отбирают одну кнопку.

    5. Опять-таки раскрываемость — если есть команда, скажем, «патрулировать», не очень понятно, что кнопка P действует, даже если контекстное меню не раскрывать.

    6. Со всей этой раскрываемостью, боковыми панелями и прочим интерфейсом контекстное меню кажется излишним, и годится в двух случаях: или нет статус-панели, или она не отображает наш объект.

    7. А если ещё важно заранее раскрыть и момент улучить — меню мешать будет.

    В общем, контекстное меню может пригодиться, но, во-первых, в интерфейсе, а не на игровом поле. Ну или в медленной игре, где игровое поле мало чем отличается от интерфейса. А во-вторых, оно по определению дублирует команды, а если в игре их немного (иначе хрен этой игре обучишься), ничего не стоит вывалить их все другим методом. А те игры, которые делают доступ ТОЛЬКО через контекстное меню — ну, учитесь у Fallout 1/2 и Incredible Machine.

    UPD. Вспомнил. В Mafia и некоторых частях Hitman, если действий вокруг много, по кнопке «Действие» выводит меню: что выбираешь?
    Ответ написан
    Комментировать