Задать вопрос
  • 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. Если, как оказалось, вы у этого разработчика работаете, вы гарантированно полетите с работы с волчьим билетом, да и в трудовом договоре может найтись крючок.
    Ответ написан
    Комментировать
  • Как преобразовать std::string в LPCSTR?

    @Mercury13
    Программист на «си с крестами» и не только
    std::string dir = "subdir";
    CreateDirectoryA(dir.c_str(), nullptr);
    Ответ написан
    Комментировать
  • Как создать новый экземпляр класса переменной?

    @Mercury13
    Программист на «си с крестами» и не только
    1. В Си++ нет такого понятия, как виртуальный конструктор. А виртуальный конструктор копирования — я вообще не понимаю…
    2. В makecopy вы допустили ошибку — переменная никуда не девается и тупо занимает память.
    3. Чтобы отдать указатель на переменную и сказать: «сам уничтожай, когда хочешь»,— есть стандартный тип unique_ptr. Но для ковариантности по unique_ptr нужны две функции: одна виртуальная и возвращает простой указатель, другая отдаёт конкретный u_p.

    Для простоты предполагаю, что все классы неабстрактные, и виртуальная vclone имеет права доступа public, а не protected.
    #include <iostream>
    #include <memory>
    
    #define IMPLEMENT_CLONE_1(Class, Keyword) \
        virtual Class* vclone() const Keyword { return new Class(*this); }  \
        auto clone() const { return std::unique_ptr<Class>{vclone()}; }
    
    #define IMPLEMENT_CLONE(Class)  IMPLEMENT_CLONE_1(Class, override)
    
    class BaseClass {
    public:
      IMPLEMENT_CLONE_1(BaseClass, )
      virtual ~BaseClass() = default;
    };
    
    class MyClass : public BaseClass {
    public:
      IMPLEMENT_CLONE(MyClass)
      void print() const { std::cout << "I'm a MyClass!" << std::endl; }
    };
    
    int main()
    {
      auto a = std::make_unique<MyClass>();
      auto b = a->clone();
      b->print();
      return 0;
    }


    Есть ещё вот такая хрѣнь. Я уж не знаю, что лучше: сóрок пя́ток или пятóк сорóк прямое преобразование указателей или препроцессор.

    #include <iostream>
    #include <memory>
    
    class BaseClass {
    public:
        auto clone() const { return std::unique_ptr<BaseClass>{ vclone() }; }
    protected:
        virtual BaseClass* vclone() const { return new BaseClass(*this); }
    };
    
    template <class Super, class This>
    class CloneImpl : public Super
    {
    public:
        auto clone() const { return std::unique_ptr<This>{ static_cast<This*>(vclone()) }; }
    protected:
        CloneImpl* vclone() const override { return new This(*static_cast<const This*>(this)); }
    };
    
    class MyClass : public CloneImpl<BaseClass, MyClass> {
    public:
      void print() const { std::cout << "I'm a MyClass!" << std::endl; }
    };
    
    int main()
    {
        auto a = std::make_unique<MyClass>();
        auto b = a->clone();
        b->print();
        return 0;
    }
    Ответ написан
  • Почему у меня %c выводит а %s выбивает ошибку?

    @Mercury13
    Программист на «си с крестами» и не только
    char x = 'FIO';
    Ошибка.
    Многосимвольные константы определяются реализацией, используются для жёсткой оптимизации, и в Visual C++ крайне ненадёжны. В GCC вроде бэ-мэ, но в любом случае преобразование в char 'FIO', которое должно занимать три байта, даст 'F' или 'O'.
    Вам нужна не символьная константа, а строковая:
    char x[] = "FIO";
    И, соответственно, %s.

    А как действует printf и любая другая функция с переменным числом аргументов? Эти аргументы сваливаются навалом в область памяти, которая называется «стек вызовов», и printf начинает этот навал разбирать. Чтобы сдвинуло указатель на один байт и сказало: это char — и используется формат %c. Разумеется, неверный формат приведёт к неверной интерпретации типа данных и отказу на одной или всех платформах.
    %c — в памяти лежит один байт, интерпретировать его как один символ
    %s — в памяти лежит указатель на строку, заканчивающуюся нулём, 4/8 байтов. (Все массивы в исходном Си куда бы то ни было передаются как указатели.)
    Ответ написан
    3 комментария
  • Как правильно выделить память (с проверкой выделения) для массива класса?

    @Mercury13
    Программист на «си с крестами» и не только
    Но обычный new выкидывает std::bad_alloc.
    Так что не совсем понятно, что вы хотите сделать, когда делаете new с флагом «не кидать», а потом при неудавшемся new кидаете аварию.
    Ответ написан
    Комментировать
  • На каком цифровом элементе системное время строится и как функционирует?

    @Mercury13
    Программист на «си с крестами» и не только
    Собственно есть три основных источника времени в компьютере.
    1. Часы реального времени. Как говорит название, содержит батарейку и поддерживает системное время даже при выключенном компе. Точность традиционно 1 секунда. Более точное время можно получить, комбинируя ЧРВ и программируемый таймер.
    2. Программируемый таймер — традиционно 18,2 тика в секунду, но может вроде бы до 1000. С каждым тиком вызывает прерывание. В многозадачных ОС типа Windows и Linux программируемым таймером мы управлять не можем, ведь он используется планировщиком задач, но вокруг него написан неплохой API.
    3. Счётчик тактов процессора — для управления мультимедиа и играми с переменной кадровой частотой. Это самый точный (по разрешению) доступный источник времени, но и самый сложный.
    Ответ написан
    Комментировать
  • Где взять заголовок graphics.h?

    @Mercury13
    Программист на «си с крестами» и не только
    graphics.h — это, очевидно, хедер Borland C++. Его просто нет в многозадачных ОС, где механизмы рисования на формах другие и вообще нет кроссплатформенного графического фреймворка.

    Если нужна просто графика, лучше осваивать SDL. Если графический интерфейс — Qt.

    Если нужно быстро портануть прогу с Borland C++ на что-то более новое —
    https://home.cs.colorado.edu/~main/cs1300-old/cs13...
    openbgi.sourceforge.net
    https://github.com/genpfault/sdl-bgi
    Ответ написан
  • Как имитировать зажатие кнопки на клавиатуре c++?

    @Mercury13
    Программист на «си с крестами» и не только
    Что вы хотите? Если нужен автоповтор кнопки, вам придётся реализовать его вручную.
    Ответ написан
  • Comparison between pointer and integer. Как решить?

    @Mercury13
    Программист на «си с крестами» и не только
    Несколько вариантов.
    1. pnumber превратить в string
    2. strncmp(s[0].pnumber, std::size(s[0].pnumber), "2") == 0
    3. std::string_view(s[0].pnumber) == "2"
    Прости, глючу, и там. и там нужны двойные кавыки
    Ответ написан
    2 комментария
  • 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’шнике не занимает и заполняется на старте. Таким образом мы может легко пробежаться по всем символам, и можем по коду символа найти его запись.

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

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

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

    И второе: по указателю мы можем получить доступ к объекту, чьего имени мы не знаем (как у меня в таблице символов) или он безымянный (если он выделен динамически, при работе программы, а не вкомпилирован в неё).
    Ответ написан
  • Как сделать такой финт ушами с 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 комментариев