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.