vector of pimpl's / weird behavior

Discussion in 'C++' started by er, Sep 28, 2007.

  1. er

    er Guest

    hi,

    the code below generates the following behavior. cld someone please
    help understand it?
    1) clean project + build project generates build errors (see bottom)
    2) build a second time, errors disappears, run: ok
    3) uncomment [1] and comment [2], no problem
    4) uncomment [3] and [4], also no problem

    #ifndef A_IMPL_H_
    #define A_IMPL_H_
    #include <memory>
    class A_impl{
    public:
    A_impl();
    virtual ~A_impl(){};
    virtual std::auto_ptr<A_impl> clone()const=0;
    private:
    A_impl& operator=(const A_impl&);
    };
    #endif /*A_IMPL_H_*/

    #ifndef A_H_
    #define A_H_
    #include <memory>
    //#include "A_impl.h" // [1]
    class A_impl;//[2]
    class A{
    public:
    A(const A_impl& impl_);
    A(const A& o);
    A& operator=(const A& rhs);
    private:
    A();
    std::auto_ptr<A_impl> pimpl;
    };
    #endif /*A_H_*/

    #ifndef AS_H_
    #define AS_H_
    #include <vector>
    #include "A.h"
    class As{
    public:
    As(
    const std::vector<A>& sub_collec_
    );
    private:
    std::vector<A> sub_collec;//[3]
    };
    #endif /*AS_H_*/
    #include "A.h"
    #include "A_impl.h"
    A::A(const A_impl& impl_):pimpl(impl_.clone()){};
    A::A(const A& o):pimpl((o.pimpl)->clone()){};
    A& A::eek:perator=(const A& rhs){
    if(&rhs!=this){
    pimpl=rhs.pimpl->clone();
    };
    return *this;
    };
    #include "As.h"
    As::As(
    const std::vector<A>& sub_collec_
    ): sub_collec(sub_collec_){};//[4]


    instantiated from 'void std::_Destroy(_Tp*) [with _Tp = A]'
    testing_vector_pimpl A.h line 6 1191008360119 128231
    /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    stl_construct.h instantiated from 'void
    std::__destroy_aux(_ForwardIterator, _ForwardIterator, __false_type)
    [with _ForwardIterator = A*]' testing_vector_pimpl line 122
    1191008360120 128232
    /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    stl_construct.h instantiated from 'void
    std::_Destroy(_ForwardIterator, _ForwardIterator, std::allocator<_T2>)
    [with _ForwardIterator = A*, _Tp = A]' testing_vector_pimpl line 182
    1191008360120 128234
    /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    stl_construct.h instantiated from 'void
    std::_Destroy(_ForwardIterator, _ForwardIterator) [with
    _ForwardIterator = A*]' testing_vector_pimpl line 155 1191008360120
    128233
    /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    stl_vector.h instantiated from 'std::vector<_Tp, _Alloc>::~vector()
    [with _Tp = A, _Alloc = std::allocator<A>]' testing_vector_pimpl line
    272 1191008360120 128235
    er, Sep 28, 2007
    #1
    1. Advertising

  2. er

    Barry Guest

    er wrote:
    > hi,
    >
    > the code below generates the following behavior. cld someone please
    > help understand it?


    don't say generate behavior, I see that's compiler-time error, behavior
    much more means that the code runs expectedly.

    > 1) clean project + build project generates build errors (see bottom)
    > 2) build a second time, errors disappears, run: ok


    that's more about the compiler, not about C++ language, I know that VC6
    has some problems here.

    > 3) uncomment [1] and comment [2], no problem
    > 4) uncomment [3] and [4], also no problem
    >
    > #ifndef A_IMPL_H_
    > #define A_IMPL_H_
    > #include <memory>
    > class A_impl{
    > public:
    > A_impl();
    > virtual ~A_impl(){};
    > virtual std::auto_ptr<A_impl> clone()const=0;
    > private:
    > A_impl& operator=(const A_impl&);
    > };
    > #endif /*A_IMPL_H_*/
    >
    > #ifndef A_H_
    > #define A_H_
    > #include <memory>
    > //#include "A_impl.h" // [1]
    > class A_impl;//[2]
    > class A{
    > public:
    > A(const A_impl& impl_);
    > A(const A& o);
    > A& operator=(const A& rhs);


    dtor is produced by compiler, it's

    ~A() {}

    where std::auto_ptr::~auto_ptr() is called, which delete A_impl*, but
    A_impl is incomplete type, if we only use forward declaration here. A
    solution, declaration A::~A, and define it it *A.cpp*, where you include
    "A_impl.h", so, A_impl is complete type there.


    > private:
    > A();
    > std::auto_ptr<A_impl> pimpl;
    > };
    > #endif /*A_H_*/
    >
    > #ifndef AS_H_
    > #define AS_H_
    > #include <vector>
    > #include "A.h"
    > class As{
    > public:
    > As(
    > const std::vector<A>& sub_collec_
    > );
    > private:
    > std::vector<A> sub_collec;//[3]
    > };
    > #endif /*AS_H_*/
    > #include "A.h"
    > #include "A_impl.h"
    > A::A(const A_impl& impl_):pimpl(impl_.clone()){};
    > A::A(const A& o):pimpl((o.pimpl)->clone()){};
    > A& A::eek:perator=(const A& rhs){
    > if(&rhs!=this){
    > pimpl=rhs.pimpl->clone();
    > };
    > return *this;
    > };
    > #include "As.h"
    > As::As(
    > const std::vector<A>& sub_collec_
    > ): sub_collec(sub_collec_){};//[4]
    >
    >
    > instantiated from 'void std::_Destroy(_Tp*) [with _Tp = A]'
    > testing_vector_pimpl A.h line 6 1191008360119 128231
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_construct.h instantiated from 'void
    > std::__destroy_aux(_ForwardIterator, _ForwardIterator, __false_type)
    > [with _ForwardIterator = A*]' testing_vector_pimpl line 122
    > 1191008360120 128232
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_construct.h instantiated from 'void
    > std::_Destroy(_ForwardIterator, _ForwardIterator, std::allocator<_T2>)
    > [with _ForwardIterator = A*, _Tp = A]' testing_vector_pimpl line 182
    > 1191008360120 128234
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_construct.h instantiated from 'void
    > std::_Destroy(_ForwardIterator, _ForwardIterator) [with
    > _ForwardIterator = A*]' testing_vector_pimpl line 155 1191008360120
    > 128233
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_vector.h instantiated from 'std::vector<_Tp, _Alloc>::~vector()
    > [with _Tp = A, _Alloc = std::allocator<A>]' testing_vector_pimpl line
    > 272 1191008360120 128235
    >



    --
    Thanks
    Barry
    Barry, Sep 29, 2007
    #2
    1. Advertising

  3. er

    Guest

    On 9 29 , 3 49 , er <> wrote:
    > hi,
    >
    > the code below generates the following behavior. cld someone please
    > help understand it?
    > 1) clean project + build project generates build errors (see bottom)


    build error because you used undefined class A_impl (with [1]
    commented and [2] uncommented at the first time).

    > 2) build a second time, errors disappears, run: ok


    I have no idea how can you run the program when build still error.

    > 3) uncomment [1] and comment [2], no problem


    When this done, class A_impl has been defined before you use it, of
    course OK.

    > 4) uncomment [3] and [4], also no problem


    When this done, class A_impl is used only as a type (for pointer). It
    needn't declare its detail, 'class A_impl;' is enough.

    >
    > #ifndef A_IMPL_H_
    > #define A_IMPL_H_
    > #include <memory>
    > class A_impl{
    > public:
    > A_impl();
    > virtual ~A_impl(){};
    > virtual std::auto_ptr<A_impl> clone()const=0;
    > private:
    > A_impl& operator=(const A_impl&);};
    >
    > #endif /*A_IMPL_H_*/
    >
    > #ifndef A_H_
    > #define A_H_
    > #include <memory>
    > //#include "A_impl.h" // [1]
    > class A_impl;//[2]
    > class A{
    > public:
    > A(const A_impl& impl_);
    > A(const A& o);
    > A& operator=(const A& rhs);
    > private:
    > A();
    > std::auto_ptr<A_impl> pimpl;};
    >
    > #endif /*A_H_*/
    >
    > #ifndef AS_H_
    > #define AS_H_
    > #include <vector>
    > #include "A.h"
    > class As{
    > public:
    > As(
    > const std::vector<A>& sub_collec_
    > );
    > private:
    > std::vector<A> sub_collec;//[3]};
    >
    > #endif /*AS_H_*/
    > #include "A.h"
    > #include "A_impl.h"
    > A::A(const A_impl& impl_):pimpl(impl_.clone()){};
    > A::A(const A& o):pimpl((o.pimpl)->clone()){};
    > A& A::eek:perator=(const A& rhs){
    > if(&rhs!=this){
    > pimpl=rhs.pimpl->clone();
    > };
    > return *this;};
    >
    > #include "As.h"
    > As::As(
    > const std::vector<A>& sub_collec_
    > ): sub_collec(sub_collec_){};//[4]
    >
    > instantiated from 'void std::_Destroy(_Tp*) [with _Tp = A]'
    > testing_vector_pimpl A.h line 6 1191008360119 128231
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_construct.h instantiated from 'void
    > std::__destroy_aux(_ForwardIterator, _ForwardIterator, __false_type)
    > [with _ForwardIterator = A*]' testing_vector_pimpl line 122
    > 1191008360120 128232
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_construct.h instantiated from 'void
    > std::_Destroy(_ForwardIterator, _ForwardIterator, std::allocator<_T2>)
    > [with _ForwardIterator = A*, _Tp = A]' testing_vector_pimpl line 182
    > 1191008360120 128234
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_construct.h instantiated from 'void
    > std::_Destroy(_ForwardIterator, _ForwardIterator) [with
    > _ForwardIterator = A*]' testing_vector_pimpl line 155 1191008360120
    > 128233
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_vector.h instantiated from 'std::vector<_Tp, _Alloc>::~vector()
    > [with _Tp = A, _Alloc = std::allocator<A>]' testing_vector_pimpl line
    > 272 1191008360120 128235
    , Sep 29, 2007
    #3
  4. er

    er Guest

    On Sep 29, 8:08 am, Barry <> wrote:
    > er wrote:
    > > hi,

    >
    > > the code below generates the following behavior. cld someone please
    > > help understand it?

    >
    > don't say generate behavior, I see that's compiler-time error, behavior
    > much more means that the code runs expectedly.
    >
    > > 1) clean project + build project generates build errors (see bottom)
    > > 2) build a second time, errors disappears, run: ok

    >
    > that's more about the compiler, not about C++ language, I know that VC6
    > has some problems here.
    >
    >
    >
    > > 3) uncomment [1] and comment [2], no problem
    > > 4) uncomment [3] and [4], also no problem

    >
    > > #ifndef A_IMPL_H_
    > > #define A_IMPL_H_
    > > #include <memory>
    > > class A_impl{
    > > public:
    > > A_impl();
    > > virtual ~A_impl(){};
    > > virtual std::auto_ptr<A_impl> clone()const=0;
    > > private:
    > > A_impl& operator=(const A_impl&);
    > > };
    > > #endif /*A_IMPL_H_*/

    >
    > > #ifndef A_H_
    > > #define A_H_
    > > #include <memory>
    > > //#include "A_impl.h" // [1]
    > > class A_impl;//[2]
    > > class A{
    > > public:
    > > A(const A_impl& impl_);
    > > A(const A& o);
    > > A& operator=(const A& rhs);

    >
    > dtor is produced by compiler, it's
    >
    > ~A() {}
    >
    > where std::auto_ptr::~auto_ptr() is called, which delete A_impl*, but
    > A_impl is incomplete type, if we only use forward declaration here. A
    > solution, declaration A::~A, and define it it *A.cpp*, where you include
    > "A_impl.h", so, A_impl is complete type there.
    >
    >
    >
    > > private:
    > > A();
    > > std::auto_ptr<A_impl> pimpl;
    > > };
    > > #endif /*A_H_*/

    >
    > > #ifndef AS_H_
    > > #define AS_H_
    > > #include <vector>
    > > #include "A.h"
    > > class As{
    > > public:
    > > As(
    > > const std::vector<A>& sub_collec_
    > > );
    > > private:
    > > std::vector<A> sub_collec;//[3]
    > > };
    > > #endif /*AS_H_*/
    > > #include "A.h"
    > > #include "A_impl.h"
    > > A::A(const A_impl& impl_):pimpl(impl_.clone()){};
    > > A::A(const A& o):pimpl((o.pimpl)->clone()){};
    > > A& A::eek:perator=(const A& rhs){
    > > if(&rhs!=this){
    > > pimpl=rhs.pimpl->clone();
    > > };
    > > return *this;
    > > };
    > > #include "As.h"
    > > As::As(
    > > const std::vector<A>& sub_collec_
    > > ): sub_collec(sub_collec_){};//[4]

    >
    > > instantiated from 'void std::_Destroy(_Tp*) [with _Tp = A]'
    > > testing_vector_pimpl A.h line 6 1191008360119 128231
    > > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > > stl_construct.h instantiated from 'void
    > > std::__destroy_aux(_ForwardIterator, _ForwardIterator, __false_type)
    > > [with _ForwardIterator = A*]' testing_vector_pimpl line 122
    > > 1191008360120 128232
    > > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > > stl_construct.h instantiated from 'void
    > > std::_Destroy(_ForwardIterator, _ForwardIterator, std::allocator<_T2>)
    > > [with _ForwardIterator = A*, _Tp = A]' testing_vector_pimpl line 182
    > > 1191008360120 128234
    > > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > > stl_construct.h instantiated from 'void
    > > std::_Destroy(_ForwardIterator, _ForwardIterator) [with
    > > _ForwardIterator = A*]' testing_vector_pimpl line 155 1191008360120
    > > 128233
    > > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > > stl_vector.h instantiated from 'std::vector<_Tp, _Alloc>::~vector()
    > > [with _Tp = A, _Alloc = std::allocator<A>]' testing_vector_pimpl line
    > > 272 1191008360120 128235

    >
    > --
    > Thanks
    > Barry


    declaring ~A() in .h and writing A::~A(){} in .cpp did solve my
    problem. thanks!
    er, Sep 30, 2007
    #4
  5. er

    James Kanze Guest

    On Sep 28, 9:49 pm, er <> wrote:
    > the code below generates the following behavior. cld someone please
    > help understand it?
    > 1) clean project + build project generates build errors (see bottom)
    > 2) build a second time, errors disappears, run: ok
    > 3) uncomment [1] and comment [2], no problem
    > 4) uncomment [3] and [4], also no problem


    > #ifndef A_IMPL_H_
    > #define A_IMPL_H_
    > #include <memory>
    > class A_impl{
    > public:
    > A_impl();
    > virtual ~A_impl(){};
    > virtual std::auto_ptr<A_impl> clone()const=0;
    > private:
    > A_impl& operator=(const A_impl&);};
    > #endif /*A_IMPL_H_*/


    > #ifndef A_H_
    > #define A_H_
    > #include <memory>
    > //#include "A_impl.h" // [1]
    > class A_impl;//[2]
    > class A{
    > public:
    > A(const A_impl& impl_);
    > A(const A& o);
    > A& operator=(const A& rhs);
    > private:
    > A();
    > std::auto_ptr<A_impl> pimpl;};
    > #endif /*A_H_*/


    The line with std::auto_ptr<> is undefined behavior (with the
    code as written). According to the standard, you can only
    instantiate a template in the standard library over a completely
    defined type.

    > #ifndef AS_H_
    > #define AS_H_
    > #include <vector>
    > #include "A.h"
    > class As{
    > public:
    > As(
    > const std::vector<A>& sub_collec_
    > );
    > private:
    > std::vector<A> sub_collec;//[3]};
    > #endif /*AS_H_*/
    > #include "A.h"
    > #include "A_impl.h"
    > A::A(const A_impl& impl_):pimpl(impl_.clone()){};
    > A::A(const A& o):pimpl((o.pimpl)->clone()){};
    > A& A::eek:perator=(const A& rhs){
    > if(&rhs!=this){
    > pimpl=rhs.pimpl->clone();
    > };
    > return *this;};


    > #include "As.h"
    > As::As(
    > const std::vector<A>& sub_collec_
    > ): sub_collec(sub_collec_){};//[4]


    > instantiated from 'void std::_Destroy(_Tp*) [with _Tp = A]'
    > testing_vector_pimpl A.h line 6 1191008360119 128231
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_construct.h instantiated from 'void
    > std::__destroy_aux(_ForwardIterator, _ForwardIterator, __false_type)
    > [with _ForwardIterator = A*]' testing_vector_pimpl line 122
    > 1191008360120 128232
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_construct.h instantiated from 'void
    > std::_Destroy(_ForwardIterator, _ForwardIterator, std::allocator<_T2>)
    > [with _ForwardIterator = A*, _Tp = A]' testing_vector_pimpl line 182
    > 1191008360120 128234
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_construct.h instantiated from 'void
    > std::_Destroy(_ForwardIterator, _ForwardIterator) [with
    > _ForwardIterator = A*]' testing_vector_pimpl line 155 1191008360120
    > 128233
    > /usr/lib/gcc/x86_64-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/
    > stl_vector.h instantiated from 'std::vector<_Tp, _Alloc>::~vector()
    > [with _Tp = A, _Alloc = std::allocator<A>]' testing_vector_pimpl line
    > 272 1191008360120 128235


    I don't know exactly why the rebuild eliminates the error with
    g++. With some other compilers, however, template
    instantiations are stored in a repository; if the template is
    successfully instantiated in another translation unit, then the
    rebuild will work.

    But it doesn't really matter. You have undefined behavior, and
    you should fix it.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Oct 1, 2007
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Icosahedron

    Pimpl Idiom

    Icosahedron, Nov 20, 2003, in forum: C++
    Replies:
    7
    Views:
    672
    Icosahedron
    Nov 22, 2003
  2. pmatos
    Replies:
    6
    Views:
    23,705
  3. Replies:
    8
    Views:
    1,877
    Csaba
    Feb 18, 2006
  4. Javier
    Replies:
    2
    Views:
    533
    James Kanze
    Sep 4, 2007
  5. Rushikesh Joshi
    Replies:
    0
    Views:
    338
    Rushikesh Joshi
    Jul 10, 2004
Loading...

Share This Page