Nano Hash - криптовалюты, майнинг, программирование

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

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

#include<iostream>
int main(int argc, char* argv[]){
    ... process options...

    std::ostream& out = ... // maybe std::cout, maybe a *new* std::ofstream;
    out << "content\n";
}

Есть ли подходящая идиома для альтернативы std::cout или файловому потоку во время выполнения?

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

#include<iostream>
#include<ofstream>
int main(int argc, char* argv[]){

    std::string file = argc>1?argv[1]:"";
    std::clog << "file: " << file << '\n';
    // if there is no argument it will print to screen
    std::ostream* out = (file=="")?&std::cout:(new std::ofstream(file)); // horrible code
    *out << "content" << std::endl;
    if(out != &std::cout) delete out;

}

Я не знаю, возможно, есть какая-то особенность потоков C++, которая позволяет это делать. Возможно, мне нужно использовать какое-то стирание шрифта. Проблема, я думаю, в том, что std::cout — это то, что уже существует (является глобальным), а std::ofstream — это то, что нужно создать.

Мне удалось использовать open и избежать указателей, но это все еще уродливо:

int main(int argc, char* argv[]){

    std::string file = argc>1?argv[1]:"";
    std::clog << "file: " << file << '\n';

    std::ofstream ofs; 
    if(file != "") ofs.open(file); 
    std::ostream& out = (file=="")?std::cout:ofs;
    out << "content" << std::endl;
}
19.07.2016

  • Указатели не обязательно означают динамическое размещение. 19.07.2016
  • Может быть, немного не так, но если это программа командной строки, вы всегда должны выводить в std::cout и позволять пользователю решать, хочет ли он перенаправить вывод в файл. 19.07.2016
  • Возможный дубликат присвоения cout имени переменной 19.07.2016
  • Этот вопрос может представлять интерес: похоже, что stdistringstream разрешается иначе, чем stdifstream в th">stackoverflow.com/questions/24706480/ 19.07.2016

Ответы:


1

Я предпочитаю использовать потоки с установленными подходящими потоковыми буферами. Вот один из способов прямого вывода в файл или в std::cout:

#include <iostream>
#include <fstream>
int main(int ac, char* av) {
    std::ofstream ofs;
    if (1 < ac) {
       ofs.open(av[1]);
       // handle errors opening the file here
    }
    std::ostream os(file? file.rdbuf(): std::cout.rdbuf());
    // use os ...
}
19.07.2016
  • Это явно лучший ответ, но я единственный, кто проголосовал за него! 19.07.2016
  • Это очень аккуратно. Легко забыть о буферных аспектах потоков. 19.07.2016

  • 2

    Так много чрезмерной инженерии.

    #include <iostream>
    #include <fstream>
    
    int main(int argc, char* argv[]) {
    
        std::ofstream ofs(argc > 1 ? argv[1] : "");
        std::ostream& os = ofs.is_open() ? ofs : std::cout;
    
        // use os ...
    }
    
    19.07.2016
  • Хороший. но страшно. Будет ли ofstream пытаться открыть "" или не откроется автоматически? . Кроме того, если поток ofs не работает по другим причинам (файл не доступен для записи), он сделает что-то неожиданное. (Не поймите меня неправильно, мне нравится решение +1) 19.07.2016
  • @alfC, прочитайте документацию std::basic_ofstream, чтобы точно узнать, как распознать упомянутые вами проблемы. Вы действительно переусердствуете с этим. 19.07.2016
  • @alfC, что означает открыть ""? Как открыть файл без имени? Вам когда-нибудь удавалось создать файл с пустым именем? Как вы думаете, что произойдет, если вы попытаетесь открыть файл, недоступный для записи, для записи? Подсказка: он не делает ничего неожиданного. Научитесь использовать свои инструменты вместо того, чтобы писать много хрупкого кода для решения несущественных проблем. 19.07.2016
  • 1) если файловой системы нет или она недоступна для записи, интересно, будет ли std::ofstream ofs(""); по-прежнему попытаться что-то сделать. 2) если `argv[1] == /readonlylocation, то программа будет печатать на экран, а не выдавать ошибку просто потому, что не может открыть файл в режиме записи (это неожиданный результат). 19.07.2016
  • 1) конечно нет! 2) ах, ладно, неожиданно для пользователя, да, это правда. Отличный ответ Дитмара позволяет легко это проверить. 19.07.2016

  • 3

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

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

    int main(int argc, char* argv[]){ 
        std::string file = argc > 1 ? argv[1] : "";
        std::clog << "file: " << file << '\n';
    
        // if there is no argument it will print to screen
        std::unique_ptr<std::ostream> fp;
        if (file == "")
            fp = std::make_unique<std::ofstream>(file);
    
        std::ostream& out = (fp && fp->is_open()) ? std::cout : *fp; // not so horrible code
    
        out << "content" << std::endl;
    }
    

    Если динамический объект не требуется, проще всего перечислить это;

    int main(int argc, char* argv[]){
        std::string filename = (argc > 1) ? argv[1] : "";
    
        std::ofstream file(filename);
    
        // if there is no argument (file) it will print to screen
        std::ostream& out = file.is_open() ? file : std::cout;
    
        out << "content" << std::endl;
    }
    
    19.07.2016
  • Вам вообще не нужно динамическое размещение. Вы пытаетесь обойти это с помощью автоматического хранения std::unique_ptrs, но это просто избыточно. 19.07.2016
  • Я знаю, что у ОП это было в исходном посте, может быть, ему это понадобилось по какой-то причине ... Его обновления, похоже, говорят, что, может быть, и нет ... 19.07.2016

  • 4

    Я часто использую что-то вроде этого для инструментов командной строки:

    int main(int, char* argv[])
    {
        std::string filename;
    
        // args processing ... set filename from command line if present
        if(argv[1])
            filename = argv[1];
    
        std::ofstream ofs;
    
        // if a filename was given try to open
        if(!filename.empty())
            ofs.open(filename);
    
        // bad ofs means tried to open but failed
        if(!ofs)
        {
            std::cerr << "Error opeing file: " << filename << '\n';
            return EXIT_FAILURE;
        }
    
        // Here either ofs is open or a filename was not provided (use std::cout)
    
        std::ostream& os = ofs.is_open() ? ofs : std::cout;
    
        // write to output
        os << "Some stuff" << '\n';
    
        return EXIT_SUCCESS;
    }
    
    19.07.2016

    5

    Вы можете использовать общий указатель на поток для полиморфного поведения:

    #include <memory>
    #include <fstream>
    #include <sstream>
    #include <iostream>
    
    void nodelete(void*) {}
    
    std::shared_ptr<std::ostream> out_screen_stream() { return std::shared_ptr<std::ostream>(&std::cout, nodelete); }
    std::shared_ptr<std::ostream> out_file_stream() { return std::make_shared<std::ofstream>(); }
    std::shared_ptr<std::ostream> out_string_stream() { return std::make_shared<std::ostringstream>(); }
    
    int main ()
    {
        std::shared_ptr<std::ostream> out;
    
        // case condition:
        out = out_screen_stream();
        out = out_file_stream();
        out = out_string_stream();
    
        *out << "content" << std::endl;
        return 0;
    }
    

    Примечание: std::shared_ptr позволяет управлять различными возможными потоками, при этом некоторые потоки не должны удаляться (например, std::cout).

    Аналогично, но с std::unique_ptr:

    #include <memory>
    #include <fstream>
    #include <sstream>
    #include <iostream>
    
    class Deleter
    {
        public:
        Deleter(bool use_delete = true) : use_delete(use_delete) {}
    
        template <typename T>
        void operator () (const T* p) {
            if(use_delete)
                delete p;
        }
    
        bool nodelete() const { return ! use_delete; }
    
        private:
        bool use_delete;
    };
    
    using unique_ostream_ptr = std::unique_ptr<std::ostream, Deleter>;
    unique_ostream_ptr out_screen_stream() { return unique_ostream_ptr(&std::cout, false); }
    unique_ostream_ptr out_file_stream() { return unique_ostream_ptr{ new std::ofstream }; }
    unique_ostream_ptr out_string_stream() { return unique_ostream_ptr{ new std::ostringstream  }; }
    
    int main ()
    {
        unique_ostream_ptr out;
    
        // case condition:
        out = out_screen_stream();
        out = out_file_stream();
        out = out_string_stream();
    
        *out << "content" << std::endl;
        return 0;
    }
    
    19.07.2016

    6

    Может ссылка?

    #include<iostream>
    #include<ofstream>
    
    
    int main(int argc, char* argv[])
    {
        auto &out = std::cout;
        std::ofstream outFile;
    
        std::string fileName = argc>1?argv[1]:"";
    
        std::clog << "file: " << file << '\n';
        // if there is no argument it will print to screen
        if(!fileName.empty())
        {
            outFile.open(fileName);
            out = outFile;
        }
    
        out<<"one, one, two";
    
        return 0;
    }
    
    19.07.2016
  • Ссылку нельзя переназначить, потоки тоже. 19.07.2016
  • Новые материалы

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

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..