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 

How to give access to const-interface of a base-class?

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





PostPosted: Sun Feb 22, 2004 1:40 am    Post subject: How to give access to const-interface of a base-class? Reply with quote




When working on a reference-counting smart pointer-class I stumbled on a
tricky situation I don't know how to solve:

I want to be able to implicitly convert a RefPtr<T> & into a const
RefPtr<const T> &. Such a conversion operator would allow me to
implement the standard rules of constness and pointers.

Ideally I would like to do have a "const-inheritance" as follows:

template<typename T>
struct RefPtr<T>;

template<typename T>
struct RefPtr<const T> {
/* ... */
};

template<typename T>
struct RefPtr: public const RefPtr<const T> {
/* ... */
};

Now, I know that this kind of "const-inheritance" is not allowed (but I
wonder why, because I have seen many other cases where this would have
been useful and I can't see any problems with allowing such a feature in
c++). Anyway, the next thing I tried is private inheritance and a
conversion operator member:

template<typename T>
struct RefPtr<T>;

template<typename T>
struct RefPtr<const T> {
/* ... */
};

template<typename T>
struct RefPtr: private RefPtr<const T> {
operator const RefPtr<const T> &() const { return *this; }
/* ... */
};

It turns out that the compilers I have tried (gcc 3.3.1 and Intel icc 8.0)
do accept this code, but the overloading of the implicit conversion
operator is silently ignored, so later on when trying to compile the line

RefPtr<int> i;
RefPtr<const int> j;
j = i;

I get compilation error (gcc 3.3.1):

error: `RefPtr<const int>' is an inaccessible base of `RefPtr<int>'

So, the only solution I have found is using membership instead of
inheritance as follows:

template<typename T>
struct RefPtr<T>;

template<typename T>
struct RefPtr<const T> {
/* ... */
};

template<typename T>
struct RefPtr: private RefPtr<const T> {
operator const RefPtr<const T> &() const { return const_ptr; }
/* ... */
private:
RefPtr<const T> const_ptr;
};

All member functions of RefPtr<const T> must now be re-implemented in
RefPtr<T> as wrappers calling the same member functions in the const_ptr
member object. This seems like an ugly solution, and potentially
inefficient (depending on the compilers optimization capabilities to
inline all wrapper functions and efficiently eliminate any unnecessary
copying of arguments and return values).

So, my question is, is there a more clever way than my solution?

Is there any particular reason for not allowing const inheritance?

Is there any particular reason for allowing but ignoring conversion
operators to protected base classes? IMHO this should either be disallowed
or it should be allowed and work as expected.

Thank you.

Regards
/Oskar

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





PostPosted: Sun Feb 22, 2004 10:57 am    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote




"Oskar Enoksson" <enok (AT) lysator (DOT) liu.se> wrote

Quote:

When working on a reference-counting smart pointer-class I stumbled
on a
tricky situation I don't know how to solve:

I want to be able to implicitly convert a RefPtr<T> & into a const
RefPtr<const T> &. <snip

Why? reference counted smart pointers are meant to be easy to copy. If
you have a RefPtr define one and initialize it with the RefPtr<T> (assuming you have
defined conveting constructors). You will have two distinct smart
pointers, but they will control the same resource. What more do you
need?

Jonathan



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

Back to top
Oskar Enoksson
Guest





PostPosted: Sun Feb 22, 2004 5:29 pm    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote



Jonathan Turkanis wrote:
Quote:
"Oskar Enoksson" <enok (AT) lysator (DOT) liu.se> wrote in message
news:Pine.GSO.4.51L2.0402212037140.13741 (AT) nazgul (DOT) lysator.liu.se...

When working on a reference-counting smart pointer-class I stumbled
on a
tricky situation I don't know how to solve:

I want to be able to implicitly convert a RefPtr<T> & into a const
RefPtr<const T> &. <snip

Why? reference counted smart pointers are meant to be easy to copy. If
you have a RefPtr define one and initialize it with the RefPtr<T> (assuming you have
defined conveting constructors). You will have two distinct smart
pointers, but they will control the same resource. What more do you
need?

But copying a reference-counting pointer will generate a count-up and a
count-down of the reference-counter, right? And this will (in many
cases) force the compiler to invalidate some integers cached in
registers and re-read them from memory because of potential aliasing
problems?

I just think it would be nice to avoid copying of reference pointers
wherever possible (for efficiency-reasons). For instance in trivial
member access functions, such as the following:

struct Bar;

struct Foo {
const RefPtr<Bar> & GetBar() { return bar; }
const RefPtr<const Bar> & GetBar() const { return bar; }
private:
RefPtr<Bar> bar;
};

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

Back to top
David Baraff
Guest





PostPosted: Mon Feb 23, 2004 11:51 am    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

Oskar Enoksson <enok_tabortmig (AT) lysator (DOT) liu.se> wrote

Quote:
Jonathan Turkanis wrote:
"Oskar Enoksson" <enok (AT) lysator (DOT) liu.se> wrote in message
news:Pine.GSO.4.51L2.0402212037140.13741 (AT) nazgul (DOT) lysator.liu.se...

When working on a reference-counting smart pointer-class I stumbled
on a
tricky situation I don't know how to solve:

I want to be able to implicitly convert a RefPtr<T> & into a const
RefPtr<const T> &. <snip

Why? reference counted smart pointers are meant to be easy to copy. If
you have a RefPtr define one and initialize it with the RefPtr<T> (assuming you have
defined conveting constructors). You will have two distinct smart
pointers, but they will control the same resource. What more do you
need?

But copying a reference-counting pointer will generate a count-up and a
count-down of the reference-counter, right?


Yes. But if you follow your argument to its logical conclusion, then
RefPtr<Derived> should inherit from RefPtr<Base>, if Derived inherits
from Base. After all, if you have a RefPtr<Derived>, if would be real
nice to pass it to a function which wants a RefPtr<Base> without
making an extra copy, just to avoid the bump-up/bump-down.

IMHO, this is more trouble than it's worth. vector<Base> and
vector<Derived> have no inheritance relationship, nor should
RefPtr<Base> and RefPtr<Derived>, and from that, neither should
RefPtr<const Base> and RefPtr<Base>.

Please note that I would *love* it if there was an easy way to pass a
RefPtr<Derived> to a function wanting a RefPtr<Base> without making an
extra copy. In fact, I also code functions as

void Func(const RefPtr<Base>& ptr) {
...
}

and was surprised to find out that there was a lot of ref-count
increment/decrementing going on, until someone pointed out to me it
was coming from passing in a RefPtr<Derived>, and the compiler needing
to generate a temporary. I thought for a while about how to avoid it
and decided there was no good way.

Feel free to convince me otherwise. :)

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

Back to top
Jarkko Lempiainen
Guest





PostPosted: Tue Feb 24, 2004 1:39 pm    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

The following works in VC .NET 2003 atleast:

template<typename T_>
struct Ptr
{
template<typename U_> operator Ptr<U_>&()
{
sizeof(static_cast<U_*>((T_*)0)); // check for proper ptr conversion
return reinterpret_cast<Ptr(*this);
}
};

struct A{};
struct B: A {};
struct C {};

void foo1(const Ptr<const B>&) {}
void foo2(const Ptr<A>&) {}
void foo3(const Ptr<const A>&) {}
void foo4(const Ptr<C>&) {}

int main()
{
Ptr<B> p;
foo1(p); // ok
foo2(p); // ok
foo3(p); // ok
foo4(p); // error
return 0;
}

Jarkko


"David Baraff" <deb (AT) pixar (DOT) com> wrote

Quote:
Oskar Enoksson <enok_tabortmig (AT) lysator (DOT) liu.se> wrote

Jonathan Turkanis wrote:
"Oskar Enoksson" <enok (AT) lysator (DOT) liu.se> wrote in message
news:Pine.GSO.4.51L2.0402212037140.13741 (AT) nazgul (DOT) lysator.liu.se...

When working on a reference-counting smart pointer-class I
stumbled
on a
tricky situation I don't know how to solve:

I want to be able to implicitly convert a RefPtr<T> & into a const
RefPtr<const T> &. <snip

Why? reference counted smart pointers are meant to be easy to copy.
If
you have a RefPtr define one and initialize it with the RefPtr<T> (assuming you have
defined conveting constructors). You will have two distinct smart
pointers, but they will control the same resource. What more do you
need?

But copying a reference-counting pointer will generate a count-up and a
count-down of the reference-counter, right?


Yes. But if you follow your argument to its logical conclusion, then
RefPtr<Derived> should inherit from RefPtr<Base>, if Derived inherits
from Base. After all, if you have a RefPtr<Derived>, if would be real
nice to pass it to a function which wants a RefPtr<Base> without
making an extra copy, just to avoid the bump-up/bump-down.

IMHO, this is more trouble than it's worth. vector<Base> and
vector<Derived> have no inheritance relationship, nor should
RefPtr<Base> and RefPtr<Derived>, and from that, neither should
RefPtr<const Base> and RefPtr<Base>.

Please note that I would *love* it if there was an easy way to pass a
RefPtr<Derived> to a function wanting a RefPtr<Base> without making an
extra copy. In fact, I also code functions as

void Func(const RefPtr<Base>& ptr) {
...
}

and was surprised to find out that there was a lot of ref-count
increment/decrementing going on, until someone pointed out to me it
was coming from passing in a RefPtr<Derived>, and the compiler needing
to generate a temporary. I thought for a while about how to avoid it
and decided there was no good way.



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

Back to top
David Baraff
Guest





PostPosted: Wed Feb 25, 2004 3:12 pm    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

"Jarkko Lempiainen" <jarkko (AT) videotron (DOT) ca> wrote

Quote:
The following works in VC .NET 2003 atleast:

template<typename T_
struct Ptr
{
template {
sizeof(static_cast<U_*>((T_*)0)); // check for proper ptr conversion
return reinterpret_cast<Ptr(*this);
}
};

struct A{};
struct B: A {};
struct C {};

void foo1(const Ptr<const B>&) {}
void foo2(const Ptr<A>&) {}
void foo3(const Ptr<const A>&) {}
void foo4(const Ptr<C>&) {}

int main()
{
Ptr<B> p;
foo1(p); // ok
foo2(p); // ok
foo3(p); // ok
foo4(p); // error
return 0;
}

Jarkko


Your solution will fail in the case of multiple inheritance -- if the
raw address of Derived and Base differ, what you propose will hose
you.

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

Back to top
Oskar Enoksson
Guest





PostPosted: Thu Feb 26, 2004 12:19 am    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

Jarkko Lempiainen wrote:
Quote:
The following works in VC .NET 2003 atleast:

template<typename T_
struct Ptr
{
template {
sizeof(static_cast<U_*>((T_*)0)); // check for proper ptr conversion
return reinterpret_cast<Ptr(*this);
}
};


Actually I finally implemented something along this line, but I (humbly)
think I have an even better solution. Above, you return a non-const
reference, this is bad because you may then assign that reference with a
pointer to an unrelated derived type.

Secondly, your way of checking for proper ptr conversion will allow
casting that are not normally allowed implicit casts. My way (below)
will work exactly in those cases where the corresponding built-in
pointer implicit cast works.

The only remaining problem I see is that the result of reinterpret_cast
between pointers to two unrelated instantiations of a template class is
in theory undefined (AFIK). However, I would be very interested to know
about any compiler with any optimization flag combination that will
break this code. I have some arguments for that such a compiler must
either have some almost supernatural strict-alias-based optimization
method, or it must be extremely irrational.

struct ReferenceCounter;

template<typename T>
class rp
{
ReferenceCounter *ref_counter; // Reference counter object
T *p; // Pointer to data

template<typename TT>
static inline void Dummy(TT * const &) {}

public:
template<typename TT>
operator const rp<TT> &() const
{
Dummy(p); // check for proper ptr onversion
return reinterpret_cast<const rp(*this);
}
/* ... */
};

For anyone interested I published the complete implementation and
documentation at http://www.lysator.liu.se/~enok/software/rp/ I'd be
glad to hear peoples opinion on this. How "dangerous" is it?

/Oskar

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

Back to top
Jarkko Lempiainen
Guest





PostPosted: Fri Feb 27, 2004 2:11 am    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

Yes, you are absolutely right, but you could prevent such casts.
MI is evil anyway ;)

Jarkko

"David Baraff" <deb (AT) pixar (DOT) com> wrote

Quote:

Your solution will fail in the case of multiple inheritance -- if the
raw address of Derived and Base differ, what you propose will hose
you.


[ 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: Fri Feb 27, 2004 2:44 pm    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

[email]deb (AT) pixar (DOT) com[/email] (David Baraff) wrote in message
news:<37483324.0402242020.5a7e8246 (AT) posting (DOT) google.com>...
Quote:
"Jarkko Lempiainen" <jarkko (AT) videotron (DOT) ca> wrote in message
news:<ZMA_b.34436$FO1.1354881 (AT) weber (DOT) videotron.net>...

The following works in VC .NET 2003 atleast:

template<typename T_
struct Ptr
{
template {
sizeof(static_cast<U_*>((T_*)0)); // check for proper ptr conversion
return reinterpret_cast<Ptr(*this);
}
};

struct A{};
struct B: A {};
struct C {};

void foo1(const Ptr<const B>&) {}
void foo2(const Ptr<A>&) {}
void foo3(const Ptr<const A>&) {}
void foo4(const Ptr<C>&) {}

int main()
{
Ptr<B> p;
foo1(p); // ok
foo2(p); // ok
foo3(p); // ok
foo4(p); // error
return 0;
}

Your solution will fail in the case of multiple inheritance -- if the
raw address of Derived and Base differ,

Which may be the case even in the case of single inheritance, at least
according to the standard.

Quote:
what you propose will hose you.

His proposition contains undefined behavior. I don't know what it is
intended to do, but it doesn't do it.

--
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
Jarkko Lempiainen
Guest





PostPosted: Fri Feb 27, 2004 6:21 pm    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

Obviously you need both const & non-const versions of the implicit
conversion op. I just didn't bother to write both (: I don't see what
would be the problem with unrelated type though, because you
always check for the valid conversion. Could you explain with an
example how does it allow conversion that are not normally allowed?

I don't really see any advantages in the alternative you propose.
Infact your code _allows_ incorrect casts (i.e. the foo4 passes
with your code, which is wrong).

Jarkko

"Oskar Enoksson" <enok_tabortmig (AT) lysator (DOT) liu.se> wrote

Quote:

Actually I finally implemented something along this line, but I (humbly)
think I have an even better solution. Above, you return a non-const
reference, this is bad because you may then assign that reference with a
pointer to an unrelated derived type.

Secondly, your way of checking for proper ptr conversion will allow
casting that are not normally allowed implicit casts. My way (below)
will work exactly in those cases where the corresponding built-in
pointer implicit cast works.


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

Back to top
Oskar Enoksson
Guest





PostPosted: Sat Feb 28, 2004 12:15 pm    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

Jarkko Lempiainen wrote:
Quote:
Obviously you need both const & non-const versions of the implicit
conversion op. I just didn't bother to write both (: I don't see what
would be the problem with unrelated type though, because you
always check for the valid conversion. Could you explain with an
example how does it allow conversion that are not normally allowed?

struct A {};
struct B1: public A {};
struct B2: public A {};

Ptr<B1> p1;
Ptr<A> &rp1(p1); // Cast as you want to allow it
rp1 = new B2();

Now p1 points to an object of type B2 even though it is of type Ptr<B1>.
This is not good! This is also the reason for the following implicit
cast rules for built-in pointer types:

foo1(A * const &p) {}
foo2(A * &p) {}

int main() {
B1 *p;
foo1(p);
// Works! B1 * & can be implicitly cast to a A * const &
foo2(p);
// Fails! B1 * & cannot be implicitly cast to a A * &
}


Quote:
I don't really see any advantages in the alternative you propose.
Infact your code _allows_ incorrect casts (i.e. the foo4 passes
with your code, which is wrong).

There was an error in my code ... sorry ... replace the call Dummy(p);
with Dummy<TT>(p); Then foo4 will fail (as it should).

The advantage is that my call Dummy<TT>(p) requires an implicit cast
from T * & to TT * const & (to match argument in Dummy<TT>(p) ) while
your static_cast<U_*>((T_*)0) is more "brutal". Static cast is more
powerful than implicit cast, for instance you can cast not only to
ancestor type but to sibling type:

struct A {};
struct B1: public A {};
struct B2: public A {};

Ptr<B1> pb1 = new B1();
Ptr<B2> pb2 = pb1;
// Allowed by your static_cast, but not what we want!

I hope I'm thinking right ... do you agree?

/Oskar

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

Back to top
Jarkko Lempiainen
Guest





PostPosted: Sat Feb 28, 2004 12:18 pm    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

I still have to see the compiler where the solution I
proposed doesn't work (: I think it's good to try to
follow the standard, but being a purist will probably
hurt you more than taking the hit of such an unlikely
pathological scenario.

Yet again you can prevent casts which require
pointer adjustment, if not in compile-time, atleast
in run-time. And if you like, you can always place
such non-standard constructs into nonstandard.h (:

I don't do this in my own smart pointer impl. though,
because if you really have to pass the ownership of
an object, you are probably ready to take the hit of
pump-up/pump-down ref counting anyway. Atleast
that's what I have found out in practice.

Jarkko


<kanze (AT) gabi-soft (DOT) fr> wrote

Quote:
deb (AT) pixar (DOT) com (David Baraff) wrote in message

Your solution will fail in the case of multiple inheritance -- if the
raw address of Derived and Base differ,

Which may be the case even in the case of single inheritance, at least
according to the standard.

what you propose will hose you.

His proposition contains undefined behavior. I don't know what it is
intended to do, but it doesn't do it.



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

Back to top
Jarkko Lempiainen
Guest





PostPosted: Mon Mar 01, 2004 12:24 pm    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

Ah, yes, you are absolutely right. For some reason I was completely
blind to the downcasting scenario (: You might want to have slight
improvement to your code though and change the return type of
Dummy to int and do:
sizeof(Dummy<TT>(p));
to make sure it's compile-time expression and doesn't end up to
the run-time code in debug builds/deep inlining.

Jarkko

"Oskar Enoksson" <enok_tabortmig (AT) lysator (DOT) liu.se> wrote

Quote:

I hope I'm thinking right ... do you agree?


[ 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: Mon Mar 01, 2004 12:34 pm    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

"Jarkko Lempiainen" <jarkko (AT) videotron (DOT) ca> wrote


Quote:
I still have to see the compiler where the solution I
proposed doesn't work

And I've yet to see one where it does work in the case of multiple
inheritance. And I've yet to see any serious C++ application that
doesn't use multple inheritance.

--
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
Jarkko Lempiainen
Guest





PostPosted: Wed Mar 03, 2004 11:50 am    Post subject: Re: How to give access to const-interface of a base-class? Reply with quote

Many serious C++ applications don't use MI in scenarios
where it would cause problems in the proposed solution.
I think MI is quite bad argument against use of the technique
anyway, because that's something that's usually avoidable
with composition. I'm not saying MI is totally useless, but
in many places I have seen it used, it is, and only adds
unnecessary complexities to class hierarchies.

So, if you use MI as your argument to reject an useful
techique, it's your call, but I find it quite weak and unpractical.
As I have said few times before, you can detect if someone
is trying to do implicit cast and force to the use of explicit
cast instead.

Jarkko


<kanze (AT) gabi-soft (DOT) fr> wrote

Quote:

And I've yet to see one where it does work in the case of multiple
inheritance. And I've yet to see any serious C++ application that
doesn't use multple inheritance.


[ 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.