$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: [boost] [proposal] safe_ref<T>
From: Fernando Pelliccioni (fpelliccioni_at_[hidden])
Date: 2012-12-12 23:13:19
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.