Boost logo

Boost :

From: Howard Hinnant (howard.hinnant_at_[hidden])
Date: 2006-03-17 14:17:55


Sorry for the delay in the follow up.

On Mar 16, 2006, at 7:03 PM, Peter Dimov wrote:

> Can you try the +m change on as many g++ versions as possible? I
> may be
> misremembering things. Or can you try the potentially safer
> alternative of
> adding "m"( *pw ) as an input and see whether this also works?

Yes, that seems to work just fine too.

>
> If you contribute your test case, this will be appreciated, too.

The original report was one of the boost unit tests:

---- file boost_unit_test_gcc4_pb_sample.cpp ---
#include <iostream>
#include <boost/test/unit_test.hpp>
using boost::unit_test_framework::test_suite;
using boost::unit_test_framework::test_case;
using namespace std;

class SampleTest {
public:
    SampleTest (int val) :
       a (val)
    {
    }
    int a;

    void sampleTest ()
    {
       std::cout << "a : " << a << std::endl;
       BOOST_CHECK_EQUAL (a, 12345);
    }
};

test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
    test_suite* test= BOOST_TEST_SUITE( "Sample test" );
    boost::shared_ptr<SampleTest> instance (new SampleTest (12345));
    test->add (BOOST_CLASS_TEST_CASE ( & SampleTest::sampleTest,
instance));
    return test;
}
---- end-of-file boost_unit_test_gcc4_pb_sample.cpp ---

The symptom showed itself by having the SampleTest destructor (or
malloc) scribble on destructed/deleted memory.

I later narrowed it down to that included below. Sorry, it is kind
of big. I never did reduce it further.

#include <boost/shared_ptr.hpp>

namespace boost {

namespace ut_detail {

struct unused {};

template<typename R>
struct invoker {
     template<typename Functor>
     R invoke( Functor& f ) { return f(); }
     template<typename Functor, typename T1>
     R invoke( Functor& f, T1 t1 ) { return f( t1 ); }
     template<typename Functor, typename T1, typename T2>
     R invoke( Functor& f, T1 t1, T2 t2 ) { return f( t1, t2 ); }
     template<typename Functor, typename T1, typename T2, typename T3>
     R invoke( Functor& f, T1 t1, T2 t2, T3 t3 ) { return f( t1, t2,
t3 ); }
};

template<>
struct invoker<unused> {
     template<typename Functor>
     unused invoke( Functor& f ) { f(); return unused(); }
     template<typename Functor, typename T1>
     unused invoke( Functor& f, T1 t1 ) { f( t1 ); return unused(); }
     template<typename Functor, typename T1, typename T2>
     unused invoke( Functor& f, T1 t1, T2 t2 ) { f( t1, t2 ); return
unused(); }
     template<typename Functor, typename T1, typename T2, typename T3>
     unused invoke( Functor& f, T1 t1, T2 t2, T3 t3 ) { f( t1, t2,
t3 ); return unused(); }
};

}

namespace ut_detail {

template<typename R>
struct callback0_impl {
     virtual ~callback0_impl() {}

     virtual R invoke() = 0;
};

template<typename R, typename Functor>
struct callback0_impl_t : callback0_impl<R> {

     explicit callback0_impl_t( Functor f ) : m_f( f ) {}

     virtual R invoke() { return invoker<R>().invoke( m_f ); }

private:

     Functor m_f;
};

template<typename R = void>
class callback0 {
public:

     callback0() {}

     template<typename Functor>
     callback0( Functor f )
     : m_impl( new ut_detail::callback0_impl_t<R,Functor>( f ) ) {}

     void operator=( callback0 const& rhs ) { m_impl = rhs.m_impl; }

     template<typename Functor>
     void operator=( Functor f ) { m_impl.reset( new
ut_detail::callback0_impl_t<R,Functor>( f ) ); }

     R operator()() const { return m_impl->invoke(); }

     bool operator!() const { return !m_impl; }

private:

     boost::shared_ptr<ut_detail::callback0_impl<R> > m_impl;
};

class test_case {
public:
     enum { type = 1 };

     test_case( callback0<> const& test_func );

     callback0<> const& test_func() const { return m_test_func; }

private:
     friend class framework_impl;
     ~test_case() {}

     callback0<> m_test_func;
};

inline
test_case::test_case( callback0<> const& test_func )
: m_test_func( test_func )
{
}

}

namespace ut_detail {

template<typename UserTestCase>
struct user_tc_method_invoker {
     typedef void (UserTestCase::*test_method )();

     user_tc_method_invoker( shared_ptr<UserTestCase> inst,
test_method tm )
     : m_inst( inst ), m_test_method( tm ) {}

     void operator()() { ((*m_inst).*m_test_method)(); }

     shared_ptr<UserTestCase> m_inst;
     test_method m_test_method;
};

}

template<typename UserTestCase>
ut_detail::test_case*
make_test_case( void (UserTestCase::*test_method )(),
                   boost::shared_ptr<UserTestCase> const&
user_test_case )
{
     return new ut_detail::test_case
( ut_detail::user_tc_method_invoker<UserTestCase>( user_test_case,
test_method ) );
}

}

class SampleTest {
public:
    SampleTest (int val) :
       a (val)
    {
    }
    int a;

    void sampleTest ()
    {
    }
};

SampleTest* g;

int
main()
{
      boost::ut_detail::test_case* t;
    {
    boost::shared_ptr<SampleTest> instance (g = new SampleTest (12345));
    std::cout << "instance.use_count = " << instance.use_count() << "
before add: g = " << g << " *g = " << g->a << '\n';
    t = boost::make_test_case((& SampleTest::sampleTest), instance );
    std::cout << "instance.use_count = " << instance.use_count() << "
after add: g = " << g << " *g = " << g->a << '\n';
     }
    std::cout << "after add: g = " << g << " *g = " << g->a << '\n';
    return t != 0;
}

expecting an output similar to:

instance.use_count = 1 before add: g = 0x30010 *g = 12345
instance.use_count = 2 after add: g = 0x30010 *g = 12345
after add: g = 0x30010 *g = 12345

The use_count values were the dead giveaway. The second one was
coming out 1 or even sometimes 0.

-Howard


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