469,898 Members | 1,563 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,898 developers. It's quick & easy.

templates & polymorphic classes

One problem I've come accross in designing a specific version of auto_ptr
is that I have to disntiguish between "polymorphic" arguments and "plain" ones,
because the template has to, internally, cast to void *.

Specifically,
template <typename T> void f(T * t) {
void * p = dynamic_cast<void *>(t) ;
}

will not compile if T isn't of a class that has somewhere at least
a virtual function. In particular, none of the STL containers do qualify
for T.

Yet, because of multiple inheritance, if I want to get down to the base
"most" class, the dynamic_cast<void*> is the way to go.

I could use reinterpret_cast instead, and this would work for all T,
except that I'd lose the "cast to base" feature mentionned above.

I could also use a pair of templates, one for polymorphic types,
and one of "POD". But I'd rather not do that.

I'd be happy with a runtime query ... if I could devise one, that is.

typeid() seems to not be applicable in this context.

Ideally, if I could come up with

template <typename T>
bool is_polymorphic(T t) {
...
}

I would be filled with joy :-)

Any taker?
--
JFB

Jul 23 '05 #1
20 1882
verec wrote:
One problem I've come accross in designing a specific version of auto_ptr
is that I have to disntiguish between "polymorphic" arguments and "plain"
ones, because the template has to, internally, cast to void *.

Specifically,
template <typename T> void f(T * t) {
void * p = dynamic_cast<void *>(t) ;
}

will not compile if T isn't of a class that has somewhere at least
a virtual function.
It won't ever work, because dynamic_cast only works within a class
hierarchy, and 'void' doesn't belong to it. It doesn't seem to make much
sense to me either. How and why would you want a dynamic_cast that would
attempt to check whether an object is derived from 'void'? Why not just
static_cast?
In particular, none of the STL containers do qualify
for T.

Yet, because of multiple inheritance, if I want to get down to the base
"most" class, the dynamic_cast<void*> is the way to go.
No. 'void' is not the base class.
I could use reinterpret_cast instead, and this would work for all T,
except that I'd lose the "cast to base" feature mentionned above.
I fail to see what you need that for.
I could also use a pair of templates, one for polymorphic types,
and one of "POD". But I'd rather not do that.

I'd be happy with a runtime query ... if I could devise one, that is.
What for?
typeid() seems to not be applicable in this context.

Ideally, if I could come up with

template <typename T>
bool is_polymorphic(T t) {
...
}

I would be filled with joy :-)

Any taker?


Jul 23 '05 #2
On 2005-06-16 00:26:40 +0100, Rolf Magnus <ra******@t-online.de> said:
will not compile if T isn't of a class that has somewhere at least
a virtual function.


It won't ever work, because dynamic_cast only works within a class
hierarchy, and 'void' doesn't belong to it. It doesn't seem to make much
sense to me either. How and why would you want a dynamic_cast that would
attempt to check whether an object is derived from 'void'? Why not just
static_cast?


I'm sorry I didn't make myself clear enough. An actual example will
probably help. Consider:

template <typename T> struct my_ptr {
void * rawValue ;
my_ptr(T *t) : rawValue(dynamic_cast<void *>(t)) {
}
} ;

struct polymorphic {
virtual ~polymorphic() ;
} ;

struct pod {
} ;

typedef my_ptr<polymorphic> Poly ;
typedef my_ptr<pod> Pod ;

void
test_00005() {
Poly p(new polymorphic) ;
// Pod pp(new pod) ;
}

If I leave the "Pod pp" line commented out, everything compiles and works
fine. However, if I uncomment it, I get:
### error: cannot dynamic_cast 't' (of type 'struct pod*') to type 'void*'
### (source type is not polymorphic)

What I am after, is a way to define 'my_ptr' above so that it deals
correctly with both polymorphic and POD structs. The need for the
dynamic_cast in the case of "polymorphic" structs is because when
MI is used, dynamic_cast guarantees that you get a pointer back to
the base object, no matter what branch of the derivation has been
used for T.

If anything is still unclear, please, let me know.

All I want is a "write once, run anywhere" (TM :-) template that I
can use all over the place, without having to ask first, "Am I dealing
with a pod or a poly?"

Many Thanks.
--
JFB

--
Do your users a favor: give them Style: http://www.uiwithstyle.org

Jul 23 '05 #3
verec wrote:
On 2005-06-16 00:26:40 +0100, Rolf Magnus <ra******@t-online.de> said:
will not compile if T isn't of a class that has somewhere at least
a virtual function.

It won't ever work, because dynamic_cast only works within a class
hierarchy, and 'void' doesn't belong to it. It doesn't seem to make much
sense to me either. How and why would you want a dynamic_cast that would
attempt to check whether an object is derived from 'void'? Why not just
static_cast?

I'm sorry I didn't make myself clear enough. An actual example will
probably help. Consider:

template <typename T> struct my_ptr {
void * rawValue ;
my_ptr(T *t) : rawValue(dynamic_cast<void *>(t)) {


There is no need to use 'dynamic_cast' to convert a pointer to an object
to void*. It's convertible _implicitly_.
}
} ;

struct polymorphic {
virtual ~polymorphic() ;
} ;

struct pod {
} ;

typedef my_ptr<polymorphic> Poly ;
typedef my_ptr<pod> Pod ;

void
test_00005() {
Poly p(new polymorphic) ;
// Pod pp(new pod) ;
}

If I leave the "Pod pp" line commented out, everything compiles and works
fine. However, if I uncomment it, I get:
### error: cannot dynamic_cast 't' (of type 'struct pod*') to type 'void*'
### (source type is not polymorphic)

What I am after, is a way to define 'my_ptr' above so that it deals
correctly with both polymorphic and POD structs. The need for the
dynamic_cast in the case of "polymorphic" structs is because when
MI is used, dynamic_cast guarantees that you get a pointer back to
the base object, no matter what branch of the derivation has been
used for T.
To what base object? The pointer you store is 'void*'!
If anything is still unclear, please, let me know.
Hell, yes. Perhaps you could post the _real_ 'my_ptr' class.
All I want is a "write once, run anywhere" (TM :-) template that I
can use all over the place, without having to ask first, "Am I dealing
with a pod or a poly?"


You should also try to show _how_ you're going to use an object of type
'my_ptr'. "I can use all over the place" is too vague a statement to be
of any value.

V
Jul 23 '05 #4
On 2005-06-16 22:43:49 +0100, Victor Bazarov <v.********@comAcast.net> said:
template <typename T> struct my_ptr {
void * rawValue ;
my_ptr(T *t) : rawValue(dynamic_cast<void *>(t)) {


There is no need to use 'dynamic_cast' to convert a pointer to an object
to void*. It's convertible _implicitly_.


Excellent! I just tried. And it works!

Many Thanks.
--
JFB

Jul 23 '05 #5
On 2005-06-16 22:43:49 +0100, Victor Bazarov <v.********@comAcast.net> said:
There is no need to use 'dynamic_cast' to convert a pointer to an object
to void*. It's convertible _implicitly_.


Browsing Google a bit harder, I stumbled accross:

http://groups-beta.google.com/group/...b20c6aa38940c9

Which

means now I'm not too sure what to think :(

--
Do your users a favor: give them Style: http://www.uiwithstyle.org

Jul 23 '05 #6
[]
There is no need to use 'dynamic_cast' to convert a pointer to an object
to void*. It's convertible _implicitly_. [] To what base object? The pointer you store is 'void*'!


Actually he got it wrong, but not completely:

/dynamic_cast < type-id > ( expression )/
....
/If type-id is void*, a run-time check is made to determine the/
/actual type of expression. The result is a pointer to the complete/
/object pointed to by expression./

So dynamic_cast to "void*" is a distinct language feature.

The point is, that the _value_ of that "void*" will be
comparable wherever the original pointer was cast' from. Which IMHO
can be a handy feature.
Jul 23 '05 #7
> Ideally, if I could come up with

template <typename T>
bool is_polymorphic(T t) {
...
}

I would be filled with joy :-)

Any taker?
--
JFB


::boost::is_polymorphic<T>::value

Look here: http://www.boost.org/libs/type_traits/index.html

The only problem is that the implementation of
::boost::is_polymorphic is basically a hack that relies on
the compiler inserting a vtable-pointer into a polymorphic class,
(and thereby adding to it's size).
Appearently it works with most compilers, but it's way outside
the C++ standard, and there's no guarantee that it will work
as expected under any circumstances or with new versions/revisions
of any compiler you may use today.

But since most boost developers tend to be nifty & smart guys I
think it might be the best way to do it. Which does not mean I'd
use it, but I think chances of finding some better solution are
rather slim.

Of course there might be a better runtime solution - since
::boost::is_polymorphic<T>::value is a compile-time solution. But
then again, what would you do if you have that information at runtime?
If it's a runtime solution you have to have that dynamic_cast
from T to void* somewhere in your program ... even if it would never
be called if T is not polymorphic it's still there and it won't
compile :-(

Or did I miss something?

BTW: dynamic_cast<void*>() will yield the address of the _complete_
object which could be considered the exact opposite of the "most base
type" thing you mentioned.
And: "POD" is not the opposite of "polymorphic" since there are plenty
types that aren't "POD" but aren't "polymorphic" either. E.g.:

struct X { X(); int x; };

is _not_ a POD since it defines a constructor.
Jul 23 '05 #8
On 2005-06-17 02:52:13 +0100, Swampmonster
<sw**********@der-ball-ist-rund.net> said:
::boost::is_polymorphic<T>::value

Look here: http://www.boost.org/libs/type_traits/index.html
Good news.
BTW: dynamic_cast<void*>() will yield the address of the _complete_
object which could be considered the exact opposite of the "most base
type" thing you mentioned.
Sorry to have been misleading. What you just said is what I meant,
what I wrote is ... what you understood anyway :-)
And: "POD" is not the opposite of "polymorphic" since there are plenty
types that aren't "POD" but aren't "polymorphic" either. E.g.:

struct X { X(); int x; };

is _not_ a POD since it defines a constructor.


I shall stand corrected!

My apologies for my sloppy choice of words :(

OK. The summary, as I see now is:

- there is a way to get at the "outer-most" or "complete" object
using dynamic_cast<void *>(T)

- the "implicit conversion to void *" mentionned by Victor does
indeed exist, but does not offer that guarantee. Whether this
implicit cast to void * is equivalent to reinterpret_cast<void *>(T)
is still to be determined, but seems the most logical choice

- There is at least one answer to my problem in the form of
::boost::is_polymorphic<T>::value

Many Thanks.
--
JFB

Jul 23 '05 #9
On 2005-06-17 02:52:13 +0100, Swampmonster
<sw**********@der-ball-ist-rund.net> said:
::boost::is_polymorphic<T>::value

Look here: http://www.boost.org/libs/type_traits/index.html


Nearly there ...

template <typename T> struct is_polymorphic {
struct T1 : public T {
virtual ~T1() {}
} ;

// since this whole rigmarole is supposed to be compile time
// only, there's no reason to reserve space in the "data"
// segment for a "static bool" of some sort, right?
enum {
value = sizeof(T1) != sizeof(T)
} ;
} ;

template <int I, typename T> struct void_star_caster {
static void * cast(T * t) {
return dynamic_cast<void *>(t) ;
}
} ;
template <typename T> struct void_star_caster<0, T> {
static void * cast(T * t) {
return reinterpret_cast<void *>(t) ;
}
} ;

template <typename T> struct my_ptr {

void * rawValue ;

typedef void_star_caster<is_polymorphic<T>::value, T> caster ;
my_ptr(T *t) : rawValue(caster::cast(t)) {

}

} ;

struct polymorphic {

virtual ~polymorphic() ;

} ;

polymorphic::~polymorphic() {}

struct monomorphic {

} ;

typedef my_ptr<polymorphic> Poly ;

typedef my_ptr<monomorphic> Mono ;

void

test_00005() {

Poly p(new polymorphic) ;
Mono m(new monomorphic) ;

}
If I comment out the "Mono m..." line everything compiles
but doesn't quite work: for some reason is_polymorphic<polymorphic>
seems to evaluate to 0.

If I leave the "Mono m" line in, then I get a compile error:

### test_envelope.cpp:174: error: cannot dynamic_cast 't' (of type
### 'struct monomorphic*') to type 'void*' (source type is not
### polymorphic)

Whether I add the following specialization or not doesn't seem
to make any difference:

template <typename T> struct void_star_caster<1, T> {
static void * cast(T * t) {
return dynamic_cast<void *>(t) ;
}
} ;

Puzzled.
(I just didn't grasp, at first, how hard *debugging* templates could be :(
--
JFB

Jul 23 '05 #10
Funny how a single byte can change things ...

It turns out that I got my condition wrong, and set value to non-zero,
meaning: "monomorphic" with the opposite test to what was required.

Once I had changed the != into a ==, as it was meant to be, everything
compiles and runs, and, stepping with the debugger I can see either
reinterpret_cast or dynamic_cast being invoked as I expect them to!

Many thanks!

template <typename T> struct is_polymorphic {
struct T1 : public T {
virtual ~T1() {}
} ;

enum { // ooops! wrote != the first time through ...
value = sizeof(T1) == sizeof(T)
} ;
} ;

template <int I, typename T> struct void_star_caster {
static void * cast(T * t) {
return dynamic_cast<void *>(t) ;
}
} ;
template <typename T> struct void_star_caster<0, T> {
static void * cast(T * t) {
return reinterpret_cast<void *>(t) ;
}
} ;

template <typename T> struct my_ptr {

void * rawValue ;

typedef void_star_caster<is_polymorphic<T>::value, T> caster ;
my_ptr(T *t) : rawValue(caster::cast(t)) {

}

} ;

struct polymorphic {

virtual ~polymorphic() ;

} ;

polymorphic::~polymorphic() {}

struct monomorphic {

} ;

typedef my_ptr<polymorphic> Poly ;

typedef my_ptr<monomorphic> Mono ;

void

test_00005() {

Poly p(new polymorphic) ;
Mono m(new monomorphic) ;

}

--
JFB

Jul 23 '05 #11
My apologies for my sloppy choice of words :(
No problem, happens to me all the time.

OK. The summary, as I see now is:

- there is a way to get at the "outer-most" or "complete" object
using dynamic_cast<void *>(T)

- the "implicit conversion to void *" mentionned by Victor does
indeed exist, but does not offer that guarantee. Whether this
implicit cast to void * is equivalent to reinterpret_cast<void *>(T)
is still to be determined, but seems the most logical choice


The implicit conversion exists, and it's equal to the explicit
static_cast<void*>() thing. The whole reinterpret_cast<T>()
is implementation defined, so I don't think there's a guarantee
as per the standard that it will be equal. So I'd rather use
the implicit conversion - why use an explicit cast when it's
not necessary. For clarity you could write a static_cast,
but I wouldn't use a reinterpret_cast.
Jul 23 '05 #12
verec wrote:
Funny how a single byte can change things ...

It turns out that I got my condition wrong, and set value to non-zero,
meaning: "monomorphic" with the opposite test to what was required.

Once I had changed the != into a ==, as it was meant to be, everything
compiles and runs, and, stepping with the debugger I can see either
reinterpret_cast or dynamic_cast being invoked as I expect them to!

Many thanks!

template <typename T> struct is_polymorphic {
struct T1 : public T {
virtual ~T1() {}
} ;

enum { // ooops! wrote != the first time through ...
value = sizeof(T1) == sizeof(T)
} ;
} ;


IMHO you should really use the original boost code. It's all inline,
so you don't have to link anything - just include the headers and go.
I think there are some situations where your code could lead to
incorrect results because of padding and similar things.
Theoretically the size of T1 could be the same as the size of T,
even if T is not a polymorphic type. If T is empty the compiler
might still allocate 1 or 4 or 8 (or whatever) bytes for it, and then
reuse that storage for the vtable pointer. And the size of T1 could
be bigger then the size of T, even it T is a polymorphic type, since
the compiler might want to give T1 a different address then the
copy of T that T1 contains. The compiler isn't required to do so,
but I don't think that there is any rule that forbids it either.

The boost implementation seems overly complicated at the first look,
but it's all there for a reason. And you don't have to look at it,
just use it :-)
Jul 23 '05 #13
On 2005-06-18 13:18:28 +0100, Swampmonster
<sw**********@der-ball-ist-rund.net> said:
IMHO you should really use the original boost code. It's all inline, [...] The boost implementation seems overly complicated at the first look,
but it's all there for a reason. And you don't have to look at it,
just use it :-)


Fair enough ... but ... don't get me started on boost!

(too late! :)

My experience with boost, so far, is that of a "road to hell paved with
good intentions".

What I mean is that boost 1.32 is both so overly complicated and overly
undocumented (I missed the "is_polymorphic" that your very good self
spotted) that:

- you have to read the implementation to *guess* what is the problem
that they are trying to solve

- this guess turns out wrong more often than not (please, find a decent
explanation & use case for the ``addRef'' parameter of intrusive_ptr,
that will not break your assumption of symmetric behavior of
ctors & dtors)

In addition the over emphasis of "raw performance" over usability
just kills me. They use this "efficiency" argument all the time
to justify poor/badly designed APIs, when they ought to know the
80/20 rules. Granted, which 20% of the code is the bottleneck varies
from application to applicatiom, so there's no clear area in which
a general purpose library like boost could use that rule. Yet my
mantra is "get it to work _first_ with an as clean as possible API,
_then_ optimize, and possibly degrade/corrupt the API". It's always
easier to introduce claimed for features later on, than to remove an
ill thought of one, and break compatibility...

As Churchill reportedly said of democracy ("there's no worse possible
system than democracy, except that there's no better one!"), the same
can be said of C++ in general, and boost in particular.

I hate C++ (divides my productivity by 10x wrt Java or Python) yet,
there's no other choice for ... performance reason, precisely. So
I'm just not interested in getting another 10% speed up with crooked
APIs. It has to be in the 1000% to start getting my attention. And
this 1000% is what I get when going backwards to C++ ...

What this all leads me to, is to use boost as a source of ideas,
and possible implmentation path, while not introducing, in my own
code, gratuitous attempts at API breaking compatibility with them,
just in (the highly improbable) case I would want to switch over
to boost at some later point, but so far ...

I wanted to use boost:;shared_ptr and all
- ended up creating my own envelope because the boost stance
solves the wrong problem for me,

I wanted to use boost::thread and all
- ended up rolling my own pthread based wrappers because of
stupid emphasis on minute details not relevant to the big
picture, and a very wrong stance of where thread synchronization
points ought to appear.

On my list, there is still boost::file & folder and other related
IO stuff that will, no doubt, get down the drain for some related
reasons: I don't hold my breath, though I try to keep an opne mind :)

Sorry for the rant.
--
JFB

Jul 23 '05 #14
On 2005-06-18 13:18:28 +0100, Swampmonster
<sw**********@der-ball-ist-rund.net> said:
verec wrote:
template <typename T> struct is_polymorphic {
struct T1 : public T {
virtual ~T1() {}
} ;

enum { // ooops! wrote != the first time through ...
value = sizeof(T1) == sizeof(T)
} ;
} ;
[...] I think there are some situations where your code could lead to
incorrect results because of padding and similar things.
Theoretically the size of T1 could be the same as the size of T,
even if T is not a polymorphic type. If T is empty the compiler
might still allocate 1 or 4 or 8 (or whatever) bytes for it, and then
reuse that storage for the vtable pointer. And the size of T1 could
be bigger then the size of T, even it T is a polymorphic type, since
the compiler might want to give T1 a different address then the
copy of T that T1 contains. The compiler isn't required to do so,
but I don't think that there is any rule that forbids it either.


I agree that my "stolen" implementation may not be as safe as that
of boost, but as they say, "I'll cross that bridge when I get there".

I'm interested in moving forwards, and laying down a minimal API
that I know either works now, or could be improved later on, not
on spending untold amount of hours getting every single detail right,
just to discover, a few month down the road, that I don't need this
API _at all_ for example: I'm all too weary of the dangers of bottom-
up design.

What I'm doing is iterating from bottom to top and back, in circles,
fleshing out more code on each pass, and thus, preserving the design
integrity of the whole piece.

What I want to avoid is both:
- getting bogged down in irrelevant details that might be optimized
out of existence the next time through
- getting the wrong set of API to build on top of.

Otherwise, your points are well taken, and I'll revisit my implementation
as and when.

Many thanks
--
JFB

Jul 23 '05 #15
On 2005-06-18 13:06:17 +0100, Swampmonster
<sw**********@der-ball-ist-rund.net> said:
the "implicit conversion to void *" mentionned by Victor does
indeed exist, but does not offer that guarantee. Whether this
implicit cast to void * is equivalent to reinterpret_cast<void *>(T)
is still to be determined, but seems the most logical choice


The implicit conversion exists, and it's equal to the explicit
static_cast<void*>() thing. The whole reinterpret_cast<T>()
is implementation defined, so I don't think there's a guarantee
as per the standard that it will be equal. So I'd rather use
the implicit conversion - why use an explicit cast when it's
not necessary. For clarity you could write a static_cast,
but I wouldn't use a reinterpret_cast.


Code changed (and tested!) with static_cast instead.

Many Thanks
--
JFB

Jul 23 '05 #16
* verec:

template <typename T> struct my_ptr {

void * rawValue ;

typedef void_star_caster<is_polymorphic<T>::value, T> caster ;
my_ptr(T *t) : rawValue(caster::cast(t)) {

}

} ;


How are you intending to use the void* so that a T* wouldn't do?

Are you aware that the only thing you can portably do with that void* is
to cast it back to T*?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 23 '05 #17
On 2005-06-18 19:59:57 +0100, al***@start.no (Alf P. Steinbach) said:
verec:

template <typename T> struct my_ptr {
void * rawValue ;
typedef void_star_caster<is_polymorphic<T>::value, T> caster ;
my_ptr(T *t) : rawValue(caster::cast(t)) {
}
} ;


How are you intending to use the void* so that a T* wouldn't do?

Are you aware that the only thing you can portably do with that void* is
to cast it back to T*?


Yes ...

This self-contained snippet was just to illustrate the issue/solution
tied to converting from any T* to void * ... Now as to why I need
such a beast ...

The short story: I'm implementing a ref-counted GC where the rereferences
are stored outside each object, into a global std::map<void *, int>.
The conversion from T* to void * is used as a key in the map to locate
the reference count. Also, at recycle time, the code will cycle through
each void *, convert it back to the T* it originated from, so as to track
and kill cycles.

This design strongly influenced by Greg Colvin's cyclic_ptr:

http://www.mail-archive.com/bo***@li.../msg07689.html
http://aspn.activestate.com/ASPN/Mai.../boost/1138099
http://www.boost.org/libs/smart_ptr/smarttests.htm
http://www.uiwithstyle.org/code/contribs/cyclic_ptr.hpp
http://www.uiwithstyle.org/code/contribs/cyclic_ptr.hpp

--
JFB

Jul 23 '05 #18
[]
On my list, there is still boost::file & folder and other related
IO stuff that will, no doubt, get down the drain for some related
reasons: I don't hold my breath, though I try to keep an opne mind :)

Sorry for the rant.
--
JFB
Well, I - in part - share your opinion of boost. But I think it's
best to see boost as what it is - a set of libraries from different
authors. I think there are some great libs like regex,
string_algorithm, bind, function, static_assert, pool, random,
crc, tuple, utility, smart_ptr and maybe others - but of course only
if it's the thing one is looking for.
I use shared_ptr quite a bit - but I don't mainly use it as a GC,
I just use it for what it is: shared ownership without cycle detection.

One other thing:
- this guess turns out wrong more often than not (please,
find a decent explanation & use case for the ``addRef''
parameter of intrusive_ptr, that will not break your
assumption of symmetric behavior of ctors & dtors)


You will need "intrusive_ptr( p, false )" when you do COM. If it
was not there, you would end up calling "Release" by hand after
constructing the intrusive_ptr, and why would one want to do that?
And there might be other libs that return refs to objects that
have already been "addref"ed - when you start working with those
you'll be happy that "intrusive_ptr" has that "addref" parameter :-)
Jul 23 '05 #19
On 2005-06-18 13:18:28 +0100, Swampmonster
<sw**********@der-ball-ist-rund.net> said:
Theoretically the size of T1 could be the same as the size of T,
even if T is not a polymorphic type. If T is empty the compiler
might still allocate 1 or 4 or 8 (or whatever) bytes for it, and then
reuse that storage for the vtable pointer.
I see the problem, now. And an "empty" T might not be so rare,
after all. Any stateless T chock full of non virtual members
will do. A simple fix might be to add a dummy long double or
some such to force alignement to the next boundary and go with
two derived classes T1 and T2, both of which including that
padding, but only the latter including a virtual destructor,
and then comparing sizeof(T1) and sizeof(T2).
And the size of T1 could be bigger then the size of T, even it T
is a polymorphic type, since the compiler might want to give T1 a
different address then the copy of T that T1 contains. The compiler
isn't required to do so, but I don't think that there is any rule
that forbids it either.


I don't understand the issue. The only vague hint that I can see
is if T is some MI composite, in which case an intermediate and
empty T0 deriving from T, and from which both T1 and T2 would
derive, could solve the issue?

--
JFB
Jul 23 '05 #20
And the size of T1 could be bigger then the size of T, even it T
is a polymorphic type, since the compiler might want to give T1 a
different address then the copy of T that T1 contains. The compiler
isn't required to do so, but I don't think that there is any rule
that forbids it either.

I don't understand the issue. The only vague hint that I can see
is if T is some MI composite, in which case an intermediate and
empty T0 deriving from T, and from which both T1 and T2 would
derive, could solve the issue?

--
JFB


Hm. Ok, I think I messed up my neural pathways there. Normally the
compiler should append T1's data to T. But isn't it possible that
the compiler is so very much stupid, that it wants to give T1 some
additional data? Just because he feels like doing so?
Anyway, the T1 and T2 (without T0) method should be fine, and it's
the very thing boost does. Only they append 256 bytes IIRC.
Jul 23 '05 #21

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

1 post views Thread by Kai-Uwe Bux | last post: by
7 posts views Thread by Mr. Ed | last post: by
2 posts views Thread by sarathy | last post: by
11 posts views Thread by toton | last post: by
23 posts views Thread by Ben Voigt | last post: by
16 posts views Thread by chameleon | last post: by
5 posts views Thread by Zeppe | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.