Search code examples
c++language-agnosticraii

Do programmers of other languages, besides C++, use, know or understand RAII?


I've noticed RAII has been getting lots of attention on Stackoverflow, but in my circles (mostly C++) RAII is so obvious its like asking what's a class or a destructor.

So I'm really curious if that's because I'm surrounded daily, by hard-core C++ programmers, and RAII just isn't that well known in general (including C++), or if all this questioning on Stackoverflow is due to the fact that I'm now in contact with programmers that didn't grow up with C++, and in other languages people just don't use/know about RAII?


Solution

  • For people who are commenting in this thread about RAII (resource acquisition is initialisation), here's a motivational example.

    class StdioFile {
        FILE* file_;
        std::string mode_;
    
        static FILE* fcheck(FILE* stream) {
            if (!stream)
                throw std::runtime_error("Cannot open file");
            return stream;
        }
    
        FILE* fdup() const {
            int dupfd(dup(fileno(file_)));
            if (dupfd == -1)
                throw std::runtime_error("Cannot dup file descriptor");
            return fdopen(dupfd, mode_.c_str());
        }
    
    public:
        StdioFile(char const* name, char const* mode)
            : file_(fcheck(fopen(name, mode))), mode_(mode)
        {
        }
    
        StdioFile(StdioFile const& rhs)
            : file_(fcheck(rhs.fdup())), mode_(rhs.mode_)
        {
        }
    
        ~StdioFile()
        {
            fclose(file_);
        }
    
        StdioFile& operator=(StdioFile const& rhs) {
            FILE* dupstr = fcheck(rhs.fdup());
            if (fclose(file_) == EOF) {
                fclose(dupstr); // XXX ignore failed close
                throw std::runtime_error("Cannot close stream");
            }
            file_ = dupstr;
            return *this;
        }
    
        int
        read(std::vector<char>& buffer)
        {
            int result(fread(&buffer[0], 1, buffer.size(), file_));
            if (ferror(file_))
                throw std::runtime_error(strerror(errno));
            return result;
        }
    
        int
        write(std::vector<char> const& buffer)
        {
            int result(fwrite(&buffer[0], 1, buffer.size(), file_));
            if (ferror(file_))
                throw std::runtime_error(strerror(errno));
            return result;
        }
    };
    
    int
    main(int argc, char** argv)
    {
        StdioFile file(argv[1], "r");
        std::vector<char> buffer(1024);
        while (int hasRead = file.read(buffer)) {
            // process hasRead bytes, then shift them off the buffer
        }
    }
    

    Here, when a StdioFile instance is created, the resource (a file stream, in this case) is acquired; when it's destroyed, the resource is released. There is no try or finally block required; if the reading causes an exception, fclose is called automatically, because it's in the destructor.

    The destructor is guaranteed to be called when the function leaves main, whether normally or by exception. In this case, the file stream is cleaned up. The world is safe once again. :-D