C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

this used in base member initialiser list.

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Andrew Maclean
Guest





PostPosted: Mon Sep 29, 2003 6:31 am    Post subject: this used in base member initialiser list. Reply with quote



Please look at the sample code below first.

I understand the problem but I don't know how to get around it. I can see
that I am passing a pointer to an unconsructed object to another
constructor. How can I ensure that all construction is complete before using
the "this" pointer. Can someone give me some help on this?

Thanks in advance, Andrew.


Look at this example code:

// this class is an ultimate stream associated with a socket
template <class charT, class traits = std::char_traits
class TCPGenericStream :
private TCPStreamBuffer<charT, traits>,
public std::basic_iostream<charT, traits>
{
public:

// this constructor takes 'ownership' of the socket wrapper if btakeowner
== true,
// so that the socket will be closed in the destructor of the
// TCPStreamBuffer object
explicit TCPGenericStream(TCPSocketWrapper &sock, bool takeowner = false)
: TCPStreamBuffer<charT, traits>(sock, takeowner),
std::basic_iostream<charT, traits>(this)
{
}

private:
// not for use
TCPGenericStream(const TCPGenericStream&);
TCPGenericStream& operator=(const TCPGenericStream&);
};


and

// this is even more specialized for use as a client
template <class charT, class traits = std::char_traits
class TCPGenericClientStream :
private TCPSocketWrapper,
public TCPGenericStream<charT, traits>
{
public:

TCPGenericClientStream(const char *address, int port)
: TCPGenericStream<charT, traits>(*this, false)
{
TCPSocketWrapper::connect(address, port);
}

private:
// not for use
TCPGenericClientStream(const TCPGenericClientStream&);
TCPGenericClientStream& operator=(const TCPGenericClientStream&);
};




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
David B. Held
Guest





PostPosted: Mon Sep 29, 2003 9:22 pm    Post subject: Re: this used in base member initialiser list. Reply with quote



"Andrew Maclean" <itsme (AT) gok (DOT) com> wrote

Quote:
[...]
explicit TCPGenericStream(TCPSocketWrapper &sock,
bool takeowner = false)
: TCPStreamBuffer<charT, traits>(sock, takeowner),
std::basic_iostream<charT, traits>(this)
{
}
[...]

I'm not sure why basic_iostream() needs an argument at all. Is it
not sufficient to allow it to be default-c'ted?

Dave



---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system ([url]http://www.grisoft.com)[/url].
Version: 6.0.518 / Virus Database: 316 - Release Date: 9/11/2003



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David B. Held
Guest





PostPosted: Mon Sep 29, 2003 9:35 pm    Post subject: Re: this used in base member initialiser list. Reply with quote



"Andrew Maclean" <itsme (AT) gok (DOT) com> wrote

Quote:
[...]
explicit TCPGenericStream(TCPSocketWrapper &sock,
bool takeowner = false)
: TCPStreamBuffer<charT, traits>(sock, takeowner),
std::basic_iostream<charT, traits>(this)
{
}
[...]

Ok, after taking a closer look at basic_iostream<>, I see that it
requires a streambuf pointer. However, the reason you can't
use 'this' to pass the streambuf pointer is that the iostream desn't
have a streambuf to begin with! Hence, the need to give it a
pointer. Instead, you should decide what kind of streambuf
you wish to give it, create a new one, and pass that instead.

You could probably add a streambuf object to your class,
and pass the address of that to basic_iostream<>. Make sure
the streambuf is declared first, however, so that it gets c'ted
first.

Unless, of course, basic_iostream<> takes ownership of the
streambuf, in which case you could just do a 'new streambuf'
instead. I don't have a good reference available right now, so
I can't tell which is the case.

Dave



---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system ([url]http://www.grisoft.com)[/url].
Version: 6.0.518 / Virus Database: 316 - Release Date: 9/11/2003



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Ron Natalie
Guest





PostPosted: Mon Sep 29, 2003 10:07 pm    Post subject: Re: this used in base member initialiser list. Reply with quote


"Andrew Maclean" <itsme (AT) gok (DOT) com> wrote


Quote:
I understand the problem but I don't know how to get around it. I can see
that I am passing a pointer to an unconsructed object to another
constructor. How can I ensure that all construction is complete before using
the "this" pointer. Can someone give me some help on this?

It's up to you to make sure this doesn't happen. Generally, if you know
the potentially uninitialized parts of the object aren't used in the rest of
the construction, you're safe. If not, you have to use some condition
variable that is known to be initialized to indicate when it is safe to do so.



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Maciej Sobczak
Guest





PostPosted: Mon Sep 29, 2003 10:12 pm    Post subject: Re: this used in base member initialiser list. Reply with quote

Hi,

Andrew Maclean wrote:
Quote:
Please look at the sample code below first.

I understand the problem but I don't know how to get around it.

There is probably no problem to get around. ;)

Quote:
I can see
that I am passing a pointer to an unconsructed object to another
constructor. How can I ensure that all construction is complete before using
the "this" pointer. Can someone give me some help on this?

In the following code:

Quote:
// this class is an ultimate stream associated with a socket
template <class charT, class traits = std::char_traits class TCPGenericStream :
private TCPStreamBuffer public std::basic_iostream<charT, traits
{
public:
explicit TCPGenericStream(TCPSocketWrapper &sock, bool takeowner = false)
: TCPStreamBuffer std::basic_iostream<charT, traits>(this)
{
}

the std::basic_iostream<charT, traits> base-class subobject is
initialized with the this pointer. At the time of the initialization,
the TCPGenericStream is not yet fully constructed (of course).
This is the problem you want to avoid.

Please note that the std::basic_iostream's constructor expects the
std::basic_streambuf pointer as an argument. So - what you pass as the
"this" pointer is not really a pointer to the TCPGenericStream (the
final class), but a pointer *converted* to the pointer to base class: it
is a pointer to std::basic_streambuf, which is a base class of
TCPStreamBuffer, which is a base class of TCPGenericStream.
The question now is: is the "this" pointer, when converted to the
pointer to the base class suitable here?

Incidentally (and in fact, this is the idea of this code), base classes
are initialized in the order of their appearance in the base-class list
- this is why TCPStreamBuffer is lister *before* std::basic_iostream in
the list of base classes.

So, the following actions take place:
1. The TCPStreamBuffer base class subobject is constructed. The
TCPStreamBuffer inherits from std::basic_streambuf, which means that
this subobject is also fully constructed in this step.
2. The std::basic_iostream base class subobject is then constructed with
the "this" pointer given as the constructor's argument. This pointer is
converted to the base class pointer of type std::basic_streambuf. In
other words, std:basic_iostream gets the pointer to the already fully
constructed std::basic_streambuf object.
3. The rest of TCPGenericStream is constructed (which means that the
empty constructor body is executed).

The final question is: can the "this" pointer be converted to the base
class even if the final object is not fully constructed? The answer is:
yes, it can be converted to the base class pointer, if the base class
(the one to which we convert) is already fully constructed (I believe
that 12.7/2 covers this, but maybe some guru will bring more light on
this). If you change the order of base classes, the code will break.

The code above is one of the rare occasions where private inheritance
can be useful - to ensure that part of the object is constructed before
the base class subobject.

The alternative to this approach is to use the init() function from the
std::basic_ios (which is an indirect base class of std::basic_iostream),
called from the constructor.

Something along the line (not tested):

template <class charT, class traits = std::char_traits
class TCPGenericStream : public std::basic_iostream<charT, traits>
{
public:
explicit TCPGenericStream(TCPSocketWrapper &sock, bool takeowner =
false)
: buffer_(sock, takeowner)
{
init(buffer_);
}

// ...

private:
TCPStreamBuffer<charT, traits> buffer_;
}

Here, the following actions take place:
1. the std::basic_iostream base class subobject is constructed with the
default constructor
2. the buffer_ member object is constructed
3. the std::basic_ios indirect base class subobject is initialized with
the pointer to std::basic_streambuf (taken as the address of already
constructed buffer_ object)

Of course, this alternative is possible only because std::basic_ios
provides the special function init(). For classes that do not have such
a function, the base-classes-trick can be a valid solution.


I expect that your question arouse after seeing MSVC++ warnings about
using "this" in the initializer list. Just ignore this warning, it is
not helpful here.


It can be interesting for you that Alex Vinokur has written an extensive
demo program using the socket streams wrapper, which is the source of
the code fragment in your post. It is available from his web-page:

http://alexvn.freeservers.com/s1/sock.html


--
Maciej Sobczak
http://www.maciejsobczak.com/ (temporarily not working)


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Dietmar Kuehl
Guest





PostPosted: Mon Sep 29, 2003 10:52 pm    Post subject: Re: this used in base member initialiser list. Reply with quote

"Andrew Maclean" <itsme (AT) gok (DOT) com> wrote:
Quote:
explicit TCPGenericStream(TCPSocketWrapper &sock, bool takeowner = false)
: TCPStreamBuffer<charT, traits>(sock, takeowner),
std::basic_iostream<charT, traits>(this)
{
}

I guess your concern is about this use of 'this'. Well, the IOStream
constructors and destructors are not allowed to do anything with the
except storing them. Thus, there is no harm in passing the 'this'
pointer in this case.

Apparently, you want to play safe and be sure everything is constructed.
Since you don't really need to pass a pointer to the class under
construction but actually only to subobject, you can take advantage
of the order in which base classes are constructed:

- first, all (direct and indirect) virtual base classes are constructed
(left to right).
- second, base classes are constructed (left to right).

So the first subobject being constructed is the leftmost virtual base
class. Since 'std::basic_ios' is a virtual base class of the other
stream casses, it is really necessary to become the first subobject
being constructed. My personal preference is to make the stream buffer
also a member object which will arrange for the stream buffer being
completely constructed and also avoids the potential of accidentally
overriding a virtual function like eg. 'sync()':

struct somesbuf_pbase {
somesbuf(int someargs): m_sbuf(someargs) {}
private:
somesbuf m_sbuf;
};

struct someostream:
private virtual somesbuf_pbase,
public std::ostream {
someostream(int someargs):
somesbuf_pbase(someargs),
std::ios(&m_sbuf),
std::ostream(&m_sbuf)
{
}
};
--
<mailto:dietmar_kuehl (AT) yahoo (DOT) com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Pete Becker
Guest





PostPosted: Mon Sep 29, 2003 10:54 pm    Post subject: Re: this used in base member initialiser list. Reply with quote

"David B. Held" wrote:
Quote:

"Andrew Maclean" <itsme (AT) gok (DOT) com> wrote in message
news:bl866l$10b$1 (AT) spacebar (DOT) ucc.usyd.edu.au...
[...]
explicit TCPGenericStream(TCPSocketWrapper &sock,
bool takeowner = false)
: TCPStreamBuffer<charT, traits>(sock, takeowner),
std::basic_iostream<charT, traits>(this)
{
}
[...]

I'm not sure why basic_iostream() needs an argument at all. Is it
not sufficient to allow it to be default-c'ted?


basic_iostream doesn't have a default constructor. It's constructor
takes a basic_streambuf*.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Siemel Naran
Guest





PostPosted: Tue Sep 30, 2003 10:35 am    Post subject: Re: this used in base member initialiser list. Reply with quote

"Andrew Maclean" <itsme (AT) gok (DOT) com> wrote in message

Quote:
I understand the problem but I don't know how to get around it. I can see
that I am passing a pointer to an unconsructed object to another
constructor. How can I ensure that all construction is complete before using
the "this" pointer. Can someone give me some help on this?

I don't think it's a problem in many cases. In iostreams you just
pass the address of 'this' to the base class. As long as the base
class stores the pointer then you're safe.

Are you using MSVC?

In that case consider either turning off the warning in the IDE
options box or the following pragma:

explicit TCPGenericStream(TCPSocketWrapper &sock, bool takeowner =
false)
:
TCPStreamBuffer<charT, traits>(sock, takeowner),
#pragma warning(disable: 4355) /* to suppress warning on NT: 'this' :
used in base member initializer list */
std::basic_iostream<charT, traits>(this)
#pragma warning(default: 4355)
{
}


-------------------------------------
Siemel Naran

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Shannon Barber
Guest





PostPosted: Tue Sep 30, 2003 10:51 am    Post subject: Re: this used in base member initialiser list. Reply with quote

"Andrew Maclean" <itsme (AT) gok (DOT) com> wrote

Quote:
Please look at the sample code below first.

I understand the problem but I don't know how to get around it. I can see
that I am passing a pointer to an unconsructed object to another
constructor. How can I ensure that all construction is complete before using
the "this" pointer. Can someone give me some help on this?

The answer is simple, wait for the constructor to complete.
That warning can be benign, say you copy this in the sub-class for use
later. So-long-as you don't use it in the ctor, you're OK.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Dietmar Kuehl
Guest





PostPosted: Tue Sep 30, 2003 9:24 pm    Post subject: Re: this used in base member initialiser list. Reply with quote

Pete Becker <petebecker (AT) acm (DOT) org> wrote:
Quote:
"David B. Held" wrote:
I'm not sure why basic_iostream() needs an argument at all. Is it
not sufficient to allow it to be default-c'ted?

basic_iostream doesn't have a default constructor. It's constructor
takes a basic_streambuf*.

I think David was actually asking why there is no default constructor for
'std::basic_[i][o]stream'. A fair question: it would be perfectly reasonable
to initialize the stream buffer with '0' and setting 'badbit'. Actually, the
whole IOStream initialization seems to be predating clarification of base
class construction order: what other reason could the strange requirement
for calling 'init()' have?

Maybe this is an issue for the embarassment removal Bjarne was talking of:
currently it is possible to trigger undefined behavior when failing to call
'init()' from a class derived from 'std::basic_ios'. On the other hand, the
idiom of putting a stream buffer into the left-most private virtual base
class works pretty well.
--
<mailto:dietmar_kuehl (AT) yahoo (DOT) com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Maciej Sobczak
Guest





PostPosted: Wed Oct 01, 2003 8:14 am    Post subject: Re: this used in base member initialiser list. Reply with quote

Hi,

Dietmar Kuehl wrote:

Quote:
- first, all (direct and indirect) virtual base classes are constructed
(left to right).
- second, base classes are constructed (left to right).

So the first subobject being constructed is the leftmost virtual base
class. Since 'std::basic_ios' is a virtual base class [...]

I have to admit that your post caused me to fall down from my chair.

Indeed - std::basic_ios is a *virtual* (although indirect) base class of
std::basic_iostream, which means that in the original code, the true
order of initialization is different than the author (*) of the code
assumed. Even though the TCPStreamBuffer is listed *before*
std::basic_iostream in the base class list, it will be constructed
before std::basic_iostream, but *after* std::basic_ios, just because
std::basic_ios is a virtual base class.

The case is important in that the code is alive for around two years
(see also December 2001 issue of CUJ, Listing 5 on p. 25) and even
though it is rather public, nobody noticed...

To fix the code, it is necessary to either make the streambuf a
*virtual* base class of the final class (like in the code you presented)
or use the std::basic_ios::init function from the final class
constructor's body.


(*) Yes, it is mea culpa. :)

--
Maciej Sobczak
http://www.maciejsobczak.com/ (temporarily not working)


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Siemel Naran
Guest





PostPosted: Wed Oct 01, 2003 8:36 am    Post subject: Re: this used in base member initialiser list. Reply with quote

"Dietmar Kuehl" <dietmar_kuehl (AT) yahoo (DOT) com> wrote in message

Quote:
So the first subobject being constructed is the leftmost virtual base
class. Since 'std::basic_ios' is a virtual base class of the other
stream casses, it is really necessary to become the first subobject
being constructed. My personal preference is to make the stream buffer
also a member object which will arrange for the stream buffer being
completely constructed and also avoids the potential of accidentally
overriding a virtual function like eg. 'sync()':

struct somesbuf_pbase {
somesbuf(int someargs): m_sbuf(someargs) {}
private:
somesbuf m_sbuf;
};

struct someostream:
private virtual somesbuf_pbase,
public std::ostream {
someostream(int someargs):
somesbuf_pbase(someargs),
std::ios(&m_sbuf),
std::ostream(&m_sbuf)
{
}
};

This is a good solution. Can you think of a way to get rid off the error
message for another case

class A {
public:
explicit A(X *);
};

class X {
A a;
public:
X() : a(this) { } // generates warning in MSVC
};

--
+++++++++++
Siemel Naran



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Andrew Maclean
Guest





PostPosted: Thu Oct 02, 2003 10:11 am    Post subject: Re: this used in base member initialiser list. Reply with quote

This has been a very stimulating discussion. Arising from (what I thought)
was a fairly simple question. Thanks to Dietmar Kuehl and also to Maciej
Sobczak (whose ccode it is) for responding.

I have a much clearer picture now.

This has helped my understanding of the construction of classes a lot!



Thanks
Andrew



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Thu Oct 02, 2003 11:06 pm    Post subject: Re: this used in base member initialiser list. Reply with quote

Maciej Sobczak <maciej (AT) maciejsobczak (DOT) com> wrote


Quote:
Dietmar Kuehl wrote:

- first, all (direct and indirect) virtual base classes are constructed
(left to right).
- second, base classes are constructed (left to right).

So the first subobject being constructed is the leftmost virtual
base class. Since 'std::basic_ios' is a virtual base class [...]

I have to admit that your post caused me to fall down from my chair.

Indeed - std::basic_ios is a *virtual* (although indirect) base class
of std::basic_iostream, which means that in the original code, the
true order of initialization is different than the author (*) of the
code assumed. Even though the TCPStreamBuffer is listed *before*
std::basic_iostream in the base class list, it will be constructed
before std::basic_iostream, but *after* std::basic_ios, just because
std::basic_ios is a virtual base class.

The problem is easily solved, however, by using virtual inheritance for
the streambuf. Now that the issue has been mentionned, I have a vague
recollection that the idiom was first presented (by Dietmar) using
virtual inheritance. I'd forgotten that point, however, when I wrote
the iostream classes at my site:-).

The error is serious. The default constructor of basic_ios will be
called (since the most derived class has no initializer). According to
the standard, the effects of the default constructor are:

Constructs an object of class basic_ios leaving its member objects
uninitialized. The object must be initialized by calling its init
member function. If it is destroyed before it has been initialized
the behavior is undefined.

Presumable, the constructor for basic_iostream will call init --
otherwise, the code wouldn't have worked to begin with. But if the
constructor of the streambuf throws an exception, since as far as the
compiler is concerned, basic_ios has been fully constructed, its
destructor will be called. And undefined behavior will result.

I'm not sure I like this at all. Practically speaking, it means that
inheriting from any of the iostream classes is fraught with danger --
you MUST ensure that no exception can be thrown between the constructor
of basic_ios and the constructor of istream, ostream or iostream.

Quote:
The case is important in that the code is alive for around two years
(see also December 2001 issue of CUJ, Listing 5 on p. 25) and even
though it is rather public, nobody noticed...

I've been using the idiom for longer than that, I think -- my code first
appeared in "C++ Reports", Sept., 1998 (and I originally wrote it long
before that). A quick glance at the article shows that I had already
forgotten the virtual in the inheritance.

Quote:
To fix the code, it is necessary to either make the streambuf a
*virtual* base class of the final class (like in the code you
presented) or use the std::basic_ios::init function from the final
class constructor's body.

To Dietmar's credit (and I'm pretty sure he is the original inventor of
the idea), I think he did use virtual inheritance when he originally
presented it. It just got lost in the copying.

--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.