Thread: pclose() vs fclose()?

  1. #1
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545

    pclose() vs fclose()?

    I'd like to create an AutoClose RAII class, but I see there are different functions (like fclose() & pclose()) that have the same signature but different names (i.e. they both take FILE* parameters).

    I'm pretty sure the answer is going to be "that's undefined behavior", but I'll ask it anyways... What would happen if you mix fopen()/pclose() or popen()/fclose()?

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I'm pretty sure you are right on the "undefined behaviour", although in Linux at least, a pipe has the same type of fd as any other files and devices.

    Why not have a pure virtual base class and a pipe/file class above that?

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    One opens a stream to file, and the other opens a stream to a process.
    The behind the scenes activities are very different, regardless of the common API interface.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Yes, msdn's _pclose has this to say about what it actually does:
    Quote Originally Posted by MSDN
    The _pclose function looks up the process ID of the command processor (CMD.EXE) started by the associated _popen call, executes a _cwait call on the new command processor, and closes the stream on the associated pipe.
    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Nothing good. But the behaviour is not defined.

    From reading the man page, my best guess is that using fopen/pclose will result in pclose failing and doing nothing, whereas using popen/fclose will result in the child process becoming a zombie. But there's no way to rely on such behaviour.

    There are two solutions:
    1) Create two distinct RAII classes.
    2) Create a single RAII class that takes a function pointer (or other callable entity, if you want to get fancy) on construction, which it uses to close the stream.

    In this case, I'd strongly favour the first approach. I think opening files has very different use cases from starting a sub-process and communicating with it.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  6. #6
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Thanks, right after posting I noticed that part about pclose() calling cwait() before closing the handle.

    I guess I'll just do this:
    Code:
    class AutoClose
    {
    public:
        virtual ~AutoClose() = 0;
    ...
    };
    
    class AutoFClose : public AutoClose ...
    
    class AutoPClose : public AutoClose ...

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    No, I wouldn't do that. RAII objects are kept on the stack, and that's a bad place for polymorphism. If the F and P variants have any common code, make the base's destructor protected and non-virtual.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by matsp View Post
    I'm pretty sure you are right on the "undefined behaviour", although in Linux at least, a pipe has the same type of fd as any other files and devices.
    Except a popen() FILE actually needs two fds, because pipe fds are one-way communication only. So there are actually two pipes to a popen()'d process. They can't be the same under the hood.

  9. #9
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by CornedBee View Post
    No, I wouldn't do that. RAII objects are kept on the stack, and that's a bad place for polymorphism. If the F and P variants have any common code, make the base's destructor protected and non-virtual.
    What do you mean it's bad practice for polymorphism?
    All the Auto*Close classes are exactly the same except for the destructors; so rather than copy & pasting the same code over and over, why not just inherit an abstract base class and just override the destructor?

  10. #10
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> why not just inherit an abstract base class
    In this case, it's overkill in my opinion. I prefer to shy away from inheritence in a design, unless it's absolutely needed.

    gg

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I said it's a bad place for polymorphism, because the objects don't behave polymorphically. You won't find a use case where you'll deal with a pointer/reference to AutoClose and you want differing behaviour based on the dynamic type because, as you say, the only difference between the two is the destructor. And because they're RAII objects, they're going to be on the stack, which means their destructor won't ever be invoked polymorphically.

    All this means that it would be a bad idea to give them a virtual table. Make the destructor protected and non-virtual. You still get to inherit the implementation and interface. There is no danger because you can't accidentally destruct an object through a pointer to the base. It's the best solution.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> but I see there are different functions ... that have the same signature but different names

    Here's what I would do to handle the case of "multiple RAII functions with the same signature"...
    Code:
    template<int (*fclose_func)(FILE*)>
    class scoped_FILE
    {
        FILE *m_f;
        
        // prevent copy semantics
        scoped_FILE(const scoped_FILE&);
        const scoped_FILE& operator=(const scoped_FILE&);
    
    public:
        explicit scoped_FILE(FILE *f) : m_f(f) {}
        ~scoped_FILE() {fclose_func(m_f);}
    };//scoped_FILE
    
    ...
    
    FILE *f;
    ...
    scoped_FILE<&fclose> sf(f);
    gg

  13. #13
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Oh I see now... Even if I pass an AutoClose& to a function, the destructor won't get called until that function returns, so it doesn't need a virtual destructor.

  14. #14
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Exactly.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  15. #15
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Actually, now that I think of it, I think I like Codeplug's template idea better. I don't even need to derive any classes then -- I can just use typedefs.
    Code:
    typedef AutoClose<fclose>   AutoFClose;
    typedef AutoClose<pclose>   AutoPClose;

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. fclose slow
    By Rmantingh in forum C Programming
    Replies: 4
    Last Post: 04-08-2010, 11:01 AM
  2. Question about fclose
    By ulillillia in forum C Programming
    Replies: 6
    Last Post: 05-31-2007, 01:31 AM
  3. pclose without waiting for child
    By Largo in forum C Programming
    Replies: 1
    Last Post: 11-26-2005, 05:13 AM
  4. fclose()
    By GanglyLamb in forum C Programming
    Replies: 4
    Last Post: 11-11-2002, 08:45 AM
  5. quick fclose question
    By ihatejava in forum C Programming
    Replies: 5
    Last Post: 10-10-2002, 03:56 PM