Re: Why is RAII called RAII?

Discussion in 'C++' started by Goran Pusic, Sep 13, 2010.

  1. Goran Pusic

    Goran Pusic Guest

    On Sep 12, 7:37 pm, Rolf Magnus <> wrote:
    > In fact, in many cases, the resources at not acquired at
    > initialization time but later on during object lifetime.


    (Warning: I am off-exact-topic).

    These cases are wrong most of the time. You are talking about two-
    phase initialization. Each two-phase initialization brings in one more
    state into the program (object available, but unusable), which is
    incidental complexity. In my experience, when one thinks this is
    needed, there is usually a different design where it's not, and impact
    on code size and performance is negligible and can go either way.

    Goran.
     
    Goran Pusic, Sep 13, 2010
    #1
    1. Advertising

  2. Goran Pusic

    Richard Guest

    Two-phase initialization (was: Why is RAII called RAII?)

    [Please do not mail me a copy of your followup]

    Goran Pusic <> spake the secret code
    <> thusly:

    >These cases are wrong most of the time. You are talking about two-
    >phase initialization. Each two-phase initialization brings in one more
    >state into the program (object available, but unusable), which is
    >incidental complexity. In my experience, when one thinks this is
    >needed, there is usually a different design where it's not, and impact
    >on code size and performance is negligible and can go either way.


    Sometimes its convenient to call a virtual method as part of the
    construction process. You can't do this from a c'tor in C++ so you
    have to have some sort of two-phase initialization. When this is the
    case, I've hidden the details of the two-phase initialization inside a
    static factory method of the class and made the c'tor private (or
    protected since you have virtual methods and its implied that derived
    classes want to override those virtual methods and therefore will need
    some way to call the c'tor of the base class).

    I'd be interested in hearing about design alternatives for this
    situation as the two-phase intialization approach prohibits creating
    such objects on the stack and forcing them into the heap.
    --
    "The Direct3D Graphics Pipeline" -- DirectX 9 draft available for download
    <http://legalizeadulthood.wordpress.com/the-direct3d-graphics-pipeline/>

    Legalize Adulthood! <http://legalizeadulthood.wordpress.com>
     
    Richard, Sep 13, 2010
    #2
    1. Advertising

  3. Goran Pusic

    Balog Pal Guest

    "Goran Pusic" <>
    >
    > In fact, in many cases, the resources at not acquired at
    > initialization time but later on during object lifetime.


    >(Warning: I am off-exact-topic).


    >These cases are wrong most of the time.


    Are they now?

    >You are talking about two-phase initialization.


    2-phase init is definitely a 'school' that lingers on, especially because
    many homegrown coding standards prohibit exceptions.

    But it is not everything -- many clases can pick up stuff en-route, or use
    alternative resources, or a different set at different states.

    >Each two-phase initialization brings in one more
    >state into the program (object available, but unusable), which is
    >incidental complexity.


    Even the traditional 2-phase init has its good use cases, especially if you
    can chose it. See MFC's CFile. You have both ctor that opens file
    immediately from passed info and throws on error. And another that just
    creates the object and you can Open() later. And can reuse it too, after
    close.

    You can have CFile members in an object that will open the file eventually,
    that is not part of 'init' but its natural life cycle.

    >In my experience, when one thinks this is
    >needed, there is usually a different design where it's not, and impact
    >on code size and performance is negligible and can go either way.


    I have a library of many RAII handler objects. And my experience is that
    the simplest case -- just having ctor and dtor -- are the least used.

    If I write a new one, I generally make it use the auto_ptr interface:

    - ctor with optional taking ownership
    - reset()
    - release()
    - dtor

    And it serves best. Clients can still use it for pure RAII. Can use just the
    RRID (resource release is destruction (C) Matthew Wilson ;). Can reuse via
    reset. Or even transfer stuff or handle spacial cases via release().

    At my new place i did not (yet) import my suite, deciding we use boost
    anyway, that has smart poitners, including scoped_ptr. I thought it will
    be the same as my auto_prtNC (NC for no-copy) -- but no, it has the reducer
    interface, and it turned out a general PITA.

    The important feature of 'RAII' is not construction (not even the
    interface), but the strict unique ownership imposed by design and coding
    policy -- so you can always tell who is responsible for what, and with the
    RRID behavior, most of problems go away automagically.

    As for the original question: term RAII got stuck, despite using it we quite
    rarely mean RAII in the original sense, but more the mentioned RRID or SBRM
    or something alike.
     
    Balog Pal, Sep 13, 2010
    #3
  4. Goran Pusic

    Goran Pusic Guest

    On Sep 13, 11:17 pm, "Balog Pal" <> wrote:
    > "Goran Pusic" <>
    >
    >
    >
    > > In fact, in many cases, the resources at not acquired at
    > > initialization time but later on during object lifetime.
    > >(Warning: I am off-exact-topic).
    > >These cases are wrong most of the time.

    >
    > Are they now?
    >
    > >You are talking about two-phase initialization.

    >
    > 2-phase init is definitely a 'school' that lingers on, especially because
    > many homegrown coding standards prohibit exceptions.


    And yet, they are blissfully using new without ever wondering what
    that does to their exception safety (last time i checked, e.g. a well-
    established framework Qt did that). But fair enough, that is
    tangential, and might not be true on some other code either.

    What is more to the point: I believe, those who prohibit exceptions,
    most of the time did not think that decision through. In C++, you can
    only forbid exceptions if:

    1. you don't use (or completely isolate in try/catch blocks) standard C
    ++ library, and many other-a-library;
    2. you rig new to do something else on failure except throwing (e.g.
    terminate)
    3. you forbid copy construction (because that typically throws)
    4. you forbid any operator overloading if it might throw (minor point
    5. ... (can't think of more, but isn't the above enough?)

    First three points are really constraining, so much so, that they are
    IMO just poor programming.

    > Even the traditional 2-phase init has its good use cases, especially if you
    > can chose it.   See MFC's CFile. You have both  ctor that opens file
    > immediately from passed info and throws on error.  And another that just
    > creates the object and you can Open() later.  And can reuse it too, after
    > close.


    Yes. My contention is: this is incidental complexity that serves not
    enough purpose. E.g. why would you want to "reuse" CFile object? It's
    infinitely more expensive to actually open a file on the file system
    than to construct a C++ object. There has to be a rather particular
    use-case (IMHO) to warrant two-phase construction.

    And indeed, there are stream class hierarchies in other frameworks
    (VCL of Borland, Java, .NET), containing file-backed streams, that
    have no separate "open", and I don't remember people complaining about
    any issues.

    Goran.
     
    Goran Pusic, Sep 14, 2010
    #4
  5. Goran Pusic

    Martin B. Guest

    On 14.09.2010 09:54, Goran Pusic wrote:
    > On Sep 13, 11:17 pm, "Balog Pal"<> wrote:
    >> "Goran Pusic"<>

    >[...]
    >> Even the traditional 2-phase init has its good use cases, especially if you
    >> can chose it. See MFC's CFile. You have both ctor that opens file
    >> immediately from passed info and throws on error. And another that just
    >> creates the object and you can Open() later. And can reuse it too, after
    >> close.

    >
    > Yes. My contention is: this is incidental complexity that serves not
    > enough purpose. E.g. why would you want to "reuse" CFile object? It's
    > infinitely more expensive to actually open a file on the file system
    > than to construct a C++ object. There has to be a rather particular
    > use-case (IMHO) to warrant two-phase construction.
    >


    An (open) file object neccessarily needs to provide two-phase
    construction. Because quite often failing to open the file is *not* an
    error and so I don't want an exception, I just want the error reported
    to my immediate code.

    Reusing a file object: log file object that should be closed when you
    are not currently writing a line to it.

    br,
    Martin
     
    Martin B., Sep 14, 2010
    #5
  6. Goran Pusic

    Goran Pusic Guest

    On Sep 14, 11:18 am, "Martin B." <> wrote:
    > On 14.09.2010 09:54, Goran Pusic wrote:
    >
    > > On Sep 13, 11:17 pm, "Balog Pal"<>  wrote:
    > >> "Goran Pusic"<>

    > >[...]
    > >> Even the traditional 2-phase init has its good use cases, especially if you
    > >> can chose it.   See MFC's CFile. You have both  ctor that opens file
    > >> immediately from passed info and throws on error.  And another that just
    > >> creates the object and you can Open() later.  And can reuse it too, after
    > >> close.

    >
    > > Yes. My contention is: this is incidental complexity that serves not
    > > enough purpose. E.g. why would you want to "reuse" CFile object? It's
    > > infinitely more expensive to actually open a file on the file system
    > > than to construct a C++ object. There has to be a rather particular
    > > use-case (IMHO) to warrant two-phase construction.

    >
    > An (open) file object neccessarily needs to provide two-phase
    > construction. Because quite often failing to open the file is *not* an
    > error and so I don't want an exception, I just want the error reported
    > to my immediate code.


    (Working with CFile)

    1.
    CFile f;
    if (!f.Open(params))
    return cant_open; // Incomplete. (why error? what file?). Bad code.
    use(f);

    2.
    CFile f;
    CFileException e;
    if (!f.Open(params, e))
    return error_info_form(e); // Better, but complicated.
    use(f);

    3. (correct code)
    CFile f(params);
    use(f);

    First two simply do not scale wrt complexity of error reporting. First
    off, you need at least variant 2, or else, you have crap error info.
    Second, "use" might have failures, too (file on network, second write
    fails), and these failures vary wildly. How do you plan to pass
    quality info to higher layer? If You need error info that can explain
    every source of the error, and that is _hard_.

    No 3. gives you error info to the best of CFile abilities, including
    file name, OS error code, and +/- appropriate text for _any_ error
    that might happen. And if, by any chance, block that uses the file
    must be a no-throw zone, even if you use Open, you still have to use
    try-catch. And even the calling code is better off expecting an
    exception (because one is possible anyhow).

    > Reusing a file object: log file object that should be closed when you
    > are not currently writing a line to it.


    Who cares? You need to have file name anyhow. So from there, you go:

    CFile(params).write(something);

    This is easier than having an unusable CFile that lurks somewhere and
    repeatedly calling open close, and whatnot. Any performance hit on
    creating CFile object is easily swamped by the actual OS and disk
    work. And finally, since we're talking logging, chances are that you
    need this to be a no-throw zone. There, again, you need a big try/
    catch around all file operations, so you just can't gain anything.

    There is a place, though, where you could improve (MFC specific): you
    often want to pass CString to ctor, not LPCTSTR (to avoid touching
    heap and string copy that is otherwise hiding behind).

    IOW, when done properly, bar IWannaGoFasterday, exceptions beat error-
    return any day of the week ( with added bonus of absence of 2-phase
    init ;-) ).

    Goran.
     
    Goran Pusic, Sep 14, 2010
    #6
  7. Goran Pusic

    gwowen Guest

    On Sep 14, 1:22 pm, Goran Pusic <> wrote:
    > 3. (correct code)
    > CFile f(params);
    > use(f);


    // Sometimes the inability to open a file can be handled locally
    // What about this use case?

    static const char* filelist[3] =
    {"file1.txt","file2.txt","file3.txt"};

    ifstream foo;
    for(int idx=0;idx<3;++idx){
    foo.open(filelist[idx],ifstream::in)
    if(foo.isopen()) {
    // frobnicate file contents
    } else {
    std::cerr << filelist[idx] << " cannot be opened" << std::endl;
    }
    }

    // versus
    static const char* filelist[3] =
    {"file1.txt","file2.txt","file3.txt"};
    for(int idx=0;idx<3;++idx){
    try {
    ifstream(filelist[idx],ifstream::in)
    // frobnicate file contents
    }
    catch(std::exception&e) {
    std::cerr << filelist[idx] << " " << e.what() << std::endl;
    }
    }

    // Consider writing a Unix CLI program that takes a list a of files
    // on which to operate as arguments (like 'ls', or 'rm' or
    // other obscurities). Would you *really* write that using
    exceptions
    // to catch non-existent files?
     
    gwowen, Sep 14, 2010
    #7
  8. Goran Pusic

    Balog Pal Guest

    "Goran Pusic" <>

    >> Even the traditional 2-phase init has its good use cases, especially if
    >> you
    >> can chose it. See MFC's CFile. You have both ctor that opens file
    >> immediately from passed info and throws on error. And another that just
    >> creates the object and you can Open() later. And can reuse it too, after
    >> close.


    >Yes. My contention is: this is incidental complexity that serves not
    >enough purpose.


    C++ is traditionally multi-paradigm, so you are allowed to follow your
    attitude, but leave others to have their ways too.

    >E.g. why would you want to "reuse" CFile object?


    For example to close the log file every 24 (or 1 or whatever) hours and open
    another. Or when it gets idle.

    >It's
    >infinitely more expensive to actually open a file on the file system
    >than to construct a C++ object. There has to be a rather particular
    >use-case (IMHO) to warrant two-phase construction.


    This is not multiphace contruction, but a separate use case. Or better put,
    mutable state of your object during its lifetime.

    Your logic could go out to the mutable/immutable class concepts. Both are
    valuable, and have their good use. Trying to talk down one because the
    other is useful hardly helps.

    >And indeed, there are stream class hierarchies in other frameworks
    >(VCL of Borland, Java, .NET), containing file-backed streams, that
    >have no separate "open", and I don't remember people complaining about
    >any issues.


    People are not complaining because whenever that interface is bad for their
    use approach they pick some other tool and move on.

    In a deal of cases in my program I chose not to implement 'clear()' like
    facility in a class, rather have it as smart_ptr<T>, and reset( new
    T(...) ). Especially if that member is tied to some specific states, and is
    reset(0) on transition to others.

    Using indirection and smart pointers can cover up for some narrowed
    interfaces, like missing copy or missing ability to a delayed init.

    It's a up to judgement which variant have more complexity -- I'd rather just
    skip it ;-)



    Goran.
     
    Balog Pal, Sep 15, 2010
    #8
  9. Goran Pusic

    Balog Pal Guest

    "Goran Pusic" <>

    <quote>
    (Working with CFile)

    1.
    CFile f;
    if (!f.Open(params))
    return cant_open; // Incomplete. (why error? what file?). Bad code.
    use(f);

    2.
    CFile f;
    CFileException e;
    if (!f.Open(params, e))
    return error_info_form(e); // Better, but complicated.
    use(f);

    3. (correct code)
    CFile f(params);
    use(f);
    </quote>

    Certainly all the snippets are correct. The last one is just your
    preference.

    >First two simply do not scale wrt complexity of error reporting.


    Come on. :) Exceptions are king when you report far upwards. They kinda
    suck if you must try{} -wrap a single line. As a rule of thumb I'd say if
    you have more than a few hits on 'try' there are problems with something's
    design.

    >First
    >off, you need at least variant 2, or else, you have crap error info.
    >Second, "use" might have failures, too (file on network, second write
    >fails), and these failures vary wildly. How do you plan to pass
    >quality info to higher layer? If You need error info that can explain
    >every source of the error, and that is _hard_.


    My experience and practice is quite different.
    I use the throwing open when the file is expected to be there and
    openable -- it was configured, or discovered by a directory scan, or just
    written by another component.

    I use the nonthrowing form after FileOpenDialog analog, where the user may
    provide whatever crap. Detailed error info is rarely needed, the most basic
    message box is okay.

    >No 3. gives you error info to the best of CFile abilities, including
    >file name, OS error code, and +/- appropriate text for _any_ error
    >that might happen.


    And No 2 gives the very same info -- and you can even throw it after some
    more work, if you like (say you offered the user to pick again with ability
    to abandon).

    You can assemble your use case in a sensible way without cluttering the
    program. The File wrappers i wrote always had both throwing and
    nonthrowing variants, and all had their uses. For example ReadBytes that
    expect to successfully read the requested amount from a formatted file or
    throw -- and the usual Read that may read less and report the amount.
    Open is also may or may not be expected to succeed normatively, and I
    generally reserve exceptions to actual problems. Keeping most functions free
    of try {} blocks and be transparent.

    >And if, by any chance, block that uses the file
    >must be a no-throw zone, even if you use Open, you still have to use
    >try-catch.


    Didn't we start discussion you claiming less complexity? Indeed you can --
    and it's like dropping a piano on one's head. Even in friendly cases.
    Actually throwing ctors are not so easy to work around with try in all cases
    due to forced blocks, and if you have multiple objects I better not even
    start thinking on the resulting code. ;-)

    > And even the calling code is better off expecting an
    > exception (because one is possible anyhow).


    That depends on the contract, and what uyou expect to recover from locally.


    >> Reusing a file object: log file object that should be closed when you
    >> are not currently writing a line to it.


    >Who cares? You need to have file name anyhow. So from there, you go:


    >CFile(params).write(something);


    >This is easier than having an unusable CFile that lurks somewhere and
    >repeatedly calling open close, and whatnot.


    Maybe easier for your practice, and not easier for other people's. How
    about not playing Procrustes?

    >There is a place, though, where you could improve (MFC specific): you
    >often want to pass CString to ctor, not LPCTSTR (to avoid touching
    >heap and string copy that is otherwise hiding behind).


    You what? CFile (and btw most MFC) uses LPCSTR in most interface from 1.0.
    (switching to LPCTSTR somewhere past 4.2). That works through implicit
    conversion from CString. You certainly do not want to create extra CStrings
    if have the filename as literal. And even if CFile may create some
    CString within its black box, you have no chance to avoid that.
     
    Balog Pal, Sep 15, 2010
    #9
  10. Goran Pusic

    Goran Pusic Guest

    On Sep 15, 7:09 am, "Balog Pal" <> wrote:
    > "Goran Pusic" <>
    >
    > <quote>
    > (Working with CFile)
    >
    > 1.
    > CFile f;
    > if (!f.Open(params))
    >   return cant_open; // Incomplete. (why error? what file?). Bad code.
    > use(f);
    >
    > 2.
    > CFile f;
    > CFileException e;
    > if (!f.Open(params, e))
    >   return error_info_form(e); // Better, but complicated.
    > use(f);
    >
    > 3. (correct code)
    > CFile f(params);
    > use(f);
    > </quote>
    >
    > Certainly all the snippets are correct. The last one is just your
    > preference.
    >
    > >First two simply do not scale wrt complexity of error reporting.

    >
    > Come on. :)  Exceptions are king when you report far upwards. They kinda
    > suck if you must try{} -wrap a single line.  As a rule of thumb I'd say if
    > you have more than a few hits on 'try' there are problems with something's
    > design.
    >
    > >First
    > >off, you need at least variant 2, or else, you have crap error info.
    > >Second, "use" might have failures, too (file on network, second write
    > >fails), and these failures vary wildly. How do you plan to pass
    > >quality info to higher layer? If You need error info that can explain
    > >every source of the error, and that is _hard_.

    >
    > My experience and practice is quite different.
    > I use the throwing open when the file is expected to be there and
    > openable -- it was configured, or discovered by a directory scan, or just
    > written by another component.
    >
    > I use the nonthrowing form after FileOpenDialog analog, where the user may
    > provide whatever crap. Detailed error info is rarely needed, the most basic
    > message box is okay.


    Ah. I believe this is not good enough. The problem is IMO two fold:
    1. people are helpless; depending on the quality of the info, they
    might, or might not understand what program says. So they will call
    support, or ask questions. At that point, person who tries to answer
    _will_ need exact error info, not "could not open file".
    2. even if user knows a thing or two about his software/computer,
    strange things _will_ happen. When they do, having as precise info as
    possible is king.

    When resources allow, I'd go for "show user-friendly error to user,
    but also log exact error for support".

    > >No 3. gives you error info to the best of CFile abilities, including
    > >file name, OS error code, and +/- appropriate text for _any_ error
    > >that might happen.

    >
    > And No 2 gives the very same info -- and you can even throw it after some
    > more work, if you like (say you offered the user to pick again with ability
    > to abandon).


    Agreed, but IMO always at the expense of more work. Try a use case,
    I'm pretty sure you can't make it significantly easier either way.

    > You can assemble your use case in a sensible way without cluttering the
    > program.    The File wrappers i wrote always had both throwing and
    > nonthrowing variants, and all had their uses.  For example ReadBytes that
    > expect to successfully read the requested amount from a formatted file or
    > throw -- and the usual Read that may read less and report the amount.
    > Open is also may or may not be expected to succeed normatively, and I
    > generally reserve exceptions to actual problems. Keeping most functions free
    > of try {} blocks and be transparent.
    >
    > >And if, by any chance, block that uses the file
    > >must be a no-throw zone, even if you use Open, you still have to use
    > >try-catch.

    >
    > Didn't we start discussion you claiming less complexity?  Indeed you can --  
    > and it's like dropping a piano on one's head.   Even in friendly cases.
    > Actually throwing ctors are not so easy to work around with try in all cases
    > due to forced blocks, and if you have multiple objects I better not even
    > start thinking on the resulting code. ;-)


    Hmmm... In my experience, throwing ctors are quite easy to work with.
    What is essential, though, is that any exceptions are precise enough,
    so that you can either report them to user, either use them
    programmatically for further action.

    > > And even the calling code is better off expecting an
    > > exception (because one is possible anyhow).

    >
    > That depends on the contract, and what uyou expect to recover from locally.
    >
    > >> Reusing a file object: log file object that should be closed when you
    > >> are not currently writing a line to it.

    > >Who cares? You need to have file name anyhow. So from there, you go:
    > >CFile(params).write(something);
    > >This is easier than having an unusable CFile that lurks somewhere and
    > >repeatedly calling open close, and whatnot.

    >
    > Maybe easier for your practice, and not easier for other people's.  How
    > about not playing Procrustes?


    That wasn't my intention at all. Look, please explain how is:

    class whatever
    {
    CFile f;
    CString name;
    write(params)
    {
    if (!f.Open(name))
    handle error; // return here? throw? wha'ever...
    f.write(params); // what about errors here?
    f.close();
    }
    };

    better than

    class whatever
    {
    CString name;
    write(params)
    {
    CFile(name).write(params);
    }
    };

    ?

    My contention is: you can change use-case which ever way you want,
    using CFile, you can't get this to be more simple, not for the code in
    question and not for the caller, either. Let's try it.

    >
    > >There is a place, though, where you could improve (MFC specific): you
    > >often want to pass CString to ctor, not LPCTSTR (to avoid touching
    > >heap and string copy that is otherwise hiding behind).

    >
    > You what?  CFile (and btw most MFC) uses  LPCSTR in most interface from 1.0.
    > (switching to LPCTSTR somewhere past 4.2). That works through implicit
    > conversion from CString.  You certainly do not want to create extra CStrings
    > if have the filename as literal.     And even if CFile may create some
    > CString within its black box, you have no chance to avoid that.


    No, what you are saying is simply false in practice. Indeed, implicit
    conversion is there. When you do have a string literal, CFile will
    turn it into a CString on Open (that hits allocation and copying; yes,
    CFile has a CString within it's black box). If you already have a
    CString, it goes through LPCTSTR conversion and hits allocation -
    again. But if there was a const CString& variant of Open, then,
    because CString uses COW, allocation would have been avoided.

    That stems from a more general notion WRT CString of MFC: const
    CString& is preferable to LPCTSTR, and that, because in MFC code, a
    LPCTSTR param is most likely backed by a CString, on either calling or
    call side. If so, use of const CString& lowers string copying and
    allocation.

    In MFC, LPCTSTR should be used only when you already have a literal,
    and you are sure that __none__ of the code down-stream will turn it
    into a CString. That's a tall order, hence const CString& is choice no
    1.

    Don't believe me. If you do use LPCTSTR in your interfaces, count
    allocations and copies when you pass LPCTSTR across calls, change to
    const CString& and count again. You'll be surprised.

    Goran.
     
    Goran Pusic, Sep 15, 2010
    #10
  11. Goran Pusic

    Goran Pusic Guest

    On Sep 15, 6:27 am, "Balog Pal" <> wrote:
    > "Goran Pusic" <>
    >
    > >> Even the traditional 2-phase init has its good use cases, especially if
    > >> you
    > >> can chose it. See MFC's CFile. You have both ctor that opens file
    > >> immediately from passed info and throws on error. And another that just
    > >> creates the object and you can Open() later. And can reuse it too, after
    > >> close.

    > >Yes. My contention is: this is incidental complexity that serves not
    > >enough purpose.

    >
    > C++ is traditionally multi-paradigm, so you are allowed to follow your
    > attitude, but leave others to have their ways too.
    >
    > >E.g. why would you want to "reuse" CFile object?

    >
    > For example to close the log file every 24 (or 1 or whatever) hours and open
    > another.  Or when it gets idle.


    Well, for that, you can just create a new one without any measurable
    performance hit. Look:

    class whatever
    {
    std::auto_ptr<CFile> file;
    CString name;
    void reopen()
    {
    file = std::auto_ptr<new CFile(name)>;
    }
    };

    Above beats e.g.

    class whatever
    {
    CFile file;
    CString name;
    void reopen()
    {
    file.Close();
    if (!file.Open(name))
    handle error;
    }
    };

    > >It's
    > >infinitely more expensive to actually open a file on the file system
    > >than to construct a C++ object. There has to be a rather particular
    > >use-case (IMHO) to warrant two-phase construction.

    >
    > This is not multiphace contruction, but a separate use case. Or better put,
    > mutable state of your object during its lifetime.
    >
    > Your logic could go out to the mutable/immutable class concepts.  Both are
    > valuable, and have their good use.   Trying to talk down one because the
    > other is useful hardly helps.
    >
    > >And indeed, there are stream class hierarchies in other frameworks
    > >(VCL of Borland, Java, .NET), containing file-backed streams, that
    > >have no separate "open", and I don't remember people complaining about
    > >any issues.

    >
    > People are not complaining because whenever that interface is bad for their
    > use approach they pick some other tool and move on.


    OTOH, Java or .NET are not going away...

    Goran.
     
    Goran Pusic, Sep 15, 2010
    #11
  12. Goran Pusic

    ptyxs Guest

    On Sep 15, 9:51 am, Goran Pusic <> wrote:
    > On Sep 15, 6:27 am, "Balog Pal" <> wrote:
    >
    >
    >
    > > "Goran Pusic" <>

    >
    > > >> Even the traditional 2-phase init has its good use cases, especially if
    > > >> you
    > > >> can chose it. See MFC's CFile. You have both ctor that opens file
    > > >> immediately from passed info and throws on error. And another that just
    > > >> creates the object and you can Open() later. And can reuse it too, after
    > > >> close.
    > > >Yes. My contention is: this is incidental complexity that serves not
    > > >enough purpose.

    >
    > > C++ is traditionally multi-paradigm, so you are allowed to follow your
    > > attitude, but leave others to have their ways too.

    >
    > > >E.g. why would you want to "reuse" CFile object?

    >
    > > For example to close the log file every 24 (or 1 or whatever) hours and open
    > > another.  Or when it gets idle.

    >
    > Well, for that, you can just create a new one without any measurable
    > performance hit. Look:
    >
    > class whatever
    > {
    >  std::auto_ptr<CFile> file;
    >  CString name;
    >  void reopen()
    >  {
    >   file = std::auto_ptr<new CFile(name)>;
    >  }
    >
    > };
    >
    > Above beats e.g.
    >
    > class whatever
    > {
    >  CFile file;
    >  CString name;
    >  void reopen()
    >  {
    >   file.Close();
    >   if (!file.Open(name))
    >    handle error;
    >  }
    >
    >
    >
    > };
    > > >It's
    > > >infinitely more expensive to actually open a file on the file system
    > > >than to construct a C++ object. There has to be a rather particular
    > > >use-case (IMHO) to warrant two-phase construction.

    >
    > > This is not multiphace contruction, but a separate use case. Or better put,
    > > mutable state of your object during its lifetime.

    >
    > > Your logic could go out to the mutable/immutable class concepts.  Both are
    > > valuable, and have their good use.   Trying to talk down one because the
    > > other is useful hardly helps.

    >
    > > >And indeed, there are stream class hierarchies in other frameworks
    > > >(VCL of Borland, Java, .NET), containing file-backed streams, that
    > > >have no separate "open", and I don't remember people complaining about
    > > >any issues.

    >
    > > People are not complaining because whenever that interface is bad for their
    > > use approach they pick some other tool and move on.

    >
    > OTOH, Java or .NET are not going away...
    >
    > Goran.


    As I understand it "RAII", id est 'resource acquisition is
    initialization" is quite simple and quite logical. It means : "In C++
    you must acquire resources by initializing an object which
    encapsulates these resources : as a consequence when the object goes
    out of scope the resources are automatically released.
    Extremely simple non ?
     
    ptyxs, Sep 16, 2010
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Apricot
    Replies:
    4
    Views:
    563
    velthuijsen
    Apr 16, 2004
  2. Mr. SweatyFinger
    Replies:
    2
    Views:
    2,261
    Smokey Grindel
    Dec 2, 2006
  3. S_K
    Replies:
    6
    Views:
    1,256
    Robert Dunlop
    Nov 8, 2007
  4. Johannes Schaub (litb)

    Re: Why is RAII called RAII?

    Johannes Schaub (litb), Sep 12, 2010, in forum: C++
    Replies:
    2
    Views:
    430
    James Kanze
    Sep 18, 2010
  5. cpp4ever

    Re: Why is RAII called RAII?

    cpp4ever, Sep 12, 2010, in forum: C++
    Replies:
    1
    Views:
    431
    BGB / cr88192
    Sep 13, 2010
Loading...

Share This Page