Is default behavior still desired in this case?

Discussion in 'C++' started by coal, Dec 24, 2007.

  1. coal

    coal Guest

    Previously I've suggested permitting some modification to the
    default behaviour wrt to exceptions thrown in a constructor.

    http://preview.tinyurl.com/278u77

    My guess is the default behaviour was decided when networking
    was less prevalent and should be reconsidered now.

    I'm not sure if the syntax/mechanism I suggested is good.

    If you have

    class Base { ...};
    class Inter : public Base { ...};
    class Derived : public Inter { ... };

    and

    Base* b = new (preserve) Derived(...);

    and an exception comes from Derived's constructor, b would be
    set to the Inter object since that much was built successfully.

    I'm not sure if/how the syntax would fit in with placement new.

    The rationale for preserving a base object is because the sender,
    network and receiver have invested quite a bit in getting to the
    point of having an Inter. Throwing everything away doesn't make
    sense to me.

    Brian Wood
    Ebenezer Enterprises
    www.webebenezer.net
     
    coal, Dec 24, 2007
    #1
    1. Advertisements

  2. coal

    johanatan Guest

    But, what happens to the actual exception? Do the semantics of
    'preserve' imply that the exception is eaten and unavailable to the
    calling code? That would not seem to be very informative as you would
    most surely still want to have the option of fixing the problem if
    possible and trying again only the part that failed.
     
    johanatan, Dec 24, 2007
    #2
    1. Advertisements

  3. What is wrong with doing it manually, adding new keywords and changing
    syntax just to make some rare cases does not seem like a very good idea
    to me.

    Base* b;
    try {
    b = new Derived(...);
    }
    catch(SomeException& e)
    {
    b = new Inter(...);
    }
     
    Erik Wikström, Dec 25, 2007
    #3
  4. coal

    coal Guest

    I think it would be propagated as usual. The caller could
    determine if the pointer was to anything useful.
    The assumption I'm making is it isn't desirable to have to have
    large buffers to support retries. One of the things discussed
    in the thread I mentioned was a maximum message length. The
    buffers might have to be as big as the max msg length if you
    don't have something like preserve. I don't think that is the
    size you would choose for the buffers if they didn't need to be
    available for retries.

    Brian Wood
    Ebenezer Enterprises
     
    coal, Dec 26, 2007
    #4
  5. coal

    coal Guest

    I think it forces you to use inefficient buffer sizes. What you wrote
    above would work if you have everything needed in a buffer, but it's
    not as efficient as using something like preserve since the subobjects
    would be destroyed and then rebuilt. Also, it isn't as flexible; if
    the hierarchy were

    Base
    Inter1 : Base
    Inter2 : Inter1
    Derived : Inter2

    you'd have to beef up the above code. What we want is for the
    system to give the closest object it can to what was requested.

    You don't know when you write code like the above if things will
    sometimes mess up in Inter2's construction. In order to get to
    an Inter1 you would wind up destroying an Inter1 twice and building
    it three times. (That is assuming the same problem persists
    between the attempt to build a Derived and an Inter2.)

    Brian Wood
    Ebenezer Enterprises
     
    coal, Dec 26, 2007
    #5
  6. coal

    johanatan Guest

    I don't know enough about what your classes are actually doing (or the
    inspiration for this thread), but if you are talking about something
    as specific as buffer sizes as the rationale for a grammar change,
    then surely there is another way to design this sub-system even within
    the confines of the current grammar. What about re-ordering the data
    stream? Let objects be serialized with their most-derived instance
    data specified first (and thus you would know that all of its
    ancestors should be able to be constructed). (You could prepend
    lengths as another idea or in combination with the first).

    If I'm way off base here, can you give a little more bkg on what
    exactly these proposed classes are doing? -or- if not, I'd just
    suggest to 'think outside the box' :) --there's surely a way to do it
    without a new 'preserve' mechanism.

    (Another idea just occurred to me (but less elegant)-- turn your 'is-
    a' relationships into 'has-a' relationships and let the objs construct
    each other when needed, if you can't re-order the stream)-- yes, this
    one's a hack, but it is at least possible.

    --Jonathan
     
    johanatan, Dec 28, 2007
    #6
  7. coal

    coal Guest

    I don't have a specific problem here. I agree buffer sizes
    are specific, but I don't know of a better way than what I
    suggested. What Erik wrote is an alternative, but I think
    it has some weaknesses. If the maximum message size were
    20 megabytes I think the buffers would have to be that big
    in order to guarantee support for what he suggested.
    Buffer sizes aren't the only thing; you also wind up
    destroying and rebuilding multiple times.

    I'm not suggesting that the syntax I proposed is how it
    should be. There may be better ways to handle it.
    Do you mean default constructed?
    I guess you're saying something like

    Derived dv; // default construction
    serialize into dv

    A problem with the serialization could still occur.
    If that happens you're left with a Derived instance
    in a probably inconsistent state. I would like to
    have a valid subojbect if I can't get a valid Derived
    object.

    Also from that previous thread I mentioned, I'm thinking
    about there being a stream/serialization constructor...

    class Derived {
    public:
    Derived(handle stream) {}

    };

    Using that is more efficient than default constructing
    and then serializing. I don't know if anyone is doing
    that yet.

    Brian Wood
    Ebenezer Enterprises
     
    coal, Dec 28, 2007
    #7
  8. coal

    johanatan Guest

    Well, not necessarily. You could 'peek' into the stream and see
    prepended useful information so that you could do (in pseudocode):

    while (stream >> type)
    {
    obj o;
    switch (type)
    {
    case derived:
    o = Derived( stream );
    break;
    case base:
    o = Base( stream );
    break;
    case ...:
    ...
    ...
    }
    }

    In general, always prepend block sizes (if the buffer size is a
    concern) or the obj type before the actual guts of the obj.
    Why would a problem occur? Aren't you using TCP/IP? You should be
    able with modern protocols to be assured at the application level that
    you receive what was sent. If that isn't true, then you could always
    invest in a better network stack of some sort (even if it means hand-
    rolled middleware) to ensure that at the application level this is
    true.
    See above for example that doesn't use default constructing. Though,
    the perf difference between default constructing and specialized
    constructing would be minimal (but if it really matters, I guess it
    really matters).

    --Jonathan
     
    johanatan, Dec 30, 2007
    #8
  9. coal

    coal Guest

    We already do something very similar -
    http://preview.tinyurl.com/yro2ep
    I'm working on that. The thread I mentioned in the OP
    convinced me it was necessary.

    The above is interesting but I don't see how it addresses
    what I want to do. Given the above pseudo code, how do
    you retrieve the Inter instance if the line
    o = Derived( stream );

    throws an exception in Derived's constructor?
    One problem would be failure to get needed memory
    when data is being received/unmarshalled in an object.

    Aren't you using TCP/IP?

    Yes.
    I did a few tests and they didn't make me very confident
    about my point. Perhaps you're right.

    Brian Wood
    Ebenezer Enterprises
     
    coal, Dec 31, 2007
    #9
  10. coal

    johanatan Guest

    Well, my point was that, if you prepend the type and/or buffer sizes,
    and you're using a sufficiently advanced communication medium (TCP/IP)
    that guarantees that what was sent is what is actually received, then
    you can on the sending end make sure to send a well-formed object that
    will allow successful construction (on the receiving end).

    If you're concerned about attacks, then you ought to have some other
    level of end-to-end encryption or authentication mechanism in place to
    prevent that (abstracted away from the problem of actually
    constructing objects).
    Failure on the receiving end to allocate enough memory for the entire
    object? If that's happens I'd just say the entire object would be
    invalid. At the point you determine the type, you can do:

    sizeof(type)

    to see how much will be needed (that should include all base classes
    in the calculation) and if there isn't enough, then the object would
    not be constructed at all.

    If you're getting that low on memory, needing and using a partially
    constructed object would be the least of your worries, I would think.
    What about thrashing and so forth? Your entire machine is probably
    gonna be useless at the point you fill up VM.

    --Jonathan
     
    johanatan, Dec 31, 2007
    #10
  11. coal

    johanatan Guest

    P.S. Just to step back from the problem a bit: Why can't you use D-
    COM or CORBA for this? I think they both have mechanisms for
    transplanting objects (i.e., moving a 'live' object from one server to
    another and continuing its lifetime there). That's not exactly
    serialization, but it might work.
     
    johanatan, Dec 31, 2007
    #11
    1. Advertisements

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.