Normally if we have a pointer to an object and want to call a member function we will guard the call like this:
if ( ptr ) ptr->member();
If we need to use the result in an expression we need to do this:
ptr ? ptr->member() : alternative
More complex is a case where we need to pass an argument which has a side effect:
if ( ptr ) ptr->member( ++it ); else ++it;
Here we must duplicate code for each argument which has a side effect. For an expression this is much more complex.
safe_call allows us to replace these with the following:
safe_call( ptr, &MyClass::member )( ++it )
This form works as both a statement and an expression. If ptr is null then a default constructed item of the correct return type of MyClass::member is returned.
Where the return type is not default constructable or a different alternative value is needed there is also a three argument version of safe_call.
safe_call( ptr, &MyClass::member, alternative )( ++it )
If the pointer is null then the alternative value is returned.
#include <boost/type_traits.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition.hpp>
namespace boost {
namespace detail {
template<typename R, typename C>
struct function_traits_helper< R (C::**)(void) const >
{
BOOST_STATIC_CONSTANT(int, arity = 0);
typedef R result_type;
typedef C class_type;
};
template<typename R, typename C, typename A1>
struct function_traits_helper< R (C::**)( A1 ) const >
{
BOOST_STATIC_CONSTANT(int, arity = 1);
typedef R result_type;
typedef C class_type;
typedef A1 arg1_type;
};
template<typename R, typename C, typename A1, typename A2 >
struct function_traits_helper< R (C::**)( A1, A2 ) const >
{
BOOST_STATIC_CONSTANT(int, arity = 2);
typedef R result_type;
typedef C class_type;
typedef A1 arg1_type;
typedef A2 arg2_type;
};
}
}
/*
safe_call
*/
#ifndef SAFECALL_MAX_PARAMS
#define SAFECALL_MAX_PARAMS 16
#endif
#define SAFECALL_PARAM_DECL(z, n, _) BOOST_PP_COMMA_IF(n) A ## n a ## n
#define SAFECALL_FORWARDER( z, n, t ) \
template< BOOST_PP_ENUM_PARAMS( n, typename A ) > \
result_type operator()( BOOST_PP_REPEAT( n, SAFECALL_PARAM_DECL, _) ) const { \
if ( m_self ) return (m_self->*m_member)( BOOST_PP_ENUM_PARAMS( n, a ) ); \
else return t; \
}
template< typename F, bool > class safe_call_operator;
template< typename F >
class safe_call_operator< F, false > {
protected:
typedef typename boost::function_traits< F >::result_type result_type;
BOOST_STATIC_ASSERT( ! boost::is_reference< result_type >::value );
typedef typename boost::function_traits< F >::class_type class_type;
class_type *m_self;
F m_member;
public:
safe_call_operator< F, false >( class_type *s, F m )
: m_self( s ), m_member( m ) {
}
result_type operator()() const {
if ( m_self ) return (m_self->*m_member )();
else return result_type();
}
BOOST_PP_REPEAT_FROM_TO( 1, BOOST_PP_INC( SAFECALL_MAX_PARAMS ), SAFECALL_FORWARDER, result_type() )
};
template< typename F >
class safe_call_operator< F, true > : private safe_call_operator< F, false > {
result_type m_return;
public:
safe_call_operator< F, true >( class_type *s, F m, result_type d )
: safe_call_operator< F, false >( s, m ), m_return( d ) {
}
result_type operator()() const {
if ( m_self ) return (m_self->*m_member )();
else return m_return;
}
BOOST_PP_REPEAT_FROM_TO( 1, BOOST_PP_INC( SAFECALL_MAX_PARAMS ), SAFECALL_FORWARDER, m_return )
};
#undef SAFECALL_FORWARDER
#undef SAFECALL_PARAM_DECL_REF
#undef SAFECALL_PARAM_DECL
#undef SAFECALL_MAX_PARAMS
template< typename F > inline
safe_call_operator< F, false > safe_call( typename boost::function_traits< F >::class_type *s, F f ) {
return safe_call_operator< F, false >( s, f );
}
template< typename F, typename D > inline
safe_call_operator< F, true > safe_call( typename boost::function_traits< F >::class_type *s, F f, D d ) {
return safe_call_operator< F, true >( s, f, typename boost::function_traits< F >::result_type( d ) );
}
/*
Test on a foo
*/
#include <string>
#include <iostream>
struct foo {
foo( const char *str )
: m_str( str ) {
std::cout << "Constructed with: " << m_str << std::endl;
}
std::string bar() const {
return m_str;
}
std::string baz( const foo &p ) const {
return m_str + " " + p.m_str;
}
size_t boz( int i ) const {
return i + m_str.length();
}
size_t biz( int l, int r ) const {
return l * m_str.length() + r;
}
const std::string &my_str() const {
return m_str;
}
private:
const std::string m_str;
};
int main() {
foo
instance( "Hello" ),
&instance_ref( instance ),
*pointer_instance = &instance,
*pointer_null = 0;
int calls = 0;
std::cout
<< "Instance:\n"
<< std::string( safe_call( pointer_instance, &foo::bar )() ) << "\n"
<< safe_call( pointer_instance, &foo::bar )() << "\n"
<< safe_call( pointer_instance, &foo::baz, "bypassed" )( "world 1" ) << "\n"
<< safe_call( pointer_instance, &foo::boz )( ++calls ) << "\n"
<< safe_call( pointer_instance, &foo::biz )( 3, 5 ) << "\n"
<< std::endl;
std::cout
<< "Null foo:\n"
<< std::string( safe_call( pointer_null, &foo::baz )( instance_ref ) ) << "\n"
<< safe_call( pointer_null, &foo::bar )() << "\n"
<< safe_call( pointer_null, &foo::baz, "bypassed" )( "world 2" ) << "\n"
<< safe_call( pointer_null, &foo::boz )( ++calls ) << "\n"
<< std::endl;
std::cout << "Calls should be 2: " << calls << std::endl;
// Neither of these will compile because my_str returns a reference
//std::cout << safe_call( pointer_instance, &foo::my_str )() << std::endl;
//std::cout << safe_call( pointer_null, &foo::my_str )() << std::endl;
return 0;
}This is not an official Boost site. For more information on Boost please see Boost.org.