Boost logo

Boost :

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.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk