LINUX.ORG.RU

Кто там кукарекал про С++?


1

8

Задача: разработать кроссплатформенное клиент-серверное приложение под Windows/Linux на С++ (boost, ace, etc.), клиент построчно считывает с консольки введёные числа, отправляет на сервер, сервер в ответ плюёт разложением чисел на простые множители. Стандартное тестовое задание, ничего интересного.

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

К слову, скелет сервера получился не сильно отличающимся от того, что написано в доках буста.
Что это за вырвиглазие??

class server
{
public:
  server(boost::asio::io_service& io_service, short port)
    : io_service_(io_service),
      acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
  {
    session* new_session = new session(io_service_);
    acceptor_.async_accept(new_session->socket(),
        boost::bind(&server::handle_accept, this, new_session,
          boost::asio::placeholders::error));
  }
  .......

Смеха ради, да и чтоб не вырвало от такого кода, накидал за полчаса решение на Haskell.
Что получилось:

  • Разбор параметров командной строки
  • Клиент-серверная архитектура
  • Полностью асинхронный многопоточный tcp-сервер
  • Поддержка unicode, IPv6 и BigInteger из коробки
  • Мемоизация (благодаря ленивости) из коробки
  • Полная кроссплатформенность (*nix, Mac OS, Windows etc.)
  • Правильность тривиально доказывается мат. индукцией по коду
  • Исходник чуть больше 60 строк (в 8 раз меньше, чем на крестах)

Если поднатужиться (я не стал) и заменить алгоритм нахождения простых чисел/простых множителей на более оптимальный, то ко всему прочему получаем автоматическую распараллелизацию алгоритмов из коробки (см. Data Parallel Haskell) и произодительность на уровне чистого Си/Фортрана.

Кто там пищал, что хаскель сугубо академический язык, что ничего реальго на нём написать невозможно? Кто там кукарекал про С++? Как вы с ним вообще работаете? Это же мазохизм в чистом виде (см. мыши и кактус)

★★★★★

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

Тем, что если его напичкать boost-ом по самые помидоры, то почти без кода (2000-3000 строк всего) получается бинарник в несколько Мб и компилируется это счастье несколько минут. Насколько я понял, в большом объеме виновато множественное инстанцирование шаблонов (если весь код собрать в один файл, то объем скомпилрованного файла получается адекватный).

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

Это устаревшее название LRnLA.

А что имеет в виду автор под «Формализм стены Фокса»?

ЗЫ: Значит все-таки «программно-аппаратный комплекс LRnLA/Nano», для «моделирования задач нано-оптики»? И ты, Бру^W^W^W И вы туда же?

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

В том то и прикол LRnLA, что дальше все то же самое, пока не кончится диск (или хранилище, доступное по сети - но так далеко мы пока не заходили;-)).

Это мне, как раз, как преимущество вашего метода ясно. Я говорю только о том, что классические методы для счета на отдельно взятом процессоре вполне проработаны, и более того, ваш метод является, как я понимаю, лишь обобщением классики на более общий случай гетерогенной среды.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

При этом особенностью данных задач является то, что даже при использовании алгоритмов линейной сложности по данным (к которым как раз и приводят ЯЛСМ), количество обрабатываемых данных таково, что справиться с ним могут только параллельные вычислительные системы (ПВС).

То есть задачи такого размера, что на один комп их не впихнешь. Сейчас это уже не так (типовую сейсмику мы считаем одна сейсмограмма на узел), но некоторые задачи по прежнему не влезают.

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

GPU вообще отдельная песня. Обычные методы будут уступать, и сильно (для задач размером больше RAM так точно будут). Хотя за тайлинг точно не скажу, там ИМНО большой потенциал, но его как бы и обычным методом пока не назовешь... ;-)

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

А что имеет в виду автор под «Формализм стены Фокса»?

Стену Фокса же. Тоже устоявшийся термин;-)

И вы туда же?

В том числе и туда же. Ну это и правда нано-оптика (фотонные кристаллы например) ;-)

AIv ★★★★★
()
Ответ на: [C++] от Deleted

Выглядит нормально.

EchoServiceHandler(StreamSocket& socket, SocketReactor& reactor) :
                        _socket(socket), _reactor(reactor), _pBuffer(new char[1024]) {
                _reactor.addEventHandler(
                                _socket,
                                NObserver<EchoServiceHandler, ReadableNotification>(*this,
                                                &EchoServiceHandler::onReadable));
void onReadable(const AutoPtr<ReadableNotification>& pNf) {
                int n = _socket.receiveBytes(_pBuffer, 1023);
                if (n > 0) {
                        _pBuffer[n] = 0;
                        int t;sscanf(_pBuffer, "%d", &t);
                        string r = "[";bool fst = true;

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

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

Стену Фокса же. Тоже устоявшийся термин;-)

А почему гугл о нем не знает? Что по-русски, что по-английски выдает единственную ссылку на PDF за авторством Левченко. Неужели меня забанили?

Macil ★★★★★
()
Ответ на: комментарий от no-such-file

классические методы для счета на отдельно взятом процессоре вполне проработаны

Как Вам сказать... актуальная для производства 3D задача, сейсмограмма в 10^9 узлов (в RAM не лезет), 5000 шагов по времени, генерится на обычной по нынешним временам персоналке за 20 часов. «Классические методы счета» такие задачи на отдельно взятом процессоре вообще считать не позволяют. Про тайлинг правда не скажу, ну у них же не было тестов для задач такого размера... хотя было бы интересно посмотреть.

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

А почему гугл о нем не знает?

Таак...

http://yandex.ru/yandsearch?text=стена-фокса параллельные-программы&lr=213

7я ссылка сверху, http://vmg.pp.ua/books/КопьютерыИсети/Распределённые и параллельные/Распредел...

стр 28 и далее.

С Вас 5тыс. руб.

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

Стену Фокса же. Тоже устоявшийся термин;-)

А почему гугл о нем не знает?

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

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

Да.

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

Именно питоновые структуры - список объектов, например? Наверное, можно, но не видел, чтобы кто-то такое делал. А зачем это делают в Хаскеле (если делают)? Си-программа получается зависимой от деталей реализации RTS.

черт знает возможность есть, я указал, я не насколько глубоко работал с haskell, чтобы с указателями начинать работать.

Из хороших вещей использующих эти реализация ByteString, где «массив» с массивом Word8 работается используя низкоуровневую работу с памятью и более интересно ByteString.Lazy где он разбивается на чанки по 64кб (чтобы влезал в кеши) и хранятся размеры и отступы, без использования низкоуровневой работы с памятью было бы грустно, а так sha256 от файлов на 64битных платформах работает не хуже реализаций на c (подробности бенчмарков не уверен, что найду присутсвовал на их обсуждении при тестировании ByteString и Binary).

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

Из хороших вещей использующих эти реализация ByteString, где «массив» с массивом Word8 работается используя низкоуровневую работу с памятью и более интересно ByteString.Lazy где он разбивается на чанки по 64кб (чтобы влезал в кеши) и хранятся размеры и отступы

Всё это можно сделать и через ctypes. Но это не хранение родных питоновских объектов в сырой памяти - просто ByteString будет держать в себе список указателей на c_byte*65536.

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

Конкретную критику можно?

Ничего не делающая многословность, отвратительнейший уровень абстракций и лютая байтосодомия.

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

Ничего не делающая многословность, отвратительнейший уровень абстракций и лютая байтосодомия.

Где ты все это увидел? Можно на каждую претензию говорить, к какому участку кода это относится и почему там плохо?

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

Байтосодомия, как я понимаю, состоит только в том, что в буфер в конец дописывается 0, чтобы получилась нуль-терминированная строка?

Deleted
()

<troll mode> ежу понятно нада было писать пиЭЙЧпи! </troll mode>

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

Да мало оно весит. Полной статикой в MSVC выходит метра полтора. Это аще фигня.

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

Где

Да везде.

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

Было:

        void handleOption(const std::string& name, const std::string& value) {
                ServerApplication::handleOption(name, value);
                if (name == "help")_helpRequested = true;
                if (name == "mode" && value == "server")_isServer = true;
                if (name == "port")_port = lexical_cast<int>(value);
                if (name == "host"){_host = value;_hostSetted = true;}
        }

Стало:

        handleOption: func (name, value: String) {
                super(name, value)
                match name {
                     "help" => _helpRequested = true
                     "mode" =>  if (value == "server") { _isServer = true }
                     "port" => _port = value toInt()
                     "host" => _host = value; _hostSetted = true
                }
        }

Было:

        void displayHelp() {
                HelpFormatter helpFormatter(options());
                helpFormatter.setCommand(commandName());
                helpFormatter.setUsage("OPTIONS");
                helpFormatter.setHeader("An echo server implemented using the Reactor and Acceptor patterns.");
                helpFormatter.format(std::cout);
        }

Стало:

        displayHelp : func {
                HelpFormatter new(options()) .
                      setCommand(commandName()) .
                      setUsage("OPTIONS") .
                      setHeader("An echo server implemented using the Reactor and Acceptor patterns.") .
                      format(stdout)
        }

Было:


                _reactor.addEventHandler(
                                _socket,
                                NObserver<EchoServiceHandler, ReadableNotification>(*this,
                                                &EchoServiceHandler::onReadable));
                _reactor.addEventHandler(_socket, NObserver<EchoServiceHandler, ShutdownNotification>(*this,
                                                &EchoServiceHandler::onShutdown));

Стало:

                _reactor addEventHandler(_socket, ReadableNotification(this, onReadable))
                _reactor addEventHandler(_socket, ShutdownNotification(this, onShutdown))

Ну и так далее.

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

Лично я не вижу особой разницы

Оставлена только суть, без многословного пустомудрия. Заниматься механической работой — задача компилятора, а не человека.

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

Перове - у C++ примитивный switch остался от C, хотя подобное можно запилить на C++0x.

Второе - возможно в C++, но в Poco так не сделано (точнее, сделано только для некоторых классов).

Третье - на C++ вполне реализуемо, разработчикам Poco сделать так мешала их политика относительно шаблонов.

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

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

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

Перове - у C++ примитивный switch остался от C, хотя подобное можно запилить на C++0x.

А super там когда появится?

Второе - возможно в C++, но в Poco так не сделано (точнее, сделано только для некоторых классов).

А должно быть сделано на уровне синтаксиса. (Хинт: точка в том коде — это не вызов метода.)

сделать так мешала их политика относительно шаблонов.

Ну а политика из чего взялась? Из пальца высосана, или же они уткнулись в грабли языка?

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

один пробел и отсутствующий typedef

Не распарсил.

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

А super там когда появится?

Никогда :) Ибо не нужен.

Хинт: точка в том коде — это не вызов метода.

А что тогда?

Ну а политика из чего взялась? Из пальца высосана, или же они уткнулись в грабли языка?

Ну, во первых, до 0x делегаты обычно делались через ж*пу, а во-вторых, относительно причин их политики насчет шаблонов - в их Coding standard.

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

Ибо не нужен.

Да-да. super не нужен. This не нужен. constructor не нужен. Принцип DRY тоже не нужен.

А что тогда?

В данном случае выражение вида объект . метод() вызывает указанный метод объекта, а результатом выражения служит объект. (В отличие от выражения вида объект метод(), где значением выражения является значение, возвращаемое из метода.)

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

Да-да. super не нужен. This не нужен. constructor не нужен. Принцип DRY тоже не нужен.

Из этого всего только super и constructor имеют сомнительную нужность. Чем плохо называть конструктор так же, как класс? А обычная нотация все-таки функциональнее, чем super, а вводить в C++ более узкую форму => зря усложнять язык => плохо.

В данном случае выражение вида объект . метод() вызывает указанный метод объекта, а результатом выражения служит объект. (В отличие от выражения вида объект метод(), где значением выражения является значение, возвращаемое из метода.)

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

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

Раз дошло до кода, вот вам решение на питоне

Ну тут, фактически, то что у меня в main, т.е. вызов функций client и server, сами эти функции не видно (они в mysocket). Версия на хаскеле это про то что используя конкурентность GHC и библиотеку network (URI, IPv4/IPv6, TCP, готовых серверов и клиентов там нет) можно написать tcp клиент/сервер (эхо, факториала, времени и т.п. простое общение) с одной стороны в несколько строчек, и, с другой стороны, переносимый, эффективный (в хаскеле forkIO делает лёгкие треды, примерно как в эрланге) и т.п. Сравнивать с C++ тут, наверно, будет не корректно (хаскель и C++ никогда не были языками одного уровня, C++ много чего не может как язык, рантайм C++ мало чего предоставляет помимо того что умеет ОС (ну, исключения, виртуальные методы, OpenMP, intel только начали пилить STM, а конкурентность, сообщения и синхронное/асинхронное IO исключительно такое, какое предоставляет ОС)). Сравнивать с питоном - наверно можно.

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

Из этого всего только super и constructor имеют сомнительную нужность.

Т.е. This в C++ уже изобрели? (не путать с this)

Чем плохо не следовать DRY?

fxd

А обычный ассемблер все-таки функциональнее, чем C++, вводить более узкую форму => зря усложнять язык => плохо.

fxd

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

«Нет, значит ненужно!» Я думаю, тебе вполне очевидно, что «то же» не получится. А если не очевидно, то подумай на эту тему.

Аргументы, конечно, потрясают глубиной. Скажи, ты когда-нибудь задумывался, над оптимизацией своего труда? Или, может быть, всё банальнее, и тебе платят за объём исходника?

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

Еще добавлю, что если бы, гипотетически, в C++ не было this, и приходилось бы в объявлении методов его явно указывать, то сейчас ты точно так же писал бы мне, что this «имеет сомнительную нужность» и «зря усложнять язык => плохо».

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

Т.е. This в C++ уже изобрели?

Что это за зверь?

Чем плохо не следовать DRY?

fxd

Т.е, называть конструкторы именем типа - это нарушение DRY?

«Нет, значит ненужно!» Я думаю, тебе вполне очевидно, что «то же» не получится. А если не очевидно, то подумай на эту тему.

Очевидно. И порой кажется, что было бы очень неплохо, если бы оно было :)

Вообще, я не спорю, что у C++ есть эти недостатки, просто сомневаюсь, что их исправление что-то серьезно изменило бы в плане производительности труда.

Скажи, ты когда-нибудь задумывался, над оптимизацией своего труда?

А на чем пишешь ты?

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

Не сказал бы так. Т.к. достаточно накушался языков, где аналог this нужно описывать в прототипе функции и, чтобы обратиться к члену объекта, для которого вызван метод, постоянно этот this указывать (Python, JS). Это, как минимум, не очень приятно.

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

Т.е, называть конструкторы именем типа - это нарушение DRY?

Да.

Что это за зверь?

This * this;

Вообще, я не спорю, что у C++ есть эти недостатки, просто сомневаюсь, что их исправление что-то серьезно изменило бы в плане производительности труда.

Да там всё целиком надо переделывать. Просто когда недостатков 100500, исправление трех произвольно выбранных действительно ни на что не влияет.

А на чем пишешь ты?

В основном, Js, Ruby, Си, иногда кресты. Из всего этого только Ruby позволяет выражаться по существу, без 100500 бесполезных символов. Ну мы же гипотетически рассуждаем. Впрочем, можно рассудить и практически и отправиться помогать разработчику OOC.

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

А, ну да, но что такое питон как язык и рантайм?

В том-то и проблема, что после постинга таких примеров на хаскеле приходится оправдываться относительно кода на Питоне. (Буэээ!!!).

Вообще, кто, блин, придумал что де код на хаскеле должен занимать меншье? С какого перепугу-то?

Например, у меня код для распарсивания нескольких имен файлов сообщений занимает ажно 130 строк на общую сумму 4.5 килобайт, разнесенный в два модуля.

Да блин, такую проблему можно было бы решить за 5 строчек на том же питоне. Потому что за рамки регулярных грамматик не выходит.

Удобочитаемостью это тоже не отличается. Ибо Parsec, а у меня лично Applicative головного мозга, и конструкции типа Foo <$> a <*> b <*> c <*> для меня написать — раз плюнуть.

Зато, как результат — полноценный AST на выходе и максимальный реюз парсер-комбинаторов. И полноценная обработка ситуаций с именами неизвестного формата, а не подход «ну-ка все подвинулись я щас блевать буду». Оставлен задел и для будущего изменения форматов, поскольку тип сообщений у меня определяется префиксами, неизвестное сообщенией у меня неизвестному рознь, и разные случае обрабатываются в разных частях программы.

И Хаскель такой подход ПООЩРЯЕТ. В чем его и самая главная заслуга.

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

на haskell в отличии от питона возможно общение с памятью не хуже чем в сях, правда в этих местах идеоматичность и куча бонусов будут исчезать...

«идиоматичность» использования хаскеля в данных «местах» будет возрастать

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

готовых серверов и клиентов там нет

да есть же целый список с той тем или иным удобством использования. Вон я пример на Conduit.Network привёл например

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

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

Не вижу необходимости в чём-то оправдываться, меня в приведённых примерах на хаскеле более менее всё устраивает.

Вообще, кто, блин, придумал что де код на хаскеле должен занимать меншье?

Относительно чего? Если C++, то всё понятно - автоматическое управление памятью, ФВП и комбинаторы, вообще облегчённый синтаксис (аппликации, много инфикса, возможность опускать скобки, отступы, объявления функций (отдельные сигнатуры типов) и данных (без аккессоров, т.к. есть паттерн-матчинг) ну и т.п., ISWIM rulez :)).

Например, у меня код для распарсивания нескольких имен файлов сообщений занимает ажно 130 строк на общую сумму 4.5 килобайт, разнесенный в два модуля.

Ну это просто ты такой дизайн предпочитаешь (который поощряет хаскель). Но вообще, то что тут обсуждается на любом языке, при использовании библиотек, делается просто (network и base которые позволяют так писать тоже не самые компактные библиотеки).

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

используя конкурентность GHC и библиотеку network (URI, IPv4/IPv6, TCP, готовых серверов и клиентов там нет)

Не, ну я могу на голом питонячьем socket сделать, правда там не будет проброса исключений с сервера на клиент и проч, но многопоточность будет... ну еще значит +7 строчек в сервере;-)

AIv ★★★★★
()

В том, то и проблема что задача синтетическая. А вот как только тебе прийдется залезть в какйюибудь СУБД с которой у хаскеля не лады так сразу начнется гемор.....

Ничего протоив хаскеля не имею. Но селяви.

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

Дык исключения пробросить вполне легко.

Кроме самого исключения надо еще стек с сервера пробрасывать и на клиенте по raise отображать, иначе толку то. Но ты не поверишь... в mysocket я это сделал;-)

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

Вон я пример на Conduit.Network привёл например

Я про пакет network говорил. А так, да, всегда можно написать библиотеку, экспортирующую подходящую «сделать зашибись» для данной конкретной задачи, но тогда сравнивать уже нечего (или сравнивать как в языках вызываются методы / функции). Хаскель это язык более высокого уровня чем Си или С++, а network - библиотека предоставляющая более высокоуровневый API чем glibc's sockets или boost.asio.

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

Так стек выдерни преобразуй в json или еще как и пробрось. В принципе штука занудная, но можно и этого не делать если использовать какойнибудь pyro

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

http://docs.python.org/library/socket.html

This module provides access to the BSD socket interface. It is available on all modern Unix systems, Windows, Mac OS X, BeOS, OS/2, and probably additional platforms.

Видимо, да. Там же и примеры есть. Мы пришли к тому, что на python/ruby/node/haskell/... tcp-шный hello world будет примерно один и тот же, только везде со своими заморочками (кооперативность в node, комбинаторы в haskell, ещё что-то в ruby/python).

Но TC хаскель с C++ сравнивал. Как если бы хаскель мог занять нишу C++ (или наоборот).

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

Я про пакет network говорил.

Моё утверждение состояло в том что на hackage есть несколько tcp серверов уже из коробки, основное отличие в том, что у них используется как backend, там есть несколько на iteratee, на conduit, и примерно тоже, что сделано в твоём посте.

Хаскель это язык более высокого уровня чем Си или С++, а network - библиотека предоставляющая более высокоуровневый API чем glibc's sockets или boost.asio

ну кстати network достаточно низкоуровневая библиотека.

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

что сравнивается в данном треде я не очень понимаю кто что пытается сравнить, похоже, что сравнивают чсв.

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