Ответы пользователя по тегу C++
  • Как исправить ошибку при компиляции C++ кода?

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

    Виноват определённо разработчик макроса: он назвал его простым и распространённым словом Verify. Функция-то где-то внутри объекта, а макрос заменяет идентификатор Verify на всякую дрянь независимо от того, где этот идентификатор и в каком пространстве имён. Кроме того, подобные макросы принято называть как-нибудь DBG_VERIFY — если только это не макрос совместимости, подменяющий, например, более позднее nullptr в старое NULL.
    Ответ написан
    23 комментария
  • Как сделать, чтобы шаблонная функция принимала 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); }
    Ответ написан
    Комментировать
  • При наличии каких членов класса 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.
    Ответ написан
    Комментировать
  • Как преобразовать 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;
    }
    Ответ написан
  • Как правильно выделить память (с проверкой выделения) для массива класса?

    @Mercury13
    Программист на «си с крестами» и не только
    Но обычный new выкидывает std::bad_alloc.
    Так что не совсем понятно, что вы хотите сделать, когда делаете new с флагом «не кидать», а потом при неудавшемся new кидаете аварию.
    Ответ написан
    Комментировать
  • Где взять заголовок 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 комментарий
  • Почему берутся проблемы с совместимостью массивов и контейнеров?

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

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

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

    @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 комментарий
  • Почему после вызова деструктора 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)>)
    Ответ написан
    Комментировать
  • Как вызвать CreateThread внутри Form?

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

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

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


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

    @Mercury13
    Программист на «си с крестами» и не только
    не удается преобразовать 'char*' в 'int*' для аргумента '1' в 'int

    Как правило, ошибка. Функция хотела указатель на 4-байтовые слова — а мы передаём ей указатель на байты.
    Если очень нужно рассматривать int* как char* (например, работаем не с данными какого-то определённого типа, а просто с байтами в памяти) — используйте reinterpret_cast.

    недопустимое преобразование из 'int*' в 'int'

    Почти всегда ошибка. Функция хотела число — а мы ей передаём ей указатель.
    Если реально по какой-то причине нужно значение указателя воспринимать как число — используйте reinterpret_cast.

    Возможен и другой вариант — вы просто упустили операцию «разыменовать» *.

    warning: narrowing conversion of '143' from 'int' to 'char' inside

    Точно ошибка — 143 не вписывается в char (−128…127). Если очень надо, используй static_cast.

    Покажите лучше код, и я устрою ему ревизию.
    Ответ написан
    Комментировать
  • Почему нельзя возвращать объект по значению в non-const &?

    @Mercury13
    Программист на «си с крестами» и не только
    const struct s & ref = f();
    Этот механизм называется «продление жизни временного объекта».
    Можно написать вот так.
    struct s && ref = f();

    Главная причина преобразования временного объекта только в const T& и T&& — тонкие ошибки при суперпозиции функций.
    А именно — временный объект мы ни в коем случае не должны передать в функцию, принимающую s&.
    void modify(s& arg) { ++arg[1]; }
    
    ...
    
    modify(f());

    Мы вызвали функцию f(), её результат передали в modify, и… похѣрили.
    Ответ написан
    Комментировать
  • Возможно ли автоматическое добавление переменной компилятором в namespace?

    @Mercury13
    Программист на «си с крестами» и не только
    В любом языке можно только так.
    namespace Addresses {
      uintptr_t myAddresses = processManager.xxx;
    }

    Только в CPP-файле; в хедере надо
    namespace Addresses {
      extern uintptr_t myAddresses;
    }

    Но не советую по двум причинам.
    1. Контроль задержек при инициализации программы — дело противное. На всех языках. Например, на Java ME я просил: все длинные операции делать, когда на экране что-то появится.
    2. Вторая причина специфичная для Си++. Порядок инициализации разных модулей не определён. Если менеджер в одном модуле, а адреса в другом, адреса могут с вероятностью 50/50 инициализироваться, когда менеджера ещё нет.
    Ответ написан
    6 комментариев