LINUX.ORG.RU

Года бегут, а все-равно ваш C++ - ...

 ,


1

9

В соседнем треде промелькнула очень интересная мысль.

Отличие синьора-помидора на C++ от мидла в том, что последний уже знает что C++ говно, но еще не знает, почему.

Внимание, вопрос уровня синьора-помидора. Дан код. Объясните, почему другой синьор-помидор обосрался, написав его? Где может обосраться пользователь?

Задачу не будем усложнять, допустим, у нас single producer - single consumer предполагается.

#ifndef __BLOCKING_QUEUE_HPP__
#define __BLOCKING_QUEUE_HPP__

#include <cstdlib>
#include <mutex>
#include <condition_variable>

template<typename T>
class BlockingQueue
{
private:

    struct QueueNode
    {
        T val;
        QueueNode * next;
    };

    QueueNode *_first, *_last;
    std::mutex _cs;
    std::condition_variable _cv;
    bool _abort;
    int _count;

public:

    BlockingQueue()
    {
        _first = _last = nullptr;
        _abort = false;
        _count = 0;
    }

    ~BlockingQueue()
    {
        Flush();
    }
    
    BlockingQueue(const BlockingQueue& rhs) = delete;

    void operator=(const BlockingQueue& rhs) = delete;

    bool Put(const T& val)
    {
        std::unique_lock<std::mutex> lock(_cs);

        if(!_abort)
        {
            QueueNode * node = (QueueNode*)malloc(sizeof(QueueNode));
            if (node)
            {
                new (&node->val) T(val);
                node->next = nullptr;
                if (_last)
                    _last->next = node;
                else
                    _first = node;
                _last = node;
                ++_count;
                _cv.notify_one();
                return true;
            }
        }
        return false;
    }

    bool Get(T& val)
    {
        std::unique_lock<std::mutex> lock(_cs);

        for (;;)
        {
            if (_abort) return false;

            QueueNode * node = _first;
            if (node)
            {
                _first = node->next;
                if (!_first) _last = nullptr;
                --_count;
                val = node->val;
                node->val.~T();
                free(node);
                return true;
            }
            else
            {
                _cv.wait(lock);
            }
        }
    }

    int Count()
    {
        return _count;
    }

    void Flush()
    {
        QueueNode *node, *tmp;

        std::unique_lock<std::mutex> lock(_cs);

        for (node = _first; node; node = tmp)
        {
            tmp = node->next;
            node->val.~T();
            free(node);
        }

        _first = nullptr;
        _last = nullptr;
        _count = 0;
    }

    void Abort()
    {
        std::unique_lock<std::mutex> lock(_cs);
        _abort = true;
        _cv.notify_one();
    }

    void Start()
    {
        std::unique_lock<std::mutex> lock(_cs);
        _abort = false;
        _cv.notify_one();
    }
};

#endif // __BLOCKING_QUEUE_HPP__

Вопрос номер два - назовите хотя бы один язык программирования с подобными проблемами.

★★

Где еще есть дедлоки что ли? Примерно везде.

А судя по стилю, писал таки джун.

anonymous
()

С++
malloc
~T()

А еще можно рояль на йайца поставить, а потом рассуждать, что рояль говно, потому что больно

язабан

anonymous
()
Ответ на: комментарий от anonymous

писал таки джун.

писал студент, который вчера про цепепе узнал.

anonymous
()

А для этого надо запустить PVS студию

fornlr ★★★★★
()
            QueueNode * node = (QueueNode*)malloc(sizeof(QueueNode));
            if (node)
            {
                new (&node->val) T(val);

вот тут потенциальная утечка, а потом потенциальный пиздец по деструктору из-за не обнуленного node->next = nullptr;

anonymous
()

Объясните, почему другой синьор-помидор обосрался, написав его?

Потому что он написал аналог стандартно-библиотечной функциональности.

назовите хотя бы один язык программирования с подобными проблемами

Любой, в котором есть стандартная библиотека.

intelfx ★★★★★
()
Ответ на: комментарий от anonymous

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

lovesan ★★
() автор топика

Помимо вышеозвученного: переменная count должна быть либо volatile, либо atomic, либо что-то из этой серии, иначе компилятор может создать по регистровому «экземпляру» переменной на тред в процессе оптимизации и изменения не будут расшарены.

jcd ★★★★★
()
Последнее исправление: jcd (всего исправлений: 2)

Отличие синьора-помидора на C++ от мидла в том, что последний уже знает что C++ говно, но еще не знает, почему.

уже понял, что C++ говно, но не знаю почему т.к. не понимаю его. теперь я у мамы мидл
ушел покупать https://www.udemy.com/learn-how-to-code/ в срочном порядке.

system-root ★★★★★
()

Это не с++, а некая гогнопомесь рукожопого си-кода с плюсами.

anonymous
()

Объясните, почему другой синьор-помидор обосрался, написав его?

Потому что не обернул return _count мьютексом, ибо moodaque. Не объявил _abort как volatile по той же причине.

Заодно можешь передать помидору, что названия идентификатором, начинающихся с «_» и «__» зарезервированы.

Вопрос номер два - назовите хотя бы один язык программирования с подобными проблемами.

С какими именно проблемами? Соскучился по уютненькому synchronized? Хочешь, чтобы все кругом было volatile? Ну пиши на js, там ни потоков, ни всей этой головной боли.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

С тредами тут проблем нету, тут проблемы именно со спецификой C++

Пока никто их особо не назвал.

lovesan ★★
() автор топика
Ответ на: комментарий от Dendy

_abort же там везде под мутексом.

Дело не во мьютексе. Дело в том, что у компилятора нет никакой причины считать, что вызов _cv.wait() может повлиять на значение _abort, поэтому проверка if(_abort) может быть вынесена за тело цикла.

kawaii_neko ★★★★
()
Ответ на: комментарий от lovesan

С тредами тут проблем нету

Если ты их не видишь, это не значит, что их нет.

тут проблемы именно со спецификой C++

Ты намекаешь на то, что вот этот онанизм с malloc/free не от хорошей жизни, а чтобы избежать вызова конструктора по умолчанию? Так это не C++, это аффтар мудак не сообразил

QueueNode(const T &val) : val(val) {}
написать, вместо этого решил показать, как он умеет placement new делать.

Депремировать, публично высечь и назначить 40 часов принудительного чтения исходников STL. При рецидиве усадить читать исходники boost, после чего выгнать из профессии с позором.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

поэтому проверка if(_abort) может быть вынесена за тело цикла

Ну да, у нас же шаблон и все методы inline. Возможно ТС именно это и имел в виду под проблемами языка.

Dendy ★★★★★
()
Ответ на: комментарий от kawaii_neko

поэтому проверка if(_abort) может быть вынесена за тело цикла.

Ничего подобного с нормальным компилятором не произойдет, это домыслы.

Ты намекаешь на то, что вот этот онанизм с malloc/free не от хорошей жизни, а чтобы избежать вызова конструктора по умолчанию? Так это не C++, это аффтар мудак не сообразил

На самом деле, абсолютно однохерственно было бы, new/delete использовать, вместо malloc/free и placement new с явным вызовом деструктора. Но проблема близко. Скажем так, какая проблема есть тут, и какой бы не было в случае с std::queue? (а хотя смотря как ее использовать)

lovesan ★★
() автор топика
Последнее исправление: lovesan (всего исправлений: 1)
Ответ на: комментарий от lovesan

тут проблемы именно со спецификой C++

в С++ много проблем, но конкретно тут их нет. вижу тут проблемы с головой.

anonymous
()
Ответ на: комментарий от kawaii_neko

решил показать, как он умеет placement new делать

Возможно оставлял задел на оптимизацию для аллокации нод в массиве. В текущей реализации да, смысла не имеет.

Dendy ★★★★★
()
Ответ на: комментарий от kawaii_neko

Дело в том, что у компилятора нет никакой причины считать, что вызов _cv.wait() может повлиять на значение _abort, поэтому проверка if(_abort) может быть вынесена за тело цикла.

Нет, не может, компилятор по умолчанию все ф-ии считает грязными.

anonymous
()
Ответ на: комментарий от lovesan

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

anonymous
()

Это ССЗБ, конечно, но может имеется в виду бесконечный цикл в Get при удалении объекта очереди, который используется, либо move-копирование/присваивание такого объекта?

xaizek ★★★★★
()
Ответ на: комментарий от kawaii_neko

Заодно можешь передать помидору, что названия идентификатором, начинающихся с «_» и «__» зарезервированы.

Ты забыл про регистр первой буквы после _

anonymous
()

Вопрос номер два - назовите хотя бы один язык программирования с подобными проблемами. =====================================

Неужели лишп?

anonymous
()
    bool Get(T& val)
    {
        std::unique_lock<std::mutex> lock(_cs);

        for (;;)
        {
            if (_abort) return false;
            ...
        }
    }

    void Abort()
    {
        std::unique_lock<std::mutex> lock(_cs);
        _abort = true;
        ...
    }

Вызов Abort() не прервёт ожидающий Get()?

NeXTSTEP ★★
()

Ой! А когда это Лавсосанчика разбанили?

anonymous
()
Ответ на: комментарий от UVV

Чё за трешь?

Там выше по коду placement new используется. Так что деструктор у такого объекта нужно дергать руками.

andreyu ★★★★★
()

Откуда эта фиксация на фекальных темах здесь?

Deleted
()
Ответ на: комментарий от xaizek

либо move-копирование/присваивание такого объекта?

Практически в точку. А если точнее - то проблема в отсутствии каких-либо знаний о move-семантике у коллекции, как таковой.

Вот с таким классом(который видимо уже написал не синьор-помидор, а мидл) данная очередь работать обосрется:

class SuperCoolRAIIResourceWrapper
{
private:
    void* _res;

public:
    SuperCoolRAIIResourceWrapper(void* res)
    {
        std::cout << "Constructor " << (void*)this << std::endl;
        _res = res;
    }

    ~SuperCoolRAIIResourceWrapper()
    {
        std::cout << "Destructor " << (void*)this << std::endl;
        // destroy resource here
        _res = NULL;
    }
};

С классом, реализующим rvalue и прочие move, очередь же работать не станет.

Внимание вопрос - что надо поправить и где? Ответ, что использовать shared_ptr (или какой-нибудь ComPtr) в качестве элемента коллекции, в целом принимается, конечно, и такое многие делают и по сей день, и не только с самописными коллекциями, и правильно делают, в целом. Но а если прям как бох крестов?

Внимание, вопрос номер два - в каких еще языках есть такие проблемы?

lovesan ★★
() автор топика
Ответ на: комментарий от lovesan

вопрос номер два - в каких еще языках есть такие проблемы?

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

anonymous
()
Ответ на: комментарий от anonymous

как видно на практике, в плюсах повсеместно хранят умные указатели, и не зря делают

lovesan ★★
() автор топика

ОП, я дальше первых тридцати строчек этого говнокода даже читать не стал. Делать такой велосипед используя С++11 ещё нужно додуматься. И C++ здесь ни при чем. Просто обезьяне дали микроскоп и она не знает как им пользоваться.

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 2)
Ответ на: комментарий от lovesan

Практически в точку. А если точнее - то проблема в отсутствии каких-либо знаний о move-семантике у коллекции, как таковой.

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

vzzo ★★★
()
Ответ на: комментарий от lovesan

А если точнее - то проблема в отсутствии каких-либо знаний о move-семантике у коллекции, как таковой

Ты ведь сам написал:

bool Put(const T& val)

Какие претензии к языку?

Move-семантика никакая не тонкость.

Оно сразу очевидно, что приведённый код её не поддерживает.

Но поскольку в условиях про это требование не было ни слова, то, значит, оно и не особо и было нужно.

anatoly
()

Такая шняжка в стандартной питон реализации есть (примерно), т.ч. валите все на питон :-D а не мучайтесь с C++, в крайнем случае многопоток можно прикрутить через C расширения.

menangen ★★★★★
()
Последнее исправление: menangen (всего исправлений: 1)
Ответ на: комментарий от lovesan

Принимать по значению и перемещать в ноду, хранить/отдавать можно либо по значению и кидать исключение либо в unique_ptr и не кидать (пустой будет обозначять ошибку).

в каких еще языках есть такие проблемы?

В разных языках разные проблемы. Вон в хаскеле можно написать (a + b) и получить пожирание памяти из-за ленивости. И я не уверен, что я бы предпочёл отлаживать такие бока. Шарповые async/await и автоматический подсчёт ссылок в C++/CX тоже мозг выносили неслабо.

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

xaizek ★★★★★
()
Ответ на: комментарий от lovesan

Твой SuperCoolRAIIResourceWrapper говно и писал его никакой не мидл, если не удалил оператор присваивания и копирующий конструктор

anonymous
()
Ответ на: комментарий от lovesan

Практически в точку. А если точнее - то проблема в отсутствии каких-либо знаний о move-семантике у коллекции, как таковой.

Ну ты клоун, конечно. Тебе же сразу сказали, что это не с++ код и тут всё нужно причёсывать.

anonymous
()

А с чего ты взял, что этот код написан на C++?

Обычное кривое поделие, судя по виду, специально предназначенное для сбивания ЧСВ у кандидатов на собеседовании. Или троллинга, что собственно одно и то же.

Ну, про говноконторки, начинающие с демотивации даже ещё не работников — не будем. Чего брать с дебилов?

Заданный вопрос некорректен. Отсутствуют функциональные требования, а ты просишь описать «где облажался автор». Т.е. сначала нужно восстановить а чего думал автор (если думал), а потом догадаться в каких условиях будут всплывать косяки. Естественно, корректно на этот вопрос ответить невозможно.

Если интересно мнение как бы я поступил бы? Ну, для начала я не стал бы заморачиваться с unbounded очередью.

Macil ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.