LINUX.ORG.RU

Добавление сахарку в C++ для быстрого написания «скриптов»

 ,


1

1

Доброй ночи. Ищу способ добавления ещё большего количества синтаксического сахара в C++, чтобы можно было писать компактные и выразительные небольшие приложения. Скажем, в качестве замены простыни из Perl/Bash/Python.

Допускаю использование любых библиотек, хэдеров, и фреймворков, в том числе и построенных на boost и qt4/qt5 (без GUI).

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

Попытки сделать это через boost (в том числе и boost::filesystem) или через glibc выливаются в лапшу из кода с кучей дополнительных телодвижений.

Вот пример относительно простого способа прочитать конфиги в алфавитном порядке из указанной директории (самым приятным пока рассматриваю вариант с Qt5)

QDir dir(QString::fromStdString(path));
if(!dir.exists()) {
    std::cerr << path << " does not exist\n";
    return 1;
}

for(const QFileInfo &fileinfo : dir.entryInfoList(QDir::Files | QDir::Readable, QDir::Name)) {
    QFile file(fileinfo.absoluteFilePath());
    file.open(QIODevice::ReadOnly);
    if(!file.isOpen()) {
        std::cerr << "Can't open file: " << qPrintable(file.fileName()) << "\nReason: " << qPrintable(file.errorString()) << '\n';
        return 1;
    }
    // ...
}

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

У кого какие идеи?

Скриптовые языки заменять на заранее более многословные плюсы (с ручной памятью и жесткой типизацией)? Совсем упоролся?

Pavval ★★★★★ ()

выносишь код в функцию, функция принимает std::function, передаешь ей лямбду - profit

anonymous ()

кстати твой код легко можно упростить и без «макросов», займись сначала этим

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

на заранее более многословные плюсы

Для этого и нужен сахар. Те же лямбды или for в C++11 значительно упрощают код.

с ручной памятью

Здесь тебе не C. Управлять памятью вручную можно (там, где это оправдано), в остальных случаях у нас есть переменные в стеке, которые уничтожаются при выходе из scope, деструкторы, а также смартпоинтеры.

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

жесткой типизацией

Это я как раз считаю большим плюсом. А если тебе лень выводить типы для переменных вручную - то есть ключевое слово auto, которое пришло в C++11.

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

Прозрачненько так

Я другой анонимус, еслечо.
Ну, как-то так, наверное. Недостающее допиши сам, пожалуйста.
otherwise - это макрос, порождающий лямду, понятное дело.

        auto
dir = existingDir(path, otherwise(, << path << " does not exist\n"));

for (auto  finfo : dir.entryInfoList(Files | Readable, Name)) {
            QFile
    file = openedFile(
        finfo.absoluteFilePath(), ReadOnly,
        otherwise(file, << "Can't open file: " << qPrintable(file.fileName())
                        << "\nReason: " << qPrintable(file.errorString())
                        << '\n'));

    // ...
}
anonymous ()

О, наконец обскурантизм и предельная упоротость апологетов C/C++ материализовалась в паре Eddy_Em/Chaser_Andrey.

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

Ура товарищи!

bj ()

я всё понимаю, но в сравнении с тем же башем это просто «Война и мир»

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

Здесь тебе не C. Управлять памятью вручную можно (там, где это оправдано), в остальных случаях у нас есть переменные в стеке, которые уничтожаются при выходе из scope, деструкторы, а также смартпоинтеры.

Ээээ...

А переменные в стеке в C не уничтожаются при выходе из scope?

А деструкторы в C++ уже самовызываемые? Вот не надо путать с деревом QObject'ов, когда уничтожение родителя убивает всех детей.

grondek ()
Ответ на: Re: Прозрачненько так от anonymous

Ага, можно:

qPrintable(file.errorString())

Заменить на

file.errorString().toLocal8Bit().constData()

Иначе вывод QString будет с кавычками.

grondek ()

Но зачем? Проще уж тогда например сразу на go писать.

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

я всё понимаю, но в сравнении с тем же башем это просто «Война и мир»

мы - C++ программисты - народ мускулистый

и да, можно покороче

for (auto  file : dirFiles(path))
{
    // ...
}
то есть всё волосатое говно, которое у ТСа, запихать в dirFiles и забыть, как страшный сон

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

А переменные в стеке в C не уничтожаются при выходе из scope?

Только POD. А всякие списки, хэш-таблицы, файловые дескрипторы?

А деструкторы в C++ уже самовызываемые?

За это можно не волноваться. Объект или удаляется при выходе из scope, или его автоматически удаляет смарт-поинтер.

Chaser_Andrey ★★★★★ ()

оно мало того, что многословное, так оно и нечитабельное. оставьте кресты, питон в помощь. Если питон тормозит, то есть cython. А ещё лучше http://nimrod-lang.org/

exhu ()

Но зачем? Сильно сомневаюсь, что на манипуляциях с файлами Си++ значительно обгонит Питон.

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

Чтобы научиться на C++, например?
Для этого нужно писать на C++ всё подряд, да побольше, побольше; чтение книжек, чужого кода и каментов на лоре недостаточно. В данной задаче не только обогнать педон по скорости работы с файлами, но и обойти по ясности кода.
То есть всякого говна в программе может быть море, но оно должно быть упрятано под врапперы (см. выше, говно -> dirFiles), а собственно смысловая часть кода должна быть краткой и ясной.

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

Но зачем?

Скажем, в качестве замены простыни из Perl/Bash/Python.

Человек реально надеется добавить столько «сахара» в C++, что код на нем будет компактнее «скриптоты». О, сколько нам открытий чудных...

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

То есть всякого говна в программе может быть море, но оно должно быть упрятано под врапперы

Тогда зачем ему «сахар»? Не всё ли равно, какое количество говна прятать во враппере? Пусть пишет свою библиотечку с ништяками, да дергает из нее функции/классы, тоже будет своего рода «сахар».

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

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

Так может уже есть что-то готовое?

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

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

Virtuos86 ★★★★★ ()

тебе надо свой шел запилить...

чтобы не велосипедить с самого начала, можешь взять за основу это: http://sourceforge.net/projects/libbash/

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

Но зачем?

1. Скорость работы.

2. Рантайм. Даже QtCore5 (Qt вообще считается жирной либой) - это лишь 5 мб so-шка. Расходы на запуск интерпретатора более жирные, учитывая, что so-шки - разделяемые, и достаточно одного экземпляра.

3. Статическая типизация. Мне не по душе языки с динамической типизацией.

4. Мне нравится C++.

5. Многопоточность. О, сколько было костылей, чтобы организовать работу с тредами в Python, чтобы параллельно обрабатывать несколько огромных файлов, хотя на C++ достаточно банальную лямбду и std::thread.

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

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

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

из boost filesystem tutorial:

  path p (argv[1]);

  try
  {
    if (exists(p))
    {
      if (is_directory(p))      // is p a directory?
      {
        copy(directory_iterator(p), directory_iterator(),
          ostream_iterator<directory_entry>(cout, "\n"));
      }
    }
    else
      cout << p << " does not exist\n";
  }

  catch (const filesystem_error& ex)
  {
    cout << ex.what() << '\n';
  }
чего проще?

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

Так ведь проще, не?

QDir dir(argv[1]);
if(dir.exists())
    for(const QFileInfo &fileinfo : dir.entryInfoList(QDir::Files | QDir::Readable, QDir::Name)) {
        // do something with fileinfo.absoluteFilePath()
    }
else
    std::cout << argv[1] << " does not exist\n";

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

С моей точки зрения - примерно одинаково. Это потому, что я много кода писал, и много читал (хотел бы развидеть, да не могу).

Оба варианта, и кутейный, и бустовый, нетрудно свести к вышесказанному

for (auto  file : dirFiles(path))
    {
        // ...
    }
, что, по-моему, не длиннее, чем на любом языке, хоть питон, хоть баш.

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

Кстати, на заметку entryInfoList и entryList безумно тормозные функции. Относительно большое количество файлов (2000-3000) уже вычитывают долго.

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

А всякие списки, хэш-таблицы, файловые дескрипторы?

Если ты не напишешь в деструкторе правильное уничтожение списка, то и он не уничтожится. Файлы не закроются.

Объект удаляется при выходе из scope

Это только если он не был создан в куче и без умного указателя.

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

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

С++
ясность кода

Отсыпь слегка.

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

Если ты не напишешь в деструкторе правильное уничтожение списка, то и он не уничтожится.

В C++ если я создаю std::map или QMap в стеке или в умном поинтере - то они вполне себе уничтожаться, как только выйдут за пределы scope.

Файлы не закроются.

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

Это только если он не был создан в куче и без умного указателя.

А если создан с умным указателем, то о нём позаботится умный указатель. Получается, мне память нужно вручную освобождать, только если я делаю new без умных поинтеров, но по-настоящему это мне нужно достаточно редко.

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

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

Смысла нет. Зашоренные крестовики отмахнуться, а модники и так попробуют. Кто не дает шанс чему то новому, будет страдать (ну или находиться в счастливом неведении) вечно.

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

Тот же boost::filesystem возвращает найденные сущности в директории в неопределенном порядке, так что и там надо писать код, чтобы сохранить всё найденное в векторе, который потом нужно посортировать, и только тогда можно обрабатывать. Это важно в конфигах, где имеет значение порядок их включения (например, с префиксами 00, 01, 10 и т.д.).

Впрочем, всегда есть профилирование, которое позволит убедиться, что узкое место - именно в entryInfoList и в entryList.

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

В твоем примере нет сортировки по имени, например. Это ещё пару строчек и дополнительный цикл обхода отсортированого вектора.

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

Если реально есть время - то напиши, сначала код, как тебе было бы удобно записывать свои манипуляции (лучше сразу оформленный в виде теста), а потом запили имплементацию.

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

Ну лично я может и извращенец, но бустовые интерфейсы мне нравяться больше. Посмотри ещё на Poco, но там оно не сильно от Qt отличается.

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

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

А почему вектор? Что мешает в set сложить?:) В том же бусте - никаких проблем это не вызовет :)

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