Блог пользователя CodingKnight

Автор CodingKnight, 4 года назад, По-английски

Hello Codeforces,

It is sometimes required in competetive programming problems to use fast data input/output and/or read and write problem data from and to data files. It is well known that the C++ standard libray functions std::ios_base::sync_with_stdio(), std::basic_io::tie(), and std::freopen() can be used to peform the sought input/output initializtion. The following is a C++ io_init class that simplifies this initialization by encapsulating it inside public class constructors.

#include <bits/stdc++.h>
using namespace std;

class io_init {
    struct fast { fast() { ios_base::sync_with_stdio(false), cin.tie(nullptr); } } io;
    void reopen(const string &file_name, const char *mode, FILE *stream) {
        if (freopen(file_name.c_str(),mode,stream) == nullptr) 
            throw runtime_error("freopen() failed: cannot open file "+file_name); }
public:
    io_init() {}
    io_init(const string &data) { reopen(data+".in","r",stdin), reopen(data+".out","w",stdout); } };
		    
int main() { 
     io_init(); // io_init("data"); 
    // solution
}

The first class constructor without parameter initializes fast input/output by creating the io instance of the private data structure fast. The second class constructor with string data paremeter performs the same operation, and then initializes the data input/output to read data from input file data+".in" and write results to output file data+".out". The private class member function reopen() throws a run-time error exception if the freopen() function call returns nullptr.

Your constructive comments and feedback are welcome and appreciated.

UPDATE

The following alternative to use a fuction call instead of using public class constructors is favored by Actium.

#include <bits/stdc++.h>
using namespace std;

void io_init(const string &data = "") { 
    const auto reopen = [data](const string &ext, const char *mode, FILE *stream) {
        const string file_name = data+"."+ext;
        if (freopen(file_name.c_str(),mode,stream) == nullptr)
            throw runtime_error("freopen() failed: cannot open file "+file_name); };
    if (ios_base::sync_with_stdio(false), cin.tie(nullptr), not data.empty())
        reopen("in","r",stdin), reopen("out","w",stdout); }
		    
int main() {
    io_init(); // io_init("data");
}

A third alterntative (favored by spookywooky) that does not require a function call to be included in the main() function is to declare io_init as a named lambda expression as follows.

#include <bits/stdc++.h>
using namespace std;
     
const auto io_init = [](const string &data = "") {
    const auto reopen = [data](const string &ext, const char *mode, FILE *stream) {
        const string file_name = data+"."+ext;
        if (freopen(file_name.c_str(),mode,stream) == nullptr)
            throw runtime_error("freopen() failed: cannot open file "+file_name); };
    if (ios_base::sync_with_stdio(false), cin.tie(nullptr), not data.empty())
        reopen("in","r",stdin), reopen("out","w",stdout); 
        return 0; } (); // ("data");
    
int main() {
    // solution
}

A fourth alterntative and perhaps the simplest way to initialize data input/output files for read/write is to create std::ifsteam and std::ofstream objects. Names other than cin and cout should be used for the created object if using namespace std; is part of the program so as to avoid name redeclaration errors.

#include <bits/stdc++.h>
using namespace std;
     
ifstream  i_data("data.in");
ofstream  o_data("data.out");

#define cin  i_data
#define cout o_data

int main() {
    
   int a, b; 

   cin >> a >> b;        // read (a) and (b) from file data.in

   cout << a+b << endl;  // write (a+b) to file data.out
}
  • Проголосовать: нравится
  • -23
  • Проголосовать: не нравится

»
4 года назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

I think it is enough to have a single function and call it from main(), that is class io_init + nested class fast are just overcomplication in my opinion.

  • »
    »
    4 года назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    Thanks for your feedback. I appreciate your opinion. Note that the class constructor can be called from the main() function just like any other function if it not necessary to store the object reference explicitly using a variable name. Check the updated main() function for an illustration.

    • »
      »
      »
      4 года назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Even object construction looks as a function call ;) Just make it a function that takes single argument (filename) with default value and does all the initialization work.

      • »
        »
        »
        »
        4 года назад, # ^ |
        Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

        Check the update section. It does seem that the function call is simpler and more favored. It is even less by one line than the class implementation :-) Thanks again for your constructive feedback.

»
4 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

" throw runtime_error("freopen() failed: cannot open file "+file_name); }; " for this line, even for a hello world it is signalling : "[Error] 'runtime_error' was not declared in this scope"...

»
4 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

I prefer a static initializer, it is called before main(), example

const bool ready = [](){
    ios_base::sync_with_stdio(false); cin.tie(0);
    cout << fixed << setprecision(12);
    return true;
}();

signed main() {
  // no init call nessecary
}
  • »
    »
    4 года назад, # ^ |
      Проголосовать: нравится +5 Проголосовать: не нравится

    Thanks for your feedback. I have just added this third alternative to the update section before reading your comment.