• Что нужно знать, чтобы написать web server на C++?

    @res2001
    Developer, ex-admin
    Полноценный веб сервер - это очень сложное ПО. Сам по себе протокол HTTP оброс кучей дополнений и улучшений, только посмотрите список RFC, его описывающих. В одиночку написать с нуля что-то подобное, например, apache - практически не посильное дело.
    Кроме HTTP, конечно, нужно хорошо знать и понимать сетевое программирование, параллельное программирование, английский язык, что-то еще.

    Но есть библиотеки, в которых веб сервер в каком-то виде с теми или иными ограничениями уже реализован. Можно использовать их. В этом случае задача сильно упрощается.
    Как пример: libonion, haywire, но это больше для Си. Для плюсов есть другие варианты.
    Ответ написан
    Комментировать
  • Как составлять header файл для статической библиотеки?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    А зачем это делать? Чем вас 8 хедеров не устраивают? Если уж так хочется сэкономить строчки в коде пользователей библиотеки, то можно воспользоваться рекурсивностью препроцессора и сделать хедер с 8 инклудами. Копипастить код точно не надо.
    Ответ написан
    1 комментарий
  • Как исправить ошибку "Нарушение прав доступа при записи по адресу"?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Эта ошибка означает, что вы пишите в какую-то память, которая вам не принадлежит.

    Или у вас там неинициализированный указатель, или выход за границы массива, или что-то подобное. Надо присмотреться ко всем указателям в программе.

    Можно во время отладки нажать на "продолжить" и тогда дебаггер остановится именно на той инструкции, которая вызвала ошибку. Дальше уже можно смотреть, в какую переменную вы там пишите и откуда она взялась.

    Падает оно потому, что нельзя string читать и писать в файл вот так, просто интенрпретируя память объекта как char*. Потому что string содержит в себе указатели на динамически выделенную память.

    Поэтому, когда вы его (в составе AutoBase) пишите в файл а потом читаете, вы получаете указатель на адрес, который был жив вместе со старым экземпляром класса. Однако, после удаления этого старого экземпляра, этот адрес уже вам не принадлежит.

    Вы можете так писать в файл только структуры из простых типов, и то не всегда (там всякое выравнивание может сыграть с вами злую шутку). Надо писать собственные методы сериализации и десериализации. Строку можно сохранять, например, как длину и потом столько байт, сколько нужно. Тогда при считывании вы сначала чистаете сколько-то байт размера, а потом нужное количество байт в саму строку.
    Ответ написан
    1 комментарий
  • Функция с параметром указателем на другую функцию. Как сделать правильно чтобы не выдавало ошибку?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    analiz(func(n));

    Вот тут вы вызываете функцию func и передаете результат ее работы в analiz. Хотя analiz хочет получать функцию. Надо делать так: analiz(&func);
    Ответ написан
    4 комментария
  • Почему транслятор ожидает указатель в моем коде?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Параметр функции int arr[],

    Но далее у вас arr[i][j]. Компилятор пытается взять индекс j, но перед ним же уже число. Вот он вам и говорит "начальника, индекс я у массива (указателя) взять могу, а тут число, непонятно".

    Вы обращаетесь с одномерным массивом, как будто бы он двумерный.
    Ответ написан
    Комментировать
  • Ошибка при создании статической сборки Qt 5.14.2. Что делать?

    Adamos
    @Adamos
    Много видеороликов посмотрел

    Нужно больше роликов!
    Пока даже задать вопрос так, чтобы на него можно было дать ответ, не получилось.
    Ответ написан
    7 комментариев
  • Можно ли использовать TCP протокол для игрового сервера?

    @rPman
    Во первых, собирай статистику по фреймам, смотри на графиках, почти наверняка это будут периодические пики с превышением времени на доставку данных

    Между фреймами не должна стоять фиксированная пауза, правильный подход - засекаешь время, отправляешь запрос по сети, обрабатываешь его и затем вычисляешь, сколько времени осталось ждать

    Твой подход - это как игра на сервере с помощью удаленного управления, сервер просчитывает все состояние мира а на клиенты приходит полное состояние мира на момент времени (а для оптимизации только та часть что нужна для отображения). Лаги в этом случае наиболее заметные и сложны к исключениями

    Необходимо в логику обработки игры добавить поведение в случае если время на доставку пакета превысило допустимый лимит (твои 200мс) и вот тут окажется что правильная реализация когда клиент додумывает поведение (например персонаж продолжает движение с предыдущего кадра) а когда реальная информация все же доходит, состояние мира (точнее его части - залагавшего объекта и остальных связанных с ним) откатывается назад до верного состояния. Для игрока это выглядит как расколбас, персонажа откидывает назад.

    Просто логически подумав ты поймешь что синхронизировать весь мир оказывается порочная и неудобная практика, высокий сетевой трафик (особенно на сервере как квадрат от количества игроков) уже сам по себе становится причиной лагов и можешь прийти к следующей логике:
    * все клиенты дублируют функционал сервера в расчете и валидации поведения объектов (исключение, критичные для геймплея результаты, основанные на 'рандоме', это оставить придется за сервером)
    * сервер должен выполнять роль валидатора, арбитра, у которого клиенты спрашивают - все ли участники правильно себя ведут, а по факту и логике - сервер будет еще один клиент, с функцией наведения связей (коннекта) между клиентами
    * клиентам и серверу достаточно рассылать информацию о действиях, ведущих к изменениям в мире, нет нужды рассылать координаты на каждый фрейм, если достаточно слать нажатые кнопки (точнее команды к действия - начало движения, остановка, начало поворота, конец поворота, выстрел и т.п.)
    Есть действия - критичные к оперативности, от которых зависит корректность расчета состояния мира, а есть те что отвечают только за визуальную составляющую, например поворот головы персонажа, которую например он делает при движении мыши, - визуализация, она может опоздать и быть не точной, а вот в момент выстрела необходимо отправить точную и оперативную информацию об угле поворота
    * клиенты должны додумывать действия друг друга, какой-никакой алгоритм должен быть, в простом виде - продолжить предыдущее движение, а лучше определить вручную или собирать статистику по каждому типу событий и задать свою логику (например поворот - продолажется, а вот выстрел повторным не производится)
    * само собой функционал синхронизации состояния мира никуда не девается, как минимум для первоначального наполнения клиентов, для валидации и периодического восстановления состояния из-а ошибок в сетевом протоколе

    В этой схеме информация о действиях клиентов может быть разослана напрямую, когда клиенты подключаются друг к другу напрямую, если это возможно, к сожалению мало какие игры это реализуют и действуют методом грубой силы, и даже физически рядом сидящие игроки будут получать состояние только через далеко расположенный сервер.

    При указанной реализации становится уже не так важно, используется ли tcp или udp, точнее алгоритм должен допускать что данные могут запаздывать, приходить не вовремя или вообще не приходить, а это значит udp - становится идеальным вариантом.

    Но главное, сетевой трафик в такой схеме значительно снижается, ведь рассылаются буквально только нажимаемые клавиши и изредка хеш-суммы состояния мира для валидации их корректности (все клиенты и сервер по завершению каждого фрейма должны сравнить состояния друг друга например путем вычисления хеш суммы по отсортированным объектам, всем или по какому то критерию, например попадающим в ячейку матрицы по координатам, если там происходили изменения)
    Ответ написан
    Комментировать
  • Какая книга по С++ мне лучше подойдёт?

    @res2001
    Developer, ex-admin
    Вам нужна книга, которую прочитаешь и сразу в "сеньоры" можно идти?
    Боюсь такой нет. Только практика и еще раз практика.

    Из продвинутых книг могу посоветовать:
    1. Эффективный и современный С++. 42 рекомендации по использованию C11 и C14
    в ней очень толково описаны нововведения в стандартах уже не совсем свежих, но эти нововведения чуть ли не самое главное изменение в языке до сих пор.
    2. Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
    3. Оптимизация программ на C++. Проверенные методы повышения производительности.
    Ответ написан
    2 комментария
  • Существует ли тенденция избегать size_t, если это возможно?

    gbg
    @gbg Куратор тега C++
    Любые ответы на любые вопросы
    Если есть риск присванивания знакового значения в беззнаковое, компилятор выдаст WARNING

    Гораздо опаснее пихать всюду int, особенно в качестве индексов массивов и размеров. Потому что в какой-то момент кто-то захочет посчитать больше 2^31 штук объектов.

    Выдержка из материала по ссылке:
    большое количество диагностических сообщений, выдаваемых анализатором Viva64, связанны именно с рекомендацией использования memsize-типов. Использование memsize типов (таких как size_t, ptrdiff_t, INT_PTR) в 64-битных программах, вместо 32-битных типов (int, unsigned) позволяет:

    Построить компилятору более простой и, следовательно, более быстрый код, в котором будут отсутствовать лишние преобразования 32-битных и 64-битных данных. Особенно это полезно при работе с адресной арифметикой и индексации массивов.
    Избежать ряда ошибок при обработке большого объема входных данных, когда количество обрабатываемых элементов превышает количество UINT_MAX.
    Избежать ряда других, более специфичных ошибок.
    Сделать код более переносимым между 64-битными Windows и Linux системами, в которых используются различные модели данных. Так, например, в Linux системах для индексации больших массивов можно использовать тип unsigned long, а в Windows нет.
    Ответ написан
    7 комментариев
  • Не компилируется код. Как исправить ошибку?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Во-первых, вы что-то путаете. Это не при компиляции происходит, а при запуске программы. Во-вторых, вы можете открыть окно "стек вызова" справа внизу, и посмотреть, какой код в вашей программе вызвал это исключение. Вы окажетесь на конкретной строчке, где вы обращаетесь к несуществующей памяти. Это скорее всего будет обращение к массиву у вас. После этого вам остается лишь немного подумать - а почему же идет обращение к не существуюущей памяти в этой строчке? Проверьте размер массива, чему равен индекс.
    Ответ написан
    Комментировать
  • Почему возникает ошибка c std::mutex при makefile gtest?

    gbg
    @gbg Куратор тега C++
    Любые ответы на любые вопросы
    Компилятор С++ пенсионного возраста, или не настроен C++ >= С++11

    -проверить, что компилятор новый (GCC 11й версии или Clang 12й)
    -добавить флаг -std=c++17
    Ответ написан
    Комментировать
  • Как правильно называть пространства имён в проекте?

    GavriKos
    @GavriKos
    Стив Макконнелл - Совершенный код. Вот вам гайд
    Ответ написан
    3 комментария
  • Почему lambda не захватывает this?

    gbg
    @gbg Куратор тега C++
    Любые ответы на любые вопросы
    Банальная порча всех итераторов, ссылок и указателей в тот момент, когда вы вызываете emplace у вашей свалки потоков второй раз. Лечение - избавиться от переаллокации при вызове emplace, путем вызова reserve(), или использовать контейнер, который не передвигает свое содержимое туда-сюда, например, std::list
    Ответ написан
    Комментировать
  • Почему конструктор по умолчанию считается удаленным для моего класса?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    PurifiedElement_Constants не имеет конструктора по умолчанию (очень неудобные названия у вас ElementPurified_Constant vs PurifiedElement_Constants - обратите внимание, ошибка не про ваш класс, а про тип _properties).

    Т.е. член _properties в вашем классе нельзя сконструировать без каких-то параметров.

    Вам надо в вашем конструктрое класса явно вызывать конструктор _properties с какими-то параметрами:
    Machinarium::Materials::ElementPurified_Constant::ElementPurified_Constant()
        : _properties(some, valid, parameters)
    {
    }
    Ответ написан
    4 комментария
  • Как называть указатели?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В данном примере - никак. Эта переменная тут не нужна, вместо нее можно везде, где нужен адрес, писать &conteiner1. Ради экономии одного символа заводить еще одну переменную - плохая идея.

    Если же у вас будет какая-то функция, которой нужен указатель, то ей можно аргуаментом указать Conteiner* conteiner или еще что там больше по смыслу подходит.
    Ответ написан
    Комментировать
  • Почему бекенд не пишут на С++?

    Adamos
    @Adamos
    На Крестах вам придется с нуля писать то, что в Питоне или Пыхе есть из коробки: работа со строками, датами, БД и пр. - то есть 90% всего бэкенда. А когда вы все это напишете и соберете в классы, чтобы удобно пользоваться - вы внезапно обнаружите, что написали все тот же Пых, только в формате велосипеда.
    Ответ написан
  • Важен ли return в main?

    gbg
    @gbg Куратор тега C++
    Любые ответы на любые вопросы
    Формально, единственная функция, в которой можно не делать return - это именно main.

    В остальных случаях, если функция что-то возвращает, наличие return обязательно - иначе будет UB.

    Почему для main исключение - никто не знает, потому что параграф в стандарте языка, на который ссылается другой параграф - потерялся:

    I looked it up. I believe this was introduced with C99, and apparently the C99 rationale is defect. It has comments for 5.1.2.2.1 Program startup, then labels the next chapter 5.1.2.3 Program execution. It should have been 5.1.2.2. As a consequence of this, the rationale for Program termination that should have been in the real chapter 5.1.2.3, has gone missing in action. Thus, main allows no return code in C99 and there exists no rational reason why.


    Источник
    Ответ написан
    Комментировать
  • Как очистить выделенную память адреса под адрес указателя на первый элемент строки?

    Adamos
    @Adamos
    // выделяется память размером с указатель (4/8 байт для x32/x64), ее адрес записывается в text
    text = (const char**)malloc(sizeof(char*));
    // в text записывается адрес другой строки, а где там была выделена память - больше никто не знает
    text = &smth;
    // освобождается та память, на которую указывает text - это переменная smth. Теперь память, в которой лежит эта переменная, может в любой момент быть заменена при новом выделении памяти, и smth получит новое значение, не имеющее никакого отношения к той строке, на которую оно указывало изначально
    free(text);
    Ответ написан
  • Assignment operator VS Destructor + Placement new, где аргумент placement new - prvalue?

    @dolusMalus
    С++ programmer from gamedev
    Мне кажется, что данный вопрос стоит рассмотреть с точки зрения нескольких позиций:
    1. Безопасность относительно исключений. Какой уровень гарантий должен предоставлять данный метод?
    2. Производительность. Критично ли получить максимально эффективный код или на данном этапе этим можно пожертвовать?
    3. Оценить проектное решение в свете известных и проверенных практик, например, принципы SOLID


    Для начала определяемся используются ли вообще исключения в Вашем проекте и если нет, то можно переходить сразу к вопросу о производительности. В противном случае, рассмотрим каждый случай:
    int32_t index = sizeof(T) * head;
            (reinterpret_cast<T *>(&buffer[index]))->~T();
            new (&buffer[index]) T(std::forward<Args>(args)...);
            head = (head + 1) % maxSize;

    данный вариант не предоставляет даже базовых гарантий (basic exception guarantee), т.к. если конструктор сгенерирует исключение, то мы уже закончили время жизни объекта вызвав деструктор, но не изменятся size и head; а значит мы пришли в невалидное состояние.

    int32_t index = sizeof(T) * head;
            (reinterpret_cast<T *>(&buffer[index]))->~T();
            new (&buffer[index]) T(std::forward<Args>(args)...);
            head = (head + 1) % maxSize;

    Можем сделать развилку по noexcept для конструктора с помощью SFINAE или if constexpr и в случае, если конструктор является noexcept; то оставить данный код. В противном случае придется уже действовать в зависимости от необходимого уровня гарантий, однако решения будут достаточно громоздкие: в добавок к развилке надо будет ловить исключение, пробрасывать его дальше, при этом восстанавливать объект или уменьшать счетчики и т.п. Более того можно вообще не обеспечить сильную гарантию при определенных условиях. Как видите, это уже сильно осложнило решение.
    Теперь к другому варианту:
    // Assignment operator
    //        (*(reinterpret_cast<T *>(&buffer[sizeof(T) * head]))) = T(std::forward<Args>(args)...);

    Здесь ситуация относительно лучше, т.к. проблемы могут быть только на уровне оператора присваивания, что как минимум перекладывает часть ответственности в сторону автора типа T. Однако воспользовавшись решением от Евгений Шатунов можно относительно легко получить достаточно понятный и "чистый" код на уровне сильных гарантий. Также стоит посмотреть в сторону copy and swap идиомы, как близкой к данной проблеме.
    По итогу при необходимости строгих гарантий стоит отдать предпочтение варианту с временным объектом.

    С точки зрения производительности, если нет необходимости в максимальной оптимизации, то стоит отдать предпочтение более понятному коду. Данный момент уже прекрасно освещен в ответе Евгений Шатунов, поэтому не вижу смысла повторяться. Однако формально если отбросить предположения об оптимизациях, то вариант с реконструированием по месту (деструктор -> конструктор) оптимальней, т.к. гарантировано не требует выделения дополнительных ресурсов от временного объекта и только две операции + нет необходимости в относительно сложном анализе на перемешаемость/копируемость. В случае со swap, мы можем таки попасть на копирование в зависимости от перемещаемости типа T.

    И часто забываемый, но крайне важный пункт про проектирование. Здесь нарушен Single responsibility принцип, что возможно и породило этот вопрос. Т.е. у Вас метод по добавлению элемента может удалять/заменять элементы, что должно вызвать вопросы. Более того, вы решили за клиента вашего API (даже если это и Вы сами) как нужно обрабатывать исключительную ситуацию по переполнению. Потом например вы решите, что в одном месте стоит сложить старые элементы в отдельную очередь в другом залогировать или удалять не по одному, а сразу половину буффера. Все это потребует переписывания метода add, а зачем и почему? Попробуйте убрать эту часть кода заменив на выбрасывание исключения или вариант с возвращением успешности операции (менее грамотное решение, но это уже из области субъективной оценки) и посмотреть как увеличится прозрачность и простота написания клиентского кода для этого метода. Еще стоит посмотреть на сходное проектное решение в std::vector и его методе pop_back. Подумайте почему он не возвращает удаленный элемент?

    Итого, если важна производительность и не важна работа с исключениями; то разумно выбрать вариант с реконструированием; иначе обмен с временным объектом. Но не стоит забывать всегда про анализ проектного решения и правильную ли Вы проблему вообще решаете.
    Ответ написан
    2 комментария
  • В чем разница между delete и nullptr?

    gbg
    @gbg Куратор тега C++
    Любые ответы на любые вопросы
    g = nullptr не удалаят память, оно просто записывает в переменную g значение со смыслом - "указатель вникуда". Память при этом остается нетронутой и к ней можно обращаться, что вы и делаете, запомнив указатель в другой переменной.

    delete[] же освобождает память, а значит, все последующие обращения к ней, что при помощи g, что при помощи gg - будут вызывать UB
    Ответ написан
    Комментировать