Задать вопрос
  • AUX и Jack 3,5 - это одно и то же?

    @Mercury13
    Программист на «си с крестами» и не только
    AUX ≠ Jack 3,5 ≠ наушники.
    1. AUX (неусиленный линейный выход) может быть и другими разъёмами — чаще всего DIN-5 или два RCA. Допустим, в моём самодельном переключателе колонки-наушники на колонки идут два RCA из-за сверхнадёжности, а на наушники — ну, так и быть, джек.
    2. На миниджеке 3,5 мм может быть и микрофон. У микрофонов отдельный механизм работы, и ухи от микрофона отличают омметром — наушник имеет сопротивление около 30 Ом, а микрофон — около 1к. И даже ухи с микрофоном, если джек 4-контактный.
    3. В советской аппаратуре существуют разъёмы для высокоомных наушников, и низкоомные могут плохо работать. (Сомневаюсь, что погорят — для проверки полярности часто берут и подключают к ним батарейку, хотя на AUX меньше вольта.) Впрочем, не видел ни одного устройства, где этот разъём — джек.

    Но в целом разъёмы для современных низкоомных наушников и внешней аудиоаппаратуры электрически сходны, подключай свободно. Могут быть проблемы с громкостью и/или помехами (как у меня в машине — если заряжать смартфон и выводить звук с него на магнитофон, будут помехи, зависящие от оборотов двигателя), но работать будет.
    Ответ написан
    2 комментария
  • Где использовать const?

    @Mercury13
    Программист на «си с крестами» и не только
    Это стандартный студенческий код. Можно маленькую ревизию?
    1. Класс хранит строки в виде char*, ссылаясь на чужую память, и ничего хорошего не сделано с этой памятью. Подобные ссылки на чужую память хороши как оптимизация, когда практически всегда передаём строковые литералы (например, названия тэгов XML). А так стандартный способ хранить строку — std::string. Ну или char[], если не учили этого.
    2. Продолжение 1. С одной стороны, setMarka (например) имеет дело с внешней памятью, которой объект не владеет и которую не надо освобождать. С другой — Input передаёт во владение объекту буфер памяти, который освобождать НАДО.
    3. С возвратом true/false — он хорошо сказал. Сам класс должен решать, корректны ли данные.
    4. Деструктор — если std::string разрешён, лучше возьми его вместо const char* и забей на деструктор. Если запрещён — либо используй char[], деструктор не нужен. Либо сделай свой string, способный только управлять памятью неизменяемой строки, и деструктор нужен только ему. В промышленном программировании деструкторы редки: либо это реально какая-то необычная структура данных, либо просто компилятор заглючил и нужно хоть пустое, но тело в CPP-файле. Либо идиома pimpl (pointer to implementation, указатель на реализацию), призванная снизить количество каскадных include’ов — а значит, укоротить неполную сборку.
    5. char* car_name = "Toyota"; — работает только на очень старых компиляторах. Во всех известных мне строковый литерал имеет тип const char*.
    6. Input() должен быть чем-то внешним по отношению к машине: объект «машина» предназначен для хранения данных, и нечего притягивать к нему консоль, которой в программе может и не быть (например, она GUI).
    Ответ написан
  • На каком ЯП стоит писать программу для управления компьютерным клубом (десктоп)?

    @Mercury13
    Программист на «си с крестами» и не только
    Главное — сделать клиент, который сложно будет хакнуть и/или выбить. И тут что угодно, компилирующее в машинный код или что-то близкое: Delphi, C++, .NET. И придётся поспрашивать у опытных, как правильно защитить.

    Рабочее место админа — что угодно, хоть сайт в браузере.

    Сервер — машина, которая суткам работает в уголке и к которой даже админ не имеет доступа. Что угодно, хоть PHP.
    Ответ написан
    5 комментариев
  • Для чего нужна двойная ссылка &&?

    @Mercury13
    Программист на «си с крестами» и не только
    Ссылка на временный объект.

    Прямо (int&& d = 5) используется крайне редко, чтобы продлить время жизни временному объекту.

    Куда чаще оно используется в типах возврата (string&& Container::give()), и означает: это ссылка на объект, который спокойно можно выпотрошить. Или в типах параметров — Container& Container::operator = (string&& x): то есть принимает объект, который больше не нужен.

    Для чего потрошить объекты? Вот представьте себе код.
    std::vector<int> doSmth() {
      std::vector<int> r;
      r.push_back(42);
      return r;
    }
    ...
    std::vector<int> a = doSmth();


    Забьём на необязательные оптимизации — как эта штука будет действовать по семантике Крестов?
    std::vector<int> r; — создадим в стековом фрейме переменную r.
    return r; — vector(const vector&) в какое-то место, предложенное вызывающей функцией.
    std::vector<int> a = doSmth(); — vector(const vector&) из этого места в a.

    Си++17 ТРЕБУЕТ, чтобы подобный возврат гарантированно вызывал ОДИН конструктор копирования (а если конструировать на месте вроде return vector<int>{ 42 }; — то ни одного), но у нас-то Си++03. Не много конструкторов? А ведь зная, что объект больше не нужен, можем просто перенести указатель из одного вектора в другой, точка — даже впрямую, без оптимизаций, никаких перевыделений памяти. Вот этот прямой перенос указателей я и назвал словом «выпотрошить объект».

    Пример 1: container = string("alpha") + "bravo"; Тут в контейнер перемещается временная строка.
    Пример 2: string a = "alpha"; container = a; Не компилируется: строка a не временная.
    Пример 3. container1 = container2.give(); Перенос строки из контейнера в контейнер без выделения памяти.
    Пример 4: string a = "alpha"; container = std::move(a); Компилируется (мы функцией move сказали: с обычным объектом обращаться как с временным), но в результате в строке a может быть что угодно и не стоит закладываться на дальнейшее её значение. Но объект после потрошения в любом случае должен быть корректным, ведь ему ещё вызывать деструктор :)

    Последний вопрос: кто потрошит? Функция, которая принимает временную ссылку как параметр. В данном случае Container::op=. Чем потрошит? — ну, например, кодом myString=std::move(x) или std::swap(myString, x). А они, в свою очередь — внутренняя string::op= и дружественная swap — прямыми играми с указателями.
    Ответ написан
  • Как поставляются игры на компьютеры?

    @Mercury13
    Программист на «си с крестами» и не только
    Касательно читов. Я простым анализом памяти смог узнать в Microsoft Train Simulator состав поезда игрока, его скорость, положение контроллеров, пройденный путь, давление в различных элементах пневматического тормоза (оказывается, в PSI), боксует ли он. Жаль, я не смог обнаружить привязку всего этого к путям.

    Как я это делал. Через ArtMoney получал некие базовые адреса. Если адрес статический — ну, всё в порядке. Если нет — писал утилиту, в которую изначально вводился найденный адрес. По цифрам я прикидывал, где мог начинаться объект, и снова поиск через ArtMoney… Ну и так далее, пока не дойду до статического адреса. Вот так оно у меня выглядело.

    TmstsLocalTrain = packed record
          _mem0000 : array [$0000..$0061] of byte;
          // 0062
          HeadWagon : dword;   // Головная единица ПС
          // 0066
          TailWagon : dword;   // Хвостовая единица ПС
          // 006A
          LocWagon : dword;    // Управляемая игроком единица ПС
          // 006E
          _mem006E : dword;
          // 0072
          Caps : dword;
          // 0076
          _mem0076 : array [$0076..$0091] of byte;
          // 0092
          Speed : single;       // Скорость по скоростемеру, м/с
          // 0096
          Acceleration : single;  // Ускорение, м/с2
          // 009A
          _mem009A : array [$009A..$00D5] of byte;
          // 00D6
          TimeSec : single;
          // 00DA
          ReversingOdometer : single;
          // 00DE
        end;


    ПС = подвижной состав. Одометр реверсивный, потому что при осаживании (заднем ходе) считает назад. Байтовый массив _mem0000 — это память, которую я не смог опознать. HeadWagon, TailWagon, LocWagon, Caps — на самом деле указатели, но поскольку они не имеют смысла в адресном пространстве лентописателя (задачей было сделать аналог скоростемерной ленты), они Dword, а не указатели.

    Вспоминал, что значат сокращения П, ПТЭ и прочее. Оказалось: паровоз, тепловоз, электровоз. Естественно, регулятор пара есть только у паровозов.
    Ответ написан
    Комментировать
  • Как отличить векторный графический редактор от растрового?

    @Mercury13
    Программист на «си с крестами» и не только
    Векторный
    Растровый с векторными элементами (как и Шоп)
    Растровый
    Растровый
    Векторный

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

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

    @Mercury13
    Программист на «си с крестами» и не только
    1. Для каждого цвета определить положение [0,1] в цветовом континууме. Простейший вариант — вычленить наиболее значимый канал (например, зелёный) → ограничить «крайними» значениями → (возможно) преобразовать в линейное цветовое пространство (сначала [0,255] в [0,1], затем x1/2,2) → провести линейное преобразование.
    2. Провести нелинейное преобразование этого [0,1] → [0,1], если надо.
    3. Преобразовать этот [0,1] в другой континуум. Если работали в линейном цветовом пространстве — преобразовать снова в sRGB, если надо.
    Ответ написан
    Комментировать
  • Можете решать эту задачу?

    @Mercury13
    Программист на «си с крестами» и не только
    Изначально:
    1. У корня глубина 0.

    Команда ADD:
    1. вершина.предок = предок
    2. вершина.глубина = предок.глубина + 1

    Команда GET:
    1. Найти ту вершину, что глубже. Если у одной глубина 5, у другой 7 — подняться от второй на 2.
    2. А теперь параллельно и от первой, и от второй вершины поднимаемся вверх на единицу, пока не придём в одну и ту же вершину.

    Если хватит памяти — можно устроить какой-то кэш. Мой вариант — кэшировать предков порядка 1,2,4,8…
    Чтобы подняться от вершины на 30 единиц…
    1. Находим ближайшую степень двойки (16).
    2. Если эта степень есть в кэше — просто берём её.
    3. Иначе находим максимальную имеющуюся степень (например, 4). Заходим в эту вершину и рекурсивно поднимаемся от неё на 4, потом на 8, заполняя наш кэш.
    4. На оставшиеся 14 поднимаемся по тому же алгоритму.

    Чтобы найти общего предка двух вершин — у одной глубина 30, у другой 40.
    1. От той вершины, у которой 40, поднимаемся на 10 единиц. Алгоритм я привёл.
    2. Одинаковые? — тогда понятно.
    3. Имеют общего предка? — тогда понятно.
    4. Иначе поднимаемся на 1, 2, 4, 8, 16 единиц от каждой из них по указанному алгоритму, каждый из этих шагов при наличии кэша даст O(1). Находим последнюю НЕСОВПАДАЮЩУЮ глубину. Для них повторяем алгоритм.

    ВАРИАНТ КЭША ВТОРОЙ. Для вершины глубины 27=11010 кэшировать предков АБСОЛЮТНЫХ (то есть от корня!) глубин 16=1.0000, 24=11.000, 24 :) =110.00, 26=1101.0. Тогда при создании вершины весь кэш достаточно быстро строится на основе кэша вершины-предка.

    Чтобы подняться от вершины глубины 27 на расстояние, например, 5, нам нужна абсолютная глубина 27−5=22.
    Есть только 24 → действуем так. Выныриваем туда на 24, но там кэш совершенно негодный → а дальше нас проведёт кэш глубины 23. Так поднимаемся на 1 до 23-й и повторяем алгоритм.

    Чтобы найти общего предка двух вершин (их глубины, скажем, 27 и 42), точно так же уравниваем глубины. Затем поднимаемся до глубин 16, 24, 24, 26…, пока не найдём несовпадение. Идём в эти несовпадающие вершины, проходим от них к предку и повторяем алгоритм.
    Ответ написан
  • Какова будет грамматика для данного языка?

    @Mercury13
    Программист на «си с крестами» и не только
    L → EZUU M ZUUUUF
    M → ZUU M ZUUUU | S
    Для S грамматику вы уже придумали.
    Затем — не буду расписывать, они многословны, но просты — UZ → ZU

    А чтобы превратить Z в 0 и U в единицу, если i,n ∊ N+…
    Сделаем затравку…
    EZ → E0
    Ua → 1a
    cZ → c0
    UF → 1F
    …Уничтожим технические нетерминалы…
    E0 → 0
    1F → 1
    …И проведём волну!
    0Z → 00
    U1 → 11

    Вроде так.
    (Z = zero, U = unit)
    Ответ написан
    Комментировать
  • Как в языке си вернуть пустой массив?

    @Mercury13
    Программист на «си с крестами» и не только
    Если возвращаем НОВУЮ память:
    1. вернуть массив заведомо бóльшей длины и поле длины.
    struct Arr1 {
      int length;
      int data[100];
    }
    struct Arr1 someFunc() {}

    2. Вернуть динамический массив.
    struct Arr2 {
      int length;
      int* data;   // не забудь free(arr.data);
    }
    struct Arr2 someFunc() {}


    Если работаем в ИМЕЮЩЕЙСЯ памяти.
    3. Тот же Arr2, но разница в том, что data не надо высвобождать.
    4. А если arr.data гарантированно понятное — то можно вернуть только int, поле длины (как в функции sprintf).
    int sprintf ( char * str, const char * format, ... );

    Она и возвращает массив — только его адрес гарантированно будет str и вернуть надо только длину.
    Ответ написан
    Комментировать
  • Как понять каких пакетов не хватает для корректной работы Qt программы?

    @Mercury13
    Программист на «си с крестами» и не только
    sqldrivers\qsqlmysql.dll (именно так, в подкаталоге sqldrivers!!)
    Qt5/6Sql.dll

    А дальше залезьте Dependency Walker’ом в драйвер qsqlmysql.dll и посмотрите, чего не хватает.
    Одни сборки требуют официальный коннектор MySQL, другие от MariaDB.
    Дополнительные DLL’ки должны лежать в каталоге с exe.

    Я отказался от Qt MySQL именно потому, что при выпуске легко накосячить — а поскольку MySQL не основная фича, это очень не скоро заметят. Написал собственную горбушку, к тому же коннектящуюся к MySQL8.
    Ответ написан
    2 комментария
  • Почему #pragma comment (lib) не обрабатывается?

    @Mercury13
    Программист на «си с крестами» и не только
    Судя по украшению __imp и расширеню *.o, вы прагму для VS применяете на MinGW. Поскольку я не знаю, на какой вы системе программирования, заходите в настройки проекта и подключаете билиотеку ws2_32.
    Ответ написан
    Комментировать
  • Где лучше хранить массив объектов? json или бд?

    @Mercury13
    Программист на «си с крестами» и не только
    Хранение лучше в БД. Любой, хоть SqLite, хоть MyISAM, нет у вас каких-то причин искать СамуюМоднуюСУБД®.

    Как промежуточный механизм передачи данных можно и JSON, если так хотите. Всё зависит от ваших навыков веб-разработчика. Поскольку мои веб-знания устарели лет этак на пятнадцать, я бы сначала сделал чистый PHP + не-AJAX, а потом думал бы.
    Ответ написан
    Комментировать
  • Как исправить ошибку при компиляции C++ кода?

    @Mercury13
    Программист на «си с крестами» и не только
    Тут поцапались макрос Verify в dbg.h и функция Verify в cryptlib.h.
    Я бы предпочёл переименовать одно из двух — лучше макрос — а затем разобрать, где должен быть макрос, а где функция.

    Виноват определённо разработчик макроса: он назвал его простым и распространённым словом Verify. Функция-то где-то внутри объекта, а макрос заменяет идентификатор Verify на всякую дрянь независимо от того, где этот идентификатор и в каком пространстве имён. Кроме того, подобные макросы принято называть как-нибудь DBG_VERIFY — если только это не макрос совместимости, подменяющий, например, более позднее nullptr в старое NULL.
    Ответ написан
    23 комментария
  • Как использовать RATE_VERY_FAST (Java, Android)?

    @Mercury13
    Программист на «си с крестами» и не только
    Давайте посмотрим, что представляет собой RATE_XXX и SENSOR_DELAY_XXX.
    SENSOR_DELAY_FASTEST = 0
    SENSOR_DELAY_GAME = 1
    SENSOR_DELAY_UI = 2
    SENSOR_DELAY_NORMAL = 3

    RATE_STOP = 0
    RATE_NORMAL = 1
    RATE_FAST = 2
    RATE_VERY_FAST = 3

    Функция registerListener требует задержку в мкс, или одну из четырёх констант SENSOR_DELAY. Поставив RATE_VERY_FAST=3, вы реально поставили ему SENSOR_DELAY_NORMAL=3!

    А куда же совать RATE? В SensorDirectChannel.configure! А сам SensorDirectChannel можно получить через SensorManager.createDirectChannel! Вот как-то так — и даже понимаю, откуда такая архитектура. Ну не может система, оптимизированная под низкое энергопотребление, 1700 раз в секунду пинать пользовательскую программу — программа получает управление намного реже, а информация с акселерометра накапливается в буфере памяти.

    UPD. Есть и вторая причина. Обработка может затянуться более чем на 1/1700 секунды, и в это время датчик продолжит писать информацию в тот самый буфер.

    UPD2. Другими словами, через callback и через буфер в памяти — две разных архитектуры. Первая обеспечит низкую задержку. Вторая — высокую (причём стабильно высокую!) частоту опроса, но задержка от регистрации до обработки, скорее всего, будет побольше.
    Ответ написан
    2 комментария
  • QAbstractItemModel::beginInsertRows: что происходит, если вставка не удалась?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Я пока придумал такую конструкцию…
    class Committer {  // interface
    public:
      virtual void commit() = 0;
      virtual ~Committer() = default;
    }
    
    enum class ErrCode { OK, UNCLONEABLE, BAD_RECIPIENT };
    
    struct CommitObj {
      ErrCode errCode;
      std::unique_ptr<Committer> action;
    }
    
    class UiObj {
    public:
      virtual CommitObj startClone(std::shared_ptr<UiObj> recipient) = 0;
    }


    Мы или получаем объект, для которого а защищённом блоке достаточно дать commit(), или причину, почему клонирование невозможно.
    Ответ написан
    Комментировать
  • Как сделать, чтобы шаблонная функция принимала const char*?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    template <class T> struct string_traits;
    
        template <class C, class T, class A>
        struct string_traits<std::basic_string<C,T,A>> {
            using Ch = C;
            using Tr = T;
            using Sv = std::basic_string_view<C,T>;
        };
    
        template <class C, class T>
        struct string_traits<std::basic_string_view<C,T>> {
            using Ch = C;
            using Tr = T;
            using Sv = std::basic_string_view<C,T>;
        };
    
        template <class C>
        struct string_traits<const C*> {
            using Ch = C;
            using Tr = std::char_traits<C>;
            using Sv = std::basic_string_view<C>;
        };
    
        template <class C, size_t N>
        struct string_traits<C[N]> {
            using Ch = C;
            using Sv = std::basic_string_view<C>;
        };
    
        template<class S>
        using s_v_t = typename string_traits<std::remove_cvref_t<S>>::Sv;
    
        namespace detail {
            template <class Sv>
            Sv remainderSv(Sv s, Sv prefix)
            {
                if (s.length() <= prefix.length()
                        || !s.starts_with(prefix))
                    return {};
                return s.substr(prefix.length(), s.length() - prefix.length());
            }
        }
    
        /// @brief remainderSv
        ///    Same for prefix only
        template <class A>
        inline auto remainderSv(const A& s, s_v_t<A> prefix) -> s_v_t<A>
            { return detail::remainderSv<s_v_t<A>>(s, prefix); }
    Ответ написан
    Комментировать
  • Ковариантность возвращаемых типов в Javа, я правильно понял суть?

    @Mercury13
    Программист на «си с крестами» и не только
    Ковариантность начинается, когда мы делаем class BuildCircle extends BuildShape.
    (Лучше BuildShape оформить как интерфейс, а не как класс, но шут с ним.)

    Ковариантность связана с принципом подстановки Лисков: чтобы потомок вписывался в контракт, установленный предком, он может ужесточать требования к себе (скажем, выдавать более узкий тип) и ослаблять требования к другим (скажем, принимать поток реального времени вроде консоли/сокета — а не только файл, который знает себе длину и позволяет перемотку).

    Вот это «выдавать более узкий тип, чем полагает предок» — и есть ковариантность.

    Обратное — принимать поток реального времени, а не только файл — называется контравариантность. Насколько мне известно, в Java на уровне языка её нет, но какие-то части ухитряются делать через шаблоны.
    Ответ написан
    3 комментария
  • При наличии каких членов класса move-конструктор не будет сгенерирован автоматически?

    @Mercury13
    Программист на «си с крестами» и не только
    Implicitly-declared move constructor

    If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
    • there are no user-declared copy constructors;
    • there are no user-declared copy assignment operators;
    • there are no user-declared move assignment operators;
    • there is no user-declared destructor.

    then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).

    Ну и пользовательский move-конструктор исключает автоматический.
    Так что 2, 3, 4, 5, 6.
    Ответ написан
    Комментировать
  • Защищаются ли алгоритмы авторским правом?

    @Mercury13
    Программист на «си с крестами» и не только
    Тут два камня преткновения.
    1. В некоторых юрисдикциях через большую задницу, но можно защитить алгоритм патентом. Если алгоритм известен уже 20 лет или несколько фирм им вовсю пользуются — забивайте.
    2. Если, как оказалось, вы у этого разработчика работаете, вы гарантированно полетите с работы с волчьим билетом, да и в трудовом договоре может найтись крючок.
    Ответ написан
    Комментировать