
Hi all, The motivation is to avoid code like the following... //---------------------------------------------------------------------------- #include <iostream> #include <string> struct A { void do_something( std::string s ) const { std::cout << "do something with " << s << " and " << i_ << "\n"; } int i_ = 99; }; // unsafe traditional code struct Unsafe { void set_ref( A& a ) { a_ptr_ = std::addressof(a); } void use_ref( std::string s ) const { if (a_ptr_ != nullptr) { a_ptr_->do_something(s); } } void use_ref_2( std::string s ) const { //forgot to check for nullptr a_ptr_->do_something(s); } A* a_ptr_ = nullptr; //I can't use A& nor reference_wrapper<A>, need to be nullable. }; void f() { A a; Unsafe unsafe; unsafe.use_ref( "hello" ); //null reference, do nothing, hope! unsafe.set_ref(a); unsafe.use_ref( "hello world!" ); //not-null reference, Ok! Unsafe unsafe2; unsafe2.use_ref( "hello" ); //null reference, do nothing, hope! unsafe2.use_ref_2( "hello" ); //null reference, Ops! } //---------------------------------------------------------------------------- The proposed solution: //---------------------------------------------------------------------------- // Proposed (draft) //---------------------------------------------------------------------------- template <typename T> class safe_ref { public: typedef T type; explicit safe_ref() : t_( nullptr ) {} explicit safe_ref(T& t) : t_( std::addressof(t) ) {} template <typename Func> void use ( Func f ) const { if ( is_initialized() ) { f( *t_ ); } } private: bool is_initialized() const { return t_ != nullptr; } T* t_; }; template <typename T> inline safe_ref<T> const ref(T& t) { return safe_ref<T>(t); } template <typename T> inline safe_ref<T const> const cref(T const& t) { return safe_ref<T const>(t); } //---------------------------------------------------------------------------- // Usage //---------------------------------------------------------------------------- #include <iostream> #include <string> struct A { void do_something( std::string s ) const { std::cout << "A::do_something with " << s << " and " << i_ << "\n"; } int i_ = 99; }; struct Safe { void set_ref( A& a ) { a_ref_ = ref(a); } void use_ref( std::string s ) const { a_ref_.use( [&s] ( A& a ) { a.do_something(s); }); } safe_ref<A> a_ref_; }; void usage1() { A a; Safe safe; safe.use_ref( "hello" ); //null reference, do nothing safe.set_ref(a); safe.use_ref( "hello world!" ); //safe reference, Ok! } void usage2() { safe_ref<int> si; si.use( [] (int& i ) { std::cout << i << std::endl; }); int temp = 15; safe_ref<int> si2(temp); si2.use( [] (int& i ) { std::cout << i << std::endl; }); si = ref(temp); si.use( [] (int& i ) { std::cout << i << std::endl; }); } //---------------------------------------------------------------------------- It is similar to optional<T>, but in this case the idea is to not allow access to internal data if the pointer is null, only allowing a safe usage There is interest to add something like this to Boost? Any comments or remarks? Thanks and regards, Fernando Pelliccioni.

On Thu, Dec 13, 2012 at 8:13 AM, Fernando Pelliccioni <fpelliccioni@gmail.com> wrote:
Hi all,
The motivation is to avoid code like the following...
//---------------------------------------------------------------------------- // Usage //----------------------------------------------------------------------------
#include <iostream> #include <string>
struct A { void do_something( std::string s ) const { std::cout << "A::do_something with " << s << " and " << i_ << "\n"; }
int i_ = 99; };
struct Safe { void set_ref( A& a ) { a_ref_ = ref(a); }
void use_ref( std::string s ) const { a_ref_.use( [&s] ( A& a ) { a.do_something(s); }); }
safe_ref<A> a_ref_; };
void usage1() { A a;
Safe safe; safe.use_ref( "hello" ); //null reference, do nothing safe.set_ref(a); safe.use_ref( "hello world!" ); //safe reference, Ok! }
void usage2() { safe_ref<int> si; si.use( [] (int& i ) { std::cout << i << std::endl; });
int temp = 15;
safe_ref<int> si2(temp); si2.use( [] (int& i ) { std::cout << i << std::endl; });
si = ref(temp); si.use( [] (int& i ) { std::cout << i << std::endl; }); } //----------------------------------------------------------------------------
It is similar to optional<T>, but in this case the idea is to not allow access to internal data if the pointer is null, only allowing a safe usage
There is interest to add something like this to Boost? Any comments or remarks?
I don't see how this is better than optional<T&>, sorry. Your Safe wrapper could have been written around optional with the same success, and using lambdas or other function objects to work with references seem overcomplicated to me. Why not this: optional<int&> si(temp) if (si) std::cout << *si << std::endl; Looks simpler and it is clearly apparent that the output statement is executed conditionally on si presence.

On 13 December 2012 01:05, Andrey Semashev <andrey.semashev@gmail.com>wrote:
I don't see how this is better than optional<T&>, sorry. Your Safe wrapper could have been written around optional with the same success, and using lambdas or other function objects to work with references seem overcomplicated to me.
+1
optional<int&> si(temp) if (si) std::cout << *si << std::endl;
Or even: int* pi(&temp); if (pi) std::cout << *pi << std::endl; -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On Dec 13, 2012, at 2:18 AM, Nevin Liber <nevin@eviloverlord.com> wrote:
On 13 December 2012 01:05, Andrey Semashev <andrey.semashev@gmail.com>wrote:
I don't see how this is better than optional<T&>, sorry. Your Safe wrapper could have been written around optional with the same success, and using lambdas or other function objects to work with references seem overcomplicated to me.
+1
optional<int&> si(temp) if (si) std::cout << *si << std::endl;
Or even:
int* pi(&temp); if (pi) std::cout << *pi << std::endl;
While I mostly agree with you and Andrey, neither alternative addresses the OP's desire to force the null check by virtue of using the wrapper type. Using optional comes close, as there will be an exception, IIRC. ___ Rob

On Thu, Dec 13, 2012 at 2:14 PM, Rob Stewart <robertstewart@comcast.net> wrote:
On Dec 13, 2012, at 2:18 AM, Nevin Liber <nevin@eviloverlord.com> wrote:
On 13 December 2012 01:05, Andrey Semashev <andrey.semashev@gmail.com>wrote:
I don't see how this is better than optional<T&>, sorry. Your Safe wrapper could have been written around optional with the same success, and using lambdas or other function objects to work with references seem overcomplicated to me.
+1
optional<int&> si(temp) if (si) std::cout << *si << std::endl;
Or even:
int* pi(&temp); if (pi) std::cout << *pi << std::endl;
While I mostly agree with you and Andrey, neither alternative addresses the OP's desire to force the null check by virtue of using the wrapper type. Using optional comes close, as there will be an exception, IIRC.
An assert. Still, I would prefer optional or pointer in this case as the code looks clearer.

On Thu, Dec 13, 2012 at 7:35 AM, Andrey Semashev <andrey.semashev@gmail.com>wrote:
On Dec 13, 2012, at 2:18 AM, Nevin Liber <nevin@eviloverlord.com> wrote:
On 13 December 2012 01:05, Andrey Semashev <andrey.semashev@gmail.com wrote:
I don't see how this is better than optional<T&>, sorry. Your Safe wrapper could have been written around optional with the same success, and using lambdas or other function objects to work with references seem overcomplicated to me.
+1
optional<int&> si(temp) if (si) std::cout << *si << std::endl;
Or even:
int* pi(&temp); if (pi) std::cout << *pi << std::endl;
While I mostly agree with you and Andrey, neither alternative addresses
On Thu, Dec 13, 2012 at 2:14 PM, Rob Stewart <robertstewart@comcast.net> wrote: the OP's desire to force the null check by virtue of using the wrapper type. Using optional comes close, as there will be an exception, IIRC.
An assert. Still, I would prefer optional or pointer in this case as the code looks clearer.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Thank you all for your responses. The idea is to avoid dereferencing a null reference, enforced by the compiler (type safe). optional<T&> not meet this purpose. I'm thinking of a proposal involving certain changes in the core language. But for now, I thought that this simple tool class library can help (Obviously, I need to think of all possible uses). Thanks and regards, Fernando Pelliccioni.

It is similar to optional<T>, but in this case the idea is to not allow access to internal data if the pointer is null, only allowing a safe usage
There is interest to add something like this to Boost? Any comments or remarks?
This is just my opinion, but I believe that this approach is wrong. Dereferencing a null pointer is definitely wrong, but preventing it by silently dropping some portions of code (that the programmer intended to execute) doesn't look good either. If a programmer wants to call function f() on some object, but he makes a bug by forgetting to initialize the pointer/reference/handle, safe_ref would give him a false impression that his function has been executed. If you think of some rare cases where this behavior is exactly what one needs, perhaps you should consider renaming the wrapper to something like "ignoring_ref" or "skipping_ref". The part "safe" does not give an indication of what the wrapper really does, and it is not that clear to me if its behavior makes the program safer. Regards, &rzej
participants (5)
-
Andrey Semashev
-
Andrzej Krzemienski
-
Fernando Pelliccioni
-
Nevin Liber
-
Rob Stewart