[Boost-users] [multi_array] Everyone, please make your value-types Assignable! (was: Re: more on assignability)

On Jul 17, 2008, at 3:19 PM, Ronald Garcia wrote:
Let's define some stuff here: T: some type you created, probably a class type a: an object of type T; it's valid, i.e. meets its invariants b: another object of type T, also valid; any similarities or differences in its state from a's state is unspecified C: the set of all valid states an object of type T may have Let function F: x -> Y, map a T object state x to a (sub)set of C named Y. This function returns the subset of source states that a given state can receive during an assignment. If there exists at least one x in C such that F(x) = Y < C, then type T is _NOT_ Assignable! In other words, the number of assignment- compatibility classes in T must be exactly one for T to be Assignable. Furthermore, an Assignable type requires that the destination object's observable state post-assignment matches the observable state of the source object. The destination's new state can't be the old state quasi-merged with the source's state. (The destination object's old state must appear to be splattered, but remnants could be cached to the new state.) Fortunately, most types are Assignable. What happens if your type isn't? 1. Users can't use objects of that type in standard containers, which assume to be able to freely copy objects as much as they want. 2. Such objects are a pain to work with. Any containing class must write custom assignment operators to add calls to your custom copying routine. Of course, they have to first check if the source and destination objects are in the same assignment-compatibility class, and reconfigure at least one of those two objects if they're not in the same class. The exception would be if the wrapping class always makes sure that every T object it creates is of the same class. Even that is a pain because the user has to manually confirm that invariant every time s/he changes or adds a mutating function. 3. It isn't safe. You are effectively dumping the responsibility of assignment onto your users, who won't have all the information. The user generally has to test compatibility, reconfigure at least one object, then do call the custom copying routine. These steps are distinct, so if one of the mutating steps throws, the user will probably lose his/her old state forever and have a default-valued object. Your assignment routine would have more information available, so it can structure the assignment with roll-back during throws. (This could be moderated with a swap routine that doesn't care about assignment-compatibility classes. You do have a custom swap, right? And it does work irrespective of class, right?!) -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

on Thu Jul 17 2008, Daryle Walker <darylew-AT-hotmail.com> wrote:
Then, if I understand you correctly, none of the built-in types are Assignable. char* p; // p is unintialized char* q = p; // invalid Yes, uninitialized is one of the valid states for a builtin type, i.e. part of the type's invariants. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Jul 17, 2008, at 10:32 PM, David Abrahams wrote:
Really, I was wondering about that (corner) case, especially since it can't be replicated (i.e. it's undefined to use such a state as a source). I'm thinking more about non-POD class types, which must have an initial state with the internal primitive objects initialized. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

On Jul 18, 2008, at 5:07 AM, Daryle Walker wrote:
Well, I looked into it further. In C++ 2003, section 4.1 "Lvalue-to- rvalue conversion" [conv.lval], paragraph 1, an uninitialized object can only be used as an lvalue, converting it to a rvalue is undefined behavior. This means that your program is illegitimate and we can't count it as a counter-example. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

on Fri Jul 18 2008, Daryle Walker <darylew-AT-hotmail.com> wrote:
Yes, that's what "// invalid" means.
This means that your program is illegitimate and we can't count it as a counter-example.
Huh? By that logic no counterexample is possible. Or am I missing something? -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Jul 18, 2008, at 6:57 AM, David Abrahams wrote:
Yes, the OP was having a problem with a boost class (template) that is like std::valarray: you can only do assignments if the source and destination objects _already_ had the same layout. And my first response to this thread explained why this is problematic for our users, and so we shouldn't do this. In this setup, all objects are validly initialized; it's just there isn't a single assignment- compatibility class that all valid object states belong to. Note that you have to go out of your way to create a class like this. It's a pain because the user who wants to use this class internally has either make sure all wrapping objects keep all sub-objects of this type within the same assignment-compatibility class or write a custom assignment routine. Said routine can be at most the basic guarantee if the resizing or copy steps may throw. (A strong guarantee could be done if the type provides a swap that can cross assignment-compatibility classes.) An author-supplied full- assignment routine could take advantage of the internal implementation and add rollback. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

-- Dave Abrahams Boostpro Computing http://boostpro.com On Jul 18, 2008, at 7:30 AM, Daryle Walker <darylew@hotmail.com> wrote:
I understand that
And my first response to this thread explained why this is problematic for our users,
I understand that too. My problem is with your implicit declaration that such types are not to be considered Assignable. That concept is supposed to be compatible with the builtins.
Not really. It's pretty easy to end up with uninitialized members. But that's really beside the point.
So? I don't see how that's related or why it's a problem

On Jul 18, 2008, at 8:03 AM, David Abrahams wrote:
My use of "Assignable" was more colloquial, and not one of the official meanings in various C++ literature. What does practical assignability mean? It means that any object of a certain type can be either initialized by or receive an assignment from any other valid object of that type (Uninitialized PODs can't count because a program that uses one as an r-value is illegal.) and the destination object becomes observationally identical to the source object. If an object only lets certain objects of the same type be allowed to be assignment-source objects, then its utility becomes a lot lower because the user has to add more checks on his/her code. It might be OK for a mixed-operation-assignment to discriminate (with either an assertion or exception) because there's no way to match two objects with incompatible parameters for the desired operation. But the regular copy-assignment is different. It has a special place in the C++ type/object model, so much so that said operator is automatically defined if it's not explicitly provided. Anything that messes up the assumptions that C++ has about regular assignment, like multiple assignability classes or non-splatter semantics, cascades until an ugliness-hiding wrapper is made.
It's an example of the hoops a user has to do if they need to "correct" the assignment on their end. The problem is that the user's code doesn't have access to the inside information needed to increase the safety guarantee's strength. If the author provided the assignment, s/he could have used the inside information to strengthen the safety guarantee.
-- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

I think there are two separate notions being discussed here - First is the notion of a partial function - assignment and copy are both partial functions and have a precondition that the rvalue be in a valid state. The second is the notion of a runtime type where once the type is determined it is fixed and assignment becomes defined only on items of the same type. I tend to shy away from implementing such types and usually prefer to document that assignment won't throw (perhaps unless some subtype throws) if the two objects are of the same runtime type (or have the same topology - or whatever a correct term would be for the circumstance). There are cases where I think the notion of a runtime type is valid even when it makes assignment more partial than it would be otherwise - being partial doesn't effect the concept at all - but such a design choice should be weighed carefully. Sean

On Jul 20, 2008, at 1:46 AM, Sean Parent wrote:
This is the case that Dave brought up. I think it shouldn't be the main concern because: (1) non-PODs are generally always in a valid state when created and (2) it's already illegal to use a not-(fully-) initialized (POD) object as an r-value. My main concern was what happens when a type's designer divides that type's set of valid states into multiple "runtime types" (as you say below), but the user wants to be more flexible with cross- compatibility than the designer.
The problem comes from regular (copy-)assignment being considered a special function. Library code makes assumptions that don't work if assignments aren't free-for-all. User code that wraps runtime types have to either add an invariant that all uses of that type are of the same assignment-compatibility class or do appropriate reconfigurations (or give up with an assertion or exception) when two objects of different a.c. classes clash.
Yes. I think an important thing to do for such types is to make sure that swap stays never-fail, like destructors and deallocators, by making sure it works even when the two objects are of different a.c. classes. That way: my_class & fake_assign( my_class &dest, my_class const &sour ) { my_class copy( sour ); copy.swap( dest ); // even if either "copy=dest" or "dest=copy" would fail return dest; } anyone who needs to assign across a.c. classes can work-around it and still have the option of the strong guarantee. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com
participants (3)
-
Daryle Walker
-
David Abrahams
-
Sean Parent