• Header-only. Страдает ли размер бинарника?

    @Mercury13
    Программист на «си с крестами» и не только
    Для шаблонов Си++ в линкеры пришлось добавлять специальное исключение — ведь расшаблоненные функции будут повторяться в каждом имеющемся объектном файле.

    Теста ради сделал такой проект

    // MAIN.CPP
    #include <iostream>
    
    #include "tmpl.h"
    
    void doFile1();
    
    int main()
    {
        const char* x = "main";
        outThing(x);
        doFile1();
        return 0;
    }
    
    // FILE1.CPP
    #include "tmpl.h"
    
    void doFile1()
    {
        const char* x = "doFile1";
        outThing(x);
    }
    
    
    // TMPL.H
    #pragma once
    
    #include <iostream>
    
    template <class T>
    void outThing(const T& x)
    {
        std::cout << "The thing is " << x << std::endl;
    }


    Для чего x — при расшаблонивании появилось outThing(char[5]&) и соответстсвенно char[8]&.
    А теперь дамп линкера
    Discarded input sections
    
     .text$_Z8outThingIPKcEvRKT_
                    0x0000000000000000       0x50 debug/main.o
    
    Linker script and memory map
    
    .text$_Z8outThingIPKcEvRKT_
                    0x0000000140002900       0x50 debug/file1.o
                    0x0000000140002900                void outThing<char const*>(char const* const&)
    
    Ну и ещё парочка структур для раскрутки стека и подстановки адресов…


    Так что даже при -O0 оно не будет ничего дублировать. Да и логикой понятно: что дублировать, что не дублировать — один хрен потребуется специальная логика, и разницы мало.
    Ответ написан
    1 комментарий
  • Легально ли перепродавать лицензию Zoom с наценкой?

    @Mercury13
    Программист на «си с крестами» и не только
    Запрещено. Ну если специально не договоритесь с Зумом.

    You may not reproduce, resell, or distribute the Services or any reports or data generated by the Services for any purpose unless You have been specifically permitted to do so under a separate agreement with Zoom. You may not offer or enable any third parties to use the Services purchased by You, display on any website or otherwise publish the Services or any Content obtained from a Service (other than Content created by You) or otherwise generate income from the Services or use the Services for the development, production or marketing of a service or product substantially similar to the Services.
    Ответ написан
    Комментировать
  • Почему берутся проблемы с совместимостью массивов и контейнеров?

    @Mercury13
    Программист на «си с крестами» и не только
    Учите матчасть!
    Версия для буфера памяти предполагает, что данные хранятся в непрерывном буфере nm×double (то есть длина mn, элемент double).
    Но vector<vector> — он НЕ непрерывный буфер.
    Есть непрерывный буфер n×vector<double> — центральный вектор. И каждая из n строчек по отдельности — непрерывный буфер m×double. Относительно друг друга в памяти они могут располагаться как угодно.

    Как исправить? — проще всего
    double s = 0;
    for (i...) {
      s += Sum(m, &a[i][0]);
    }

    Можно через шаблоны работать, но это уже сложнее и я сам не смогу это с листа написать, что уж говорить про первокура.
    Ответ написан
    Комментировать
  • Собрали новый пк, при некоторых нагрузках вылетает BSoD. Что делать, куда лезть, как проверять?

    @Mercury13
    Программист на «си с крестами» и не только
    Поскольку автор сказала, что встречался глюк — вместо текстур в играх были какие-то помехи — теперь могу с уверенностью сказать, что память.
    Ответ написан
    Комментировать
  • Что такое указатели в С++?

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

    Либо будет дичайше свопить, либо отказ с нехваткой памяти. А уж как этот отказ устроен — через «падение», зависание, какое-то сообщение… — это может быть по-разному.

    Можете себе представить, если бы небезызвестная Battlefield 3 использовала такой метод работы с данными? В таком случае, самым заядлым геймерам пришлось бы перезагружать свои высоконагруженные системы кнопкой reset после нескольких секунд работы игры.

    Нет. Памяти было бы отведено с запасом уже при запуске, и даже небольшая игра требовала бы многие и многие гигабайты памяти, а Battlefield бы просто не запускался даже на топовых ПК. И часть настроек качества не имела бы смысла — изначально, уже при загрузке EXE’шника, выделено памяти с запасом и никак не ужмёшься. Но нет худа без добра — половины глюков и вылетов не существовало бы. Так что мелкие игры, способные бегать на 99% современных и не очень ПК и потому не имеющие настроек качества, этим механизмом вполне пользуются.

    Разрешите попиариться — пишу программу «Юникодия», энциклопедию символов — таблица на 140 тыс. позиций (количество существующих на данный момент символов) занимает большую часть EXE’шника. А таблица на 1,1 млн (ёмкость Юникода) восьмибайтовых указателей тоже выделена статически, но места в EXE’шнике не занимает и заполняется на старте. Таким образом мы может легко пробежаться по всем символам, и можем по коду символа найти его запись.

    Если не уничтожать неиспользуемые объекты, очень скоро они заполнят весь объем ресурсов ПК.

    Указатели — это не только про уничтожение памяти, но и про выделение.

    Но главное, что вы поняли одно: указатель — это адрес в памяти плюс вкомпилированный в программу тип (на железном уровне типов нет).

    И второе: по указателю мы можем получить доступ к объекту, чьего имени мы не знаем (как у меня в таблице символов) или он безымянный (если он выделен динамически, при работе программы, а не вкомпилирован в неё).
    Ответ написан
  • Конус вращается, какие типы сечений могут быть в неподвижной плоскости?

    @Mercury13
    Программист на «си с крестами» и не только
    Перед нами эллиптический конус, крутящийся вокруг своей оси.
    Плоскость x+y+z=1 не проходит через вершину конуса — так что отпадают одна точка, две прямых-образующих и среднее между ними — одна прямая-образующая.
    Запишем уравнение конуса в стандартном виде: x²/sqrt(2)² + y²/sqrt(1/2)² − z² = 0
    Таким образом, прямое сечение конуса — эллипс с осями z и 0,5z. Угол между осью и образующей варьируется от arctg sqrt(1/2) до arctg sqrt(2) = 45°.
    Найдём угол между осью Z и секущей плоскостью; =угол между плоскостью XY и нормальным вектором к секущей (1,1,1); =угол между вектором (1,1,0) и (1,1,1). Точнее, не сам угол, а его тангенс.
    Дальний катет (0,0,1) и имеет длину 1, ближний (1,1,0) и sqrt2 → значит, тангенс будет sqrt(2). Если этот угол будет больше, чем угол между OZ и образующей — эллипс, если меньше — гипербола, если равен — парабола. Поскольку может быть только больше и равен, будут эллипс и парабола.
    Ответ написан
    Комментировать
  • Как сделать такой финт ушами с double?

    @Mercury13
    Программист на «си с крестами» и не только
    Почему нельзя? Пишу на Си с крестами, но там ничего специфичного нет, кроме парочки перестраховок.
    В Си++20 появился ещё и bit_cast.

    #include <iostream>
    
    union DoubleInt {
        double asDouble;
        uint64_t asInt;
    };
    
    static_assert(sizeof(double) == sizeof(uint64_t), "Strange machine with double != int64");
    
    constexpr int BITS_MANTISSA = 52;
    constexpr int BITS_EXPONENT = 11;
    constexpr int BITS_SIGN = 1;
    
    static_assert(BITS_MANTISSA + BITS_EXPONENT + BITS_SIGN == 64, "Programmer's funkup");
    
    constexpr uint64_t MANTISSA_UNIT = uint64_t(1) << BITS_MANTISSA;
    constexpr uint64_t MANTISSA_MASK = MANTISSA_UNIT - 1;
    
    constexpr int EXPONENT_SHIFT = BITS_MANTISSA;
    constexpr uint64_t EXPONENT_MAX = (uint64_t(1) << BITS_EXPONENT) - 1;
    constexpr uint64_t EXPONENT_ORIGIN = EXPONENT_MAX >> 1;
    constexpr uint64_t EXPONENT_MASK = EXPONENT_MAX << EXPONENT_SHIFT;
    constexpr uint64_t EXPONENT_SHIFTED_ORIGIN = EXPONENT_ORIGIN << EXPONENT_SHIFT;
    
    constexpr int SIGN_SHIFT = BITS_MANTISSA + BITS_EXPONENT;
    constexpr uint64_t SIGN_MASK = uint64_t(1) << SIGN_SHIFT;
    
    int main()
    {
        DoubleInt x { -3.45 };
    
        // Простите уж, без денормализованных чисел
    
        // Оставим знак и мантиссу
        DoubleInt xMantissa = x;
        xMantissa.asInt &= (MANTISSA_MASK | SIGN_MASK);
        // И добавим туда стандартный нулевой порядок
        xMantissa.asInt |= EXPONENT_SHIFTED_ORIGIN;
    
        // Извлечём порядок
        int exponent = ((x.asInt & EXPONENT_MASK) >> EXPONENT_SHIFT) - EXPONENT_ORIGIN;
    
        std::cout << xMantissa.asDouble << "*2^" << exponent << std::endl;
    
        return 0;
    }
    Ответ написан
    1 комментарий
  • Почему у строк всегда одиноковый hashcode()?

    @Mercury13
    Программист на «си с крестами» и не только
    String str1 = "Hello";
    String str2 = "Hello";

    Тут одна и та же ссылка. Но даже если сделать их разными объектами, всё будет аналогично.

    Начнём с того, что такое хэш-код. Если объекты равны, их хэш-коды гарантированно равны. Если не равны — то хэш коды, скорее всего (но не обязательно!) не равны. А теперь — о том, что такое «равные объекты».

    Изначально равенство объектов проверяется просто: никакие два объекта не равны, объект равен самому себе и никому больше. В таком случае естественно каким-то образом построить хэш-код из адреса.Для строк это не так, и функция hashCode() переписана, чтобы учитывала данные внутри, а не адрес.

    (Почему говорю «каким-то образом» — при определённом устройстве хэш-таблицы, если брать просто адрес, часть гнёзд будут простаивать. Так что устройство хэш-функции и хэш-таблицы надо согласовать, чтобы таблица заполнялась равномерно.)
    Ответ написан
    6 комментариев
  • Почему после вызова деструктора unordered_map перестаёт работать?

    @Mercury13
    Программист на «си с крестами» и не только
    Как в анекдоте (явно времён 70-х, когда между Гондурасом и Сальвадором была Футбольная война).
    — Что-то меня беспокоит Гондурас.
    — Так не чеши его.

    Вызов деструктора каскадно вызовет деструкторы всех полей, и это приведёт к тому, что на месте u_m будет мусор вроде nullptr и висячих указателей.

    Автодеструкторы — важнейшая фишка Си++, отличающая его от других языков, и явный вызов деструктора нужен крайне редко, когда мы хотим чуть более ручное управление памятью. Вот пример из нашего проекта. Есть выделенный кусок памяти, и мы хотим уничтожить там объект и на его месте создать новый, не отдавая-выделяя память. Считаем, что объекты одинаковые, иначе вся магия пропадёт.
    wiTile->~WiTile();
    new (wiTile) WiTile(client(), icons, clazz, tileSettings(i));

    В проекте на сотни тысяч строк я нашёл 34 таких места в библиотеках, в основном RapidJson, и три места собственно в программе.

    Если хотите полностью очистить — делайте отдельную функцию clear.

    Кстати, в вашем случае явно прописанный деструктор не нужен вообще. Вызываемые автоматически деструкторы полей сделают всё, что надо, чтобы корректно уничтожить его.
    Ответ написан
    Комментировать
  • Как передать шаблонную функцию в параметры функции?

    @Mercury13
    Программист на «си с крестами» и не только
    testFuncTemplate(a, b, func<T>)
    testFuncTemplate(a, b, func<decltype(a)>)
    Ответ написан
    Комментировать
  • Qt: как привязать popup-окно к компоненту?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    616c1cffb8c40810341186.png
    Раз эти товарищи не сподобились сделать как надо — то приходится поступать для каждого случая по-особенному.
    Ответ написан
    Комментировать
  • Как можно (через Qt или WinAPI) узнать направление ClearType: RGB/BGR/другое?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    https://docs.microsoft.com/en-us/windows/win32/api...
    В Qt нет, посмотрел исходники — либо часть Chromium, либо не выглядывает наружу.
    Ответ написан
    Комментировать
  • Что такое точка доступа Wi-fi?

    @Mercury13
    Программист на «си с крестами» и не только
    Вайфай — локальная сеть.
    Точка доступа — мост (устройство 2-го уровня) между вайфаем и какой-то другой сетью — локальной или провайдерской. (То, что провайдер позволит подключиться по 2-му уровню только одному, реже двум — это другой вопрос.)
    Но точка доступа часто является маршрутизатором, устройством 3-го уровня.
    Ответ написан
    Комментировать
  • Я придумал новый гениальный способ оптимизации игр?

    @Mercury13
    Программист на «си с крестами» и не только
    Гений постольку, поскольку каким-то раком научил движок делать то, чего он не может из коробки. Но есть вопросы.
    Поскольку игра изометрическая, низшие уровни детализации неактуальны. А в обычном 3D — более чем.
    Изометрия обычно не настолько детальна, чтобы загрузка была нужна особенно часто — нужно что-то вроде «при приближении к подуровню грузить его».
    Если движок не поддерживает фоновую подгрузку — всё будет с ней в порядке, не будет игрок испытывать тормозов?
    С мультиплеером всё в порядке?
    Ответ написан
    Комментировать
  • Как вызвать CreateThread внутри Form?

    @Mercury13
    Программист на «си с крестами» и не только
    Tony сказал правильно, но есть ещё одна вещь.
    CreateThread использовать ЗАПРЕЩАЕТСЯ. Точнее, можно — но для этого нужно знать, что делаешь. А так для прикладного прогера правило — надо перевести библиотеку времени выполнения в многопоточный режим, и для этого вызвать аналог из этой самой библиотеки. В Си++ для этого используется функция beginthreadex(), в Delphi — BeginThread.
    Ответ написан
    Комментировать
  • Стоит ли плагину доверят писать unit-тесты?

    @Mercury13
    Программист на «си с крестами» и не только
    Проверим один тест.
    // Какую концепцию проверяем?
       // Это не просто проверка функции ListAll, это проверка какой-то концепции кода
       // Варианты.
       // 1. Пустой listAll() даёт пустой список.
       // 2. Непустой listAll() даёт непустой список.
        void testListAll() {
            ArrayList<Product> productList = new ArrayList<Product>();
            // Проводим поиск в списке — что в этот список вносится?
            // И не будет ли физической зависимости тестов друг от друга?
            // И для чего вообще нужен этот search, если мы listAll тестируем?
            // Что такое ProductRepository и он вообще проверен?
            when(this.productRepository.search((String) any())).thenReturn(productList);
            // Ну, хорошо.
            List<Product> actualListAllResult = this.productService.listAll("Keyword");
            // Отказ, они не same: первый мы только что создали, а второй откуда-то пришёл.
            assertSame(productList, actualListAllResult);
            // Получается, что концепция — поиск, когда ничего не находится?
            assertTrue(actualListAllResult.isEmpty());
            verify(this.productRepository).search((String) any());
            // Получается, единственная концепция, которую мы тестируем,— поиск в пустом списке даёт пустоту
            // (и та некорректная из-за assertSame).
            assertTrue(this.productService.getAll().isEmpty());
        }


    Ну что, понятно, что или фтопку такие инструменты, или нужно их серьёзно осваивать, прежде чем будут приносить хоть какие-то результаты?
    Ответ написан
    Комментировать
  • Как лучше всего использовать ресурс батареи?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Прописать переход на зарядку процентах на 80…90 (обычно ≈95).
    2. Хоть иногда давать разряд-заряд.
    Вот и всё, что мы можем сделать.
    Ответ написан
    Комментировать
  • Как разбить числа по группам так, чтобы в группах находились близкие по значению числа?

    @Mercury13
    Программист на «си с крестами» и не только
    Это называется кластеризация, и самый ходовой метод для неё — K-means.
    Ответ написан
    2 комментария
  • Можно ли сделать код, который, если constexpr-выражение true, выдаст предупреждение?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Придумал.

    [[maybe_unused]] byte debugWarning = SIMULATE_CHANGED_HORIZON * 1000;


    В случае false компиляция пройдёт гладко. В случае true преобразование 1000 → byte выдаст предупреждение.
    Ответ написан
    Комментировать
  • Код не проходит проверку по времени, как ускорить?

    @Mercury13
    Программист на «си с крестами» и не только
    Нужно использовать тип данных «словарь» (в Си++ std::map/unordered_map), а не прямой поиск.
    Если в Питоне, как в PHP, массивы могут иметь любой индекс (ну не знаю я, не знаю) — вот вам и словарь.
    Ответ написан
    Комментировать