[unnullable_ptr] Proposal for a new library

Hello, Is there any interest in a class template which acts like a pointer and guarantees at compile time and at no CPU or memory cost (except when a call to any of its methods is not inlined) that the pointer is not null? One of the applications that I have found for this template is in defensive programming to eliminate the chances that dereferencing a pointer will result in undefined behaviour (when the pointer is null). Using this template is superior to any alternative method (that I know) to achieve the same result, since: (*) it consumes no CPU (in contrast to null checks); (*) the class template instances are copy constructible and copy assignable (in contrast to references which can also be used to guarantee unnullability); (*) it is natively supported by the language and provides a 'real' guarantee (in contrast to 'design by contract'); (*) supports the natural syntax for access to the wrapped resource (in constrast to boost::ref). Regards, Angel Tsankov

Angel Tsankov wrote:
Is there any interest in a class template which acts like a pointer and guarantees at compile time and at no CPU or memory cost (except when a call to any of its methods is not inlined) that the pointer is not null?
Preventing empty or invalid cases is always good. For example I think it's a shame Boost.Function, Boost.Any and the Boost Smart Pointers don't do it. As for forcing a pointer to not be empty, I suppose you're talking of making overloads of constructor and operator= for `int' and make them generate errors? Do you still provide pointer arithmetic and implicit upcasting? I personally stay away from pointers, which I associate with bad programming. boost::ref already provides a safe way to do what you want, and better IMHO. It is not required to use pointers at all with it. And you *can* get the wrapped pointer resource with get or get_pointer.

Mathias Gaunard skrev:
Angel Tsankov wrote:
Is there any interest in a class template which acts like a pointer and guarantees at compile time and at no CPU or memory cost (except when a call to any of its methods is not inlined) that the pointer is not null?
Preventing empty or invalid cases is always good. For example I think it's a shame Boost.Function, Boost.Any and the Boost Smart Pointers don't do it.
As for forcing a pointer to not be empty, I suppose you're talking of making overloads of constructor and operator= for `int' and make them generate errors?
That seems like a nice idea that I will think about applying to Boost.PtrContainer. -Thorsten

How is this different than a reference?? -----Original Message----- From: Thorsten Ottosen [mailto:thorsten.ottosen@dezide.com] Sent: Tuesday, July 22, 2008 7:31 AM To: boost@lists.boost.org Subject: Re: [boost] [unnullable_ptr] Proposal for a new library Mathias Gaunard skrev:
Angel Tsankov wrote:
Is there any interest in a class template which acts like a pointer and guarantees at compile time and at no CPU or memory cost (except when a call to any of its methods is not inlined) that the pointer is not null?
Preventing empty or invalid cases is always good. For example I think it's a shame Boost.Function, Boost.Any and the Boost Smart Pointers don't do it.
As for forcing a pointer to not be empty, I suppose you're talking of making overloads of constructor and operator= for `int' and make them generate errors?
That seems like a nice idea that I will think about applying to Boost.PtrContainer. -Thorsten Visit our website at http://www.ubs.com This message contains confidential information and is intended only for the individual named. If you are not the named addressee you should not disseminate, distribute or copy this e-mail. Please notify the sender immediately by e-mail if you have received this e-mail by mistake and delete this e-mail from your system. E-mails are not encrypted and cannot be guaranteed to be secure or error-free as information could be intercepted, corrupted, lost, destroyed, arrive late or incomplete, or contain viruses. The sender therefore does not accept liability for any errors or omissions in the contents of this message which arise as a result of e-mail transmission. If verification is required please request a hard-copy version. This message is provided for informational purposes and should not be construed as a solicitation or offer to buy or sell any securities or related financial instruments.

On Tue, Jul 22, 2008 at 07:30, Thorsten Ottosen <thorsten.ottosen@dezide.com> wrote:
Mathias Gaunard skrev:
As for forcing a pointer to not be empty, I suppose you're talking of making overloads of constructor and operator= for `int' and make them generate errors?
That seems like a nice idea that I will think about applying to Boost.PtrContainer.
I'm not convinced that this is useful. It doesn't protect against real nulls of pointer type, and if I see a failure talking about int when I want a null pointer, my first instinct would be to assume it's an overload or deduction error, and just cast it, at which point it will compile. YMMV, ~ Scott

Scott McMurray wrote:
I'm not convinced that this is useful. It doesn't protect against real nulls of pointer type
Yes, you would have to assume that whenever you're initializing from a pointer, that pointer points to a valid object. A possible alternative way to solve that problem would be to make the constructor private, only accessible by boost::addressof which would be used instead of &. Since you would apply adressof on an object (and thus moving the requirement that the object is valid to the user), you cannot end up with a null pointer. boost::ref is actually quite similar to that.
and if I see a failure talking about int when I want a null pointer, my first instinct would be to assume it's an overload or deduction error, and just cast it, at which point it will compile.
If you cast, you're dropping type safety, so you're on your own. A code that performs casting, outside of low-level coding or a few idioms like CRTCP, is a bad thing.

AMDG Mathias Gaunard wrote:
and if I see a failure talking about int when I want a null pointer, my first instinct would be to assume it's an overload or deduction error, and just cast it, at which point it will compile.
If you cast, you're dropping type safety, so you're on your own. A code that performs casting, outside of low-level coding or a few idioms like CRTCP, is a bad thing.
?? I don't see how int* ip = 0; is less dangerous than int* ip = static_cast<int*>(0); The latter is only making what the compiler already does implicitly, explicit. In Christ, Steven Watanabe

On Tue, Jul 22, 2008 at 11:52, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
If you cast, you're dropping type safety, so you're on your own. A code that performs casting, outside of low-level coding or a few idioms like CRTCP, is a bad thing.
I consider neither boost::implicit_cast<double>(some_int)/another_int to avoid integer division nor using foo(boost::implicit_cast<int*>(0)) to choose the pointer overload instead of the integral one to be "dropping type safety" or "low-level coding".

On Tue, 22 Jul 2008 05:23:48 -0600, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
Angel Tsankov wrote:
Is there any interest in a class template which acts like a pointer and guarantees at compile time and at no CPU or memory cost (except when a call to any of its methods is not inlined) that the pointer is not null?
Preventing empty or invalid cases is always good. For example I think it's a shame Boost.Function, Boost.Any and the Boost Smart Pointers don't do it.
(sorry, a bit off-topic for original proposal) Oh, no... I think the fact that Boost.Function allows storing of null pointer ("there is no function to call") is a good thing! Otherwise it will need to store some sort of "default" function that returns ... seriously, what is default function would be? Say we have Func<bool(void)> f; ... bool result = f(); what would be the result case? true or false? To disallow default constructor (no argument passes) completely will be way too limiting - it is a encapsulation of function with pointer to function sort-of behaviour, not reference to it. If you completely need to be sure that there is something to call underneath it (like to avoid thousand checks for empty value), just (wrap and) assign some dummy function. my 0.02 CAD Thanks, Andrey

Andrey Tcherepanov wrote:
Oh, no... I think the fact that Boost.Function allows storing of null pointer ("there is no function to call") is a good thing!
Well, if you like the possibility of invoking undefined behaviour, it is surely a good thing. I would rather have the type system and the exception mechanism make sure that can never happen.
Otherwise it will need to store some sort of "default" function that returns ... seriously, what is default function would be? Say we have
Func<bool(void)> f;
That line would be an error. Not allowing function to be empty means no default constructor, obviously.
To disallow default constructor (no argument passes) completely will be way too limiting
I don't really see how. If you want a function that might or might not be there, you can use optional<function<sig> >.
it is a encapsulation of function with pointer to function sort-of behaviour, not reference to it.
Yes, the reason Boost.Function behaves that way is to be consistent with function pointers. But maybe it could actually try to be better than these and provide safer guarantees.
If you completely need to be sure that there is something to call underneath it (like to avoid thousand checks for empty value), just (wrap and) assign some dummy function.
Making a wrapper is of course possible. It seems fairly weird though to make a wrapper around an optional container to make it unoptional rather than the other way around. A component should have as little features as possible to be complete, and then one is to combine components to have more refined ones with more features. Also, I don't really know how Boost.Function behaves when copy constructors inside operator= throw, but depending on how it does it it could lead to a potential empty object. It's not really trivial to ensure the never-empty guarantee when using stack allocation (SBO). Putting dummy functions is of course out of the question.

AMDG Mathias Gaunard wrote:
Andrey Tcherepanov wrote:
Oh, no... I think the fact that Boost.Function allows storing of null pointer ("there is no function to call") is a good thing!
Well, if you like the possibility of invoking undefined behaviour, it is surely a good thing. I would rather have the type system and the exception mechanism make sure that can never happen.
It isn't undefined behavior to call an empty Boost.Function It results in a bad_function_call. In Christ, Steven Watanabe

On Tue, 22 Jul 2008 11:22:06 -0600, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
Andrey Tcherepanov wrote:
Oh, no... I think the fact that Boost.Function allows storing of null pointer ("there is no function to call") is a good thing!
Well, if you like the possibility of invoking undefined behaviour, it is surely a good thing. I would rather have the type system and the exception mechanism make sure that can never happen.
bad_function_call will be thrown right now [skip]
To disallow default constructor (no argument passes) completely will be way too limiting
I don't really see how. If you want a function that might or might not be there, you can use optional<function<sig> >.
Hmm... and what will happen if I call this when it is not initialized yet?
it is a encapsulation of function with pointer to function sort-of behaviour, not reference to it.
Yes, the reason Boost.Function behaves that way is to be consistent with function pointers. But maybe it could actually try to be better than these and provide safer guarantees.
So (if I understand you correctly), you want not function-pointer behaviour, but something more like reference - have to be initialized, cannot be null? It is usable, indeed, but instead of modifying existing Function (and fix all the user code), may be it is easier to create something new, something like FunctionRef library?... Another way would be (sorry, I am bit of brainfa...storming here :) ) add a "policy" template parameter for storage, and default it so existing code will not be broken. BTW, shall it be not reassignable too? If not, assignment should work like atomic op, and I am not sure if this is possible to implement for all user specified arguments simultaneously. [some stuff skipped, but I have no comment on it]
Putting dummy functions is of course out of the question.
Hmm... working like a charm for me when I grow tired of checking emptiness of the func pointers.... Best regards, Andrey

Andrey Tcherepanov wrote:
bad_function_call will be thrown right now
I was unaware of that, but that's not much better IMO. If I can use the type system to prevent exceptional erroneous situations at runtime, that's better.
Hmm... and what will happen if I call this when it is not initialized yet?
That is supposing function<sig> cannot be empty. Using optional<function<sig> >, you get the same thing as the old behaviour. optional is made so that you can make any type optional, so why not use it when you need to achieve that need?
So (if I understand you correctly), you want not function-pointer behaviour, but something more like reference - have to be initialized, cannot be null?
Well yeah, preventing an "empty" state means that you have to initialize the thing, it cannot be null. That's what the whole thread is about.
It is usable, indeed, but instead of modifying existing Function (and fix all the user code), may be it is easier to create something new, something like FunctionRef library?...
Giving it another name or putting it in another namespace is of course required to achieve compability. The issue is that function is presented as being the right tool to use, but better is possible. Of course that is not function-specific. As I said, quite a few constructs do not force the never-empty guarantee, and it would be better they would (or alternative similar tools, making the current ones deprecated) in my opinion, since that's a safer programming paradigm. That reminds me of a discussion I read recently about iterators, which have to be default constructible. I believe it is a bad idea to require that, but Stepanov thought default-constructibility was an essential propriety of regular types.
BTW, shall it be not reassignable too?
Yes, there is no reason for that to change. If you don't want it to be reassignable, simply make it const, I guess.
If not, assignment should work like atomic op, and I am not sure if this is possible to implement for all user specified arguments simultaneously.
It should work like the current one but use techniques to make sure that the object is not empty even if an exception is raised during its execution.

AMDG Mathias Gaunard wrote:
So (if I understand you correctly), you want not function-pointer behaviour, but something more like reference - have to be initialized, cannot be null?
Well yeah, preventing an "empty" state means that you have to initialize the thing, it cannot be null. That's what the whole thread is about.
I don't think that boost::function should provide a never empty guarantee for several technical reasons: a) Providing the guarantee has a runtime cost. Allowing empty Boost.Function objects is free. b) Looking forward to C++0x, it is impossible to implement a no throw move for objects than have no empty state. c) Objects that provide default constructors are easier to work with. In Christ, Steven Watanabe

On Wed, 23 Jul 2008 16:29:14 -0700, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG
Mathias Gaunard wrote:
So (if I understand you correctly), you want not function-pointer behaviour, but something more like reference - have to be initialized, cannot be null?
Well yeah, preventing an "empty" state means that you have to initialize the thing, it cannot be null. That's what the whole thread is about.
I don't think that boost::function should provide a never empty guarantee for several technical reasons: a) Providing the guarantee has a runtime cost. Allowing empty Boost.Function objects is free. b) Looking forward to C++0x, it is impossible to implement a no throw move for objects than have no empty state. c) Objects that provide default constructors are easier to work with.
Hi Steven, Objects that provide strong invariants are easier to work with, too, and I value a strong invariant far more than a default constructor. In fact, I almost never have any use for the latter. Point a) is compelling. I'm not sure point b) is as absolutely true as you state it to be. I believe there are cases where you can provide such a nothrow move. The most obvious case is for objects that have no deep data. Cheers, -- David Abrahams Boost Consulting, Inc. http://www.boost-consulting.com

AMDG David Abrahams wrote:
Objects that provide strong invariants are easier to work with, too, and I value a strong invariant far more than a default constructor. In fact, I almost never have any use for the latter.
Right. Never mind that--I don't know what I was thinking.
I'm not sure point b) is as absolutely true as you state it to be. I believe there are cases where you can provide such a nothrow move. The most obvious case is for objects that have no deep data.
Ok. I wasn't being precise, but b) is true for Boost.Function. In Christ, Steven Watanabe

Steven Watanabe wrote:
I don't think that boost::function should provide a never empty guarantee for several technical reasons: a) Providing the guarantee has a runtime cost. Allowing empty Boost.Function objects is free.
It only has a cost if the object doesn't have a no-throw move, and almost all types should be able to provide that.
b) Looking forward to C++0x, it is impossible to implement a no throw move for objects than have no empty state.
Moving itself almost never needs to throw, I think, except when they need to register their address in a global collection of something like that. The problem is that the destructor will still be called on the old object, so that object needs to be in a valid deletable state while not putting any side-effect on the new object. That does require an empty state in some cases, but that state could only be reached from the move constructor. T::T(T&& o) could make 'o' empty, but there could be no other way to make 'o' empty otherwise. (I personally wonder if it wouldn't have been better to not call destructors on moved objects)

On Thu, Jul 24, 2008 at 4:58 PM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote: <snip>
T::T(T&& o) could make 'o' empty, but there could be no other way to make 'o' empty otherwise.
I think that the biggest problem is that, if T is movable, you cannot statically guarantee that T is never accessed in an empty state, so the never empty guarantee is not as useful.
(I personally wonder if it wouldn't have been better to not call destructors on moved objects)
Again, in general, the type system does not let the compiler know statically if an object has been moved, so you would need an extra bolean flag for each movable type instance to know if that instance has been moved or not. This could be an unacceptable size and performance cost in some cases. -- gpd

AMDG Giovanni Piero Deretta wrote:
On Thu, Jul 24, 2008 at 4:58 PM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote: <snip>
T::T(T&& o) could make 'o' empty, but there could be no other way to make 'o' empty otherwise.
I think that the biggest problem is that, if T is movable, you cannot statically guarantee that T is never accessed in an empty state, so the never empty guarantee is not as useful.
Upon further reflection, the never empty guarantee is not useful at all for Boost.Function. Suppose that Boost.Function does not provide such a guarantee and I have a boost::function that I want to call. What can I assume? I obviously can't assume that it won't throw, because it will throw if it is empty. This is not changed by adding the never empty guarantee. Nothing prevents a boost::function from holding a function object that throws when it is called. The only invariant that is added by the never empty guarantee is that f.target_type() != typeid(void). I don't see this as being useful. In Christ, Steven Watanabe

Giovanni Piero Deretta wrote:
I think that the biggest problem is that, if T is movable, you cannot statically guarantee that T is never accessed in an empty state, so the never empty guarantee is not as useful.
You can only move from rvalues. Rvalues cannot be accessed later on. If they're not real rvalues, that means casting was used. Which should then make it explicit that trying to access it later on is undefined behaviour as far as the implementation of the type is concerned.

AMDG Mathias Gaunard wrote:
Giovanni Piero Deretta wrote:
I think that the biggest problem is that, if T is movable, you cannot statically guarantee that T is never accessed in an empty state, so the never empty guarantee is not as useful.
You can only move from rvalues. Rvalues cannot be accessed later on.
If they're not real rvalues, that means casting was used. Which should then make it explicit that trying to access it later on is undefined behaviour as far as the implementation of the type is concerned.
Such a move would be much too limiting. Consider how std::remove would be implemented (untested): template<class Iter, class T> Iter remove(Iter begin, Iter end, const T& value) { begin = std::find(begin, end, value); Iter write_pos = begin++; for(; begin != end; ++begin) { if(*begin != value) { *write_pos++ = std::move(*begin); } } return(write_pos); } In Christ, Steven Watanabe

Steven Watanabe wrote:
AMDG
Mathias Gaunard wrote:
If they're not real rvalues, that means casting was used. Which should then make it explicit that trying to access it later on is undefined behaviour as far as the implementation of the type is concerned.
Such a move would be much too limiting.
Your code needs to be able to assign a non-empty object to an empty one. Would you also consider it necessary to be able to assign an empty object to a non-empty one? Were some guidelines chosen by the standard committee as to what move semantics ought to do?
Consider how std::remove would be implemented (untested):
template<class Iter, class T> Iter remove(Iter begin, Iter end, const T& value) { begin = std::find(begin, end, value); Iter write_pos = begin++; for(; begin != end; ++begin) { if(*begin != value) { *write_pos++ = std::move(*begin);
Possible alternative here: destruct and construct. replace *write_pos++ = std::move(*begin); by reassign(*write_pos++, std::move(*begin)); with template<typename T1, typename T2> void reassign(T1& old, T2&& new) { old.~T1(); new(&old) T1(std::forward<T2>(new)); } A function called `reassign' (that would destroy the old object then construct the new one) could be used instead of assignment in cases where the left operand can be in a `moved' state.
} } return(write_pos); }
Anyway, I'm pretty sure that function could be purely implemented in terms of swap, which seems like a safer construct.

AMDG Mathias Gaunard wrote:
Your code needs to be able to assign a non-empty object to an empty one. Would you also consider it necessary to be able to assign an empty object to a non-empty one?
To be more specific my code needs to assign a non-empty object to one that may or may not be empty.
Possible alternative here: destruct and construct.
replace *write_pos++ = std::move(*begin); by reassign(*write_pos++, std::move(*begin));
with
template<typename T1, typename T2> void reassign(T1& old, T2&& new) { old.~T1(); new(&old) T1(std::forward<T2>(new)); }
A function called `reassign' (that would destroy the old object then construct the new one) could be used instead of assignment in cases where the left operand can be in a `moved' state.
This only provides yet another way to assign (And to get undefined behavior if you forget to use the right function). a = b; // a must not have been moved from a = move(b); // ditto reassign(a, b); // a may have been moved from. doesn't even provide basic exception safety. reassign(a, std::move(b)); // a may have been moved from. Exception safe as long as the move constructor can't throw. I thought that you were advocating safety?
Anyway, I'm pretty sure that function could be purely implemented in terms of swap, which seems like a safer construct.
Sure it could be implemented in terms of swap, but I disagree that swap is safer than proper move assignment. swap is also less efficient for POD. In Christ, Steven Watanabe

Steven Watanabe wrote:
This only provides yet another way to assign (And to get undefined behavior if you forget to use the right function).
a = b; // a must not have been moved from a = move(b); // ditto reassign(a, b); // a may have been moved from. doesn't even provide basic exception safety. reassign(a, std::move(b)); // a may have been moved from. Exception safe as long as the move constructor can't throw.
I thought that you were advocating safety?
As I said, moving lvalues is unsafe, since you should treat them like rvalues and thus do not access them later. Since you casted and did unsafe magic, you have to be careful whether the objects you are manipulating are in a moved/empty state or not. It's the same with regular empty objects: you have to make sure you do not access them. At least, if you allow a non-empty object to be assigned to a empty one, but not the other way around, emptiness cannot propagate. About the exception-safety issue, I assume constructing an empty value to put back in a should be nothrow.
Anyway, I'm pretty sure that function could be purely implemented in terms of swap, which seems like a safer construct.
Sure it could be implemented in terms of swap, but I disagree that swap is safer than proper move assignment.
It is, since you're not letting any object in any empty state that obviously has only limited abilities.

AMDG Mathias Gaunard wrote:
As I said, moving lvalues is unsafe, since you should treat them like rvalues and thus do not access them later. Since you casted and did unsafe magic, you have to be careful whether the objects you are manipulating are in a moved/empty state or not.
Why do you insist on calling move assignment "casting and unsafe magic"? Ok. The value of a moved-from object is unspecified, but that is no reason to make it far more dangerous.
It's the same with regular empty objects: you have to make sure you do not access them.
No it isn't the same. There are some operations that ought to be valid for any object, regardless of whether it is empty or not. In particular if an type supports assignment, it should /always/ be safe to assign a new value to to an object of that type.
Sure it could be implemented in terms of swap, but I disagree that swap is safer than proper move assignment.
It is, since you're not letting any object in any empty state that obviously has only limited abilities.
If an algorithm leaves, certain values in an unspecified state, it is not safer to use swap to force them into a specific state internally to the algorithm. It is clearer to use a move because move means "I want a to hold the value that b held, and I don't care what b holds." In addition, for all I care, a = std::move(b) could be implemented as swap(a, b) for some types. In Christ, Steven Watanabe

on Tue Jul 29 2008, Steven Watanabe <watanabesj-AT-gmail.com> wrote:
It's the same with regular empty objects: you have to make sure you do not access them.
No it isn't the same. There are some operations that ought to be valid for any object, regardless of whether it is empty or not. In particular if an type supports assignment, it should /always/ be safe to assign a new value to to an object of that type.
Technically, in general, anything moved-from only needs to be in a destructible (and not assignable) state. However, a library can impose whatever additional requirements it wants to, and that the object needs to be assignable is certainly a reasonable (and useful) one. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Mathias Gaunard wrote:
Angel Tsankov wrote:
Is there any interest in a class template which acts like a pointer and guarantees at compile time and at no CPU or memory cost (except when a call to any of its methods is not inlined) that the pointer is not null?
Preventing empty or invalid cases is always good. For example I think it's a shame Boost.Function, Boost.Any and the Boost Smart Pointers don't do it.
As for forcing a pointer to not be empty, I suppose you're talking of making overloads of constructor and operator= for `int' and make them generate errors?
No. In C++, an int cannot be converted implicitly to a pointer, unless the int is the NULL constant, which is not a valid value for unnullable_ptr. Therefore, one should never need to construct unnullable_ptr from an int or to assign an int to an unnullable_ptr so there is no need to have overloads of constructor and operator= for int. Currently, an unnulalble_ptr is constructed directly from the pointee like this: T t; UnnullablePtr<T> pointer_to_t(t); Constructors take the pointee by reference, and apart from the implicit copy assignment there are no other mutable member functions. This is how the unnullability is guaranteed at compile time. On a side note, the behaviour of these constructors is inconsistent with the behaviour of the copy constructor (which creates a copy of the passed unnullable_ptr rather than a pointer to the passed unnullable_ptr). I'll probably fix this by making the 'inconsistent' constructors private, adding a (free) function template that calls the 'incosistent' constructors, and making that function template a friend.
Do you still provide pointer arithmetic and implicit upcasting?
Yes, implicit upcasting as well as the usual casts to void*, and T const* are supported (I've even written several unit tests especially to check the conversions). Implicit conversion from array to unnullable_ptr is also supported. As for pointer arithmetic, I've not yet needed it to work with unnullable_ptr's, so I've not considered implementing it. Nevertheless, it turns out that pointer arithmetic is partially supported (via the convertion operator from unnullable_ptr to C pointer); this is to say that subtracting unnullable_ptr's works, but subtracting int's from (or adding int's to) unnullable_ptr's doesn't yet. In fact, subtracting an int from (as well as adding an int to) an unnullable_ptr might result in a null pointer so the result need to be checked. However, the current implementation of unnullable_ptr is exactly as effective as a built-in C pointer (or at least allows instances of unnulable_ptr to be that effective) and I would like to keep it that way. So, subracting and adding int's will probably not be supported, unless a zero-overhead way is devised to guarantee unnullability of the result from these operations.
I personally stay away from pointers, which I associate with bad programming. boost::ref already provides a safe way to do what you want, and better IMHO. It is not required to use pointers at all with it. And you *can* get the wrapped pointer resource with get or get_pointer.
As I mentioned in my first post, boost::ref does not support the natural syntax for access to the wrapped resource. unnullable_ptr does -- it overloads 'operator ->' and 'operator T*'. (The latter implicitly provides dereferencing, i.e. there is no need to overload 'operator *'.) A known limitation is that unnulalble_ptr cannot be used with member functions (because there is no such thing as a reference to member function from which an unnullable_ptr is to be initialized). In order to be as clear as possible, I could provide all the source code of unnullable_ptr (157 lines) but I'm afraid that this does not meet the Boost posting guidelines. So, I think of uploading the code to the Vault. Angel Tsankov

----- Original Message ----- From: "Angel Tsankov" <fn42551@fmi.uni-sofia.bg> To: <boost@lists.boost.org> Sent: Tuesday, July 22, 2008 9:37 PM Subject: Re: [boost] [unnullable_ptr] Proposal for a new library
Mathias Gaunard wrote:
Do you still provide pointer arithmetic and implicit upcasting?
Yes, implicit upcasting as well as the usual casts to void*, and T const* are supported (I've even written several unit tests especially to check the conversions). Implicit conversion from array to unnullable_ptr is also supported.
As for pointer arithmetic, I've not yet needed it to work with unnullable_ptr's, so I've not considered implementing it. Nevertheless, it turns out that pointer arithmetic is partially supported (via the convertion operator from unnullable_ptr to C pointer); this is to say that subtracting unnullable_ptr's works, but subtracting int's from (or adding int's to) unnullable_ptr's doesn't yet. In fact, subtracting an int from (as well as adding an int to) an unnullable_ptr might result in a null pointer so the result need to be checked. However, the current implementation of unnullable_ptr is exactly as effective as a built-in C pointer (or at least allows instances of unnulable_ptr to be that effective) and I would like to keep it that way. So, subracting and adding int's will probably not be supported, unless a zero-overhead way is devised to guarantee unnullability of the result from these operations.
What is the expected calue of c? It is not 0? T t; UnnullablePtr<T> a(t); UnnullablePtr<T> b(t); UnnullablePtr<T> c(t); c=a-b; Vicente

vicente.botet wrote:
Mathias Gaunard wrote:
Do you still provide pointer arithmetic and implicit upcasting?
[...]
As for pointer arithmetic, I've not yet needed it to work with unnullable_ptr's, so I've not considered implementing it. Nevertheless, it turns out that pointer arithmetic is partially supported (via the convertion operator from unnullable_ptr to C pointer); this is to say that subtracting unnullable_ptr's works, but subtracting int's from (or adding int's to) unnullable_ptr's doesn't yet. In fact, subtracting an int from (as well as adding an int to) an unnullable_ptr might result in a null pointer so the result need to be checked. However, the current implementation of unnullable_ptr is exactly as effective as a built-in C pointer (or at least allows instances of unnulable_ptr to be that effective) and I would like to keep it that way. So, subracting and adding int's will probably not be supported, unless a zero-overhead way is devised to guarantee unnullability of the result from these operations.
What is the expected calue of c? It is not 0? T t; UnnullablePtr<T> a(t); UnnullablePtr<T> b(t); UnnullablePtr<T> c(t);
c=a-b;
This does not compile. More precisely, int i = a-b; // i = 0; c = i; // Error: int cannot be converted to UnnullablePtr<T> (neither implicitly, nor explicitly) Regards, Angel Tsankov

Angel Tsankov wrote:
vicente.botet wrote:
Mathias Gaunard wrote:
Do you still provide pointer arithmetic and implicit upcasting?
[...]
As for pointer arithmetic, I've not yet needed it to work with unnullable_ptr's, so I've not considered implementing it. Nevertheless, it turns out that pointer arithmetic is partially supported (via the convertion operator from unnullable_ptr to C pointer); this is to say that subtracting unnullable_ptr's works, but subtracting int's from (or adding int's to) unnullable_ptr's doesn't yet. In fact, subtracting an int from (as well as adding an int to) an unnullable_ptr might result in a null pointer so the result need to be checked. However, the current implementation of unnullable_ptr is exactly as effective as a built-in C pointer (or at least allows instances of unnulable_ptr to be that effective) and I would like to keep it that way. So, subracting and adding int's will probably not be supported, unless a zero-overhead way is devised to guarantee unnullability of the result from these operations.
What is the expected calue of c? It is not 0? T t; UnnullablePtr<T> a(t); UnnullablePtr<T> b(t); UnnullablePtr<T> c(t);
c=a-b;
This does not compile. More precisely, int i = a-b; // i = 0; c = i; // Error: int cannot be converted to UnnullablePtr<T> (neither implicitly, nor explicitly)
My intent was to know how subtracting unnullable_ptr's works See your text:
... this is to say that subtracting unnullable_ptr's works
Thanks for clarification. Vicente -- View this message in context: http://www.nabble.com/-unnullable_ptr--Proposal-for-a-new-library-tp18585008... Sent from the Boost - Dev mailing list archive at Nabble.com.

viboes wrote:
Angel Tsankov wrote:
vicente.botet wrote:
What is the expected calue of c? It is not 0? T t; UnnullablePtr<T> a(t); UnnullablePtr<T> b(t); UnnullablePtr<T> c(t);
c=a-b;
This does not compile. More precisely, int i = a-b; // i = 0; c = i; // Error: int cannot be converted to UnnullablePtr<T> (neither implicitly, nor explicitly)
My intent was to know how subtracting unnullable_ptr's works
Currently, subtracting unnullable_ptr's works by first calling 'operator T*() const' on each of the unnullable pointers and then subtracting the returned C pointers.
Thanks for clarification.
You are welcome. Regards, Angel Tsankov

Angel Tsankov wrote:
In order to be as clear as possible, I could provide all the source code of unnullable_ptr (157 lines) but I'm afraid that this does not meet the Boost posting guidelines. So, I think of uploading the code to the Vault. Here's the source code for UnnullablePtr and the unit tests: http://www.boostpro.com/vault/index.php?&direction=0&order=&directory=Unnullable%20Pointer
Regards, Angel Tsankov

Angel Tsankov wrote:
Here's the source code for UnnullablePtr and the unit tests: http://www.boostpro.com/vault/index.php?&direction=0&order=&directory=Unnullable%20Pointer
Shall I interpret this silence as no interest in UnnullablePtr?

I'll give a whirl at UnnullablePtr today. I'll keep you informed. On Tue, Jul 29, 2008 at 11:37 AM, Angel Tsankov <fn42551@fmi.uni-sofia.bg>wrote:
Angel Tsankov wrote:
Here's the source code for UnnullablePtr and the unit tests:
http://www.boostpro.com/vault/index.php?&direction=0&order=&directory=Unnullable%20Pointer
Shall I interpret this silence as no interest in UnnullablePtr?
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Alp Mestan --- http://blog.mestan.fr/ --- http://alp.developpez.com/ --- In charge of the Qt, Algorithms and Artificial Intelligence sections on Developpez
participants (13)
-
Alp Mestan
-
Andrey Tcherepanov
-
Angel Tsankov
-
David Abrahams
-
David Abrahams
-
Giovanni Piero Deretta
-
Lance.Diduckļ¼ ubs.com
-
Mathias Gaunard
-
Scott McMurray
-
Steven Watanabe
-
Thorsten Ottosen
-
viboes
-
vicente.botet