static_cast<Type1*>(Type2*) not working, but (Type1*) Type2* works

P

Preben

Hi

I'm having a strange problem here.

I can't get this working:
const rw::kinematics::Frame* f = static_cast<const
rw::kinematics::Frame*>(joints);


However this works with no problems:
const rw::kinematics::Frame* f = (rw::kinematics::Frame*) joints;


where "joints" are defined as
const std::vector<rw::models::Joint*>& joints = _serialDevice->getJoints();


In some cases the static_cast works, but in other cases it doesn't work.
I think it's a bug in the compiler, but you can maybe convince me
otherwise. In the class CRTPlanner I'm using the template class
ManipulatorKinematics in the initializer list. This seems to trigger the
problem. There doesn't seem to be problems in other situations. The
output of the compiler is given below:

--------------
/usr/bin/c++ -DRW_ENABLE_ASSERT -g3 -pipe -g
-I/home/preben/working/RobWork-0.5/RobWork/build/../ext
-I/home/preben/working/RobWork-0.5/RobWork/build/../src
-I/usr/include/boost-1_41
-I/home/preben/working/RobWork-0.5/RobWork/ext/yaobi
-I/home/preben/working/RobWork-0.5/RobWork/ext/PQP
-I/home/preben/working/CRTPlanner/src -save-temps -o
CMakeFiles/planning.dir/CRTPlanner.cpp.o -c
/home/preben/working/CRTPlanner/CRTPlanner.cpp
c++: warning: -pipe ignored because -save-temps specified
In file included from /home/preben/working/CRTPlanner/CRTPlanner.hpp:15,
from /home/preben/working/CRTPlanner/CRTPlanner.cpp:11:
/home/preben/working/CRTPlanner/ManipulatorKinematics.hpp: In member
function 'const std::vector<rw::math::Transform3D<Q>,
std::allocator<rw::math::Transform3D<Q> > >
rw::dynamics::ManipulatorKinematics<T>::worldTlink(const
rw::kinematics::State&) const [with T = double]':
/home/preben/working/CRTPlanner/ManipulatorKinematics.hpp:209:
instantiated from 'void
rw::dynamics::ManipulatorKinematics<T>::initialize() [with T = double]'
/home/preben/working/CRTPlanner/ManipulatorKinematics.hpp:38:
instantiated from
'rw::dynamics::ManipulatorKinematics<T>::ManipulatorKinematics(rw::models::SerialDevicePtr,
const rw::kinematics::State&) [with T = double]'
/home/preben/working/CRTPlanner/CRTPlanner.cpp:32: instantiated from here
/home/preben/working/CRTPlanner/ManipulatorKinematics.hpp:99: error:
invalid static_cast from type 'rw::models::Joint* const' to type 'const
rw::kinematics::Frame*'
/home/preben/working/CRTPlanner/MechComputer.hpp: At global scope:
/home/preben/working/CRTPlanner/MechComputer.hpp:76: warning: inline
function 'boost::numeric::ublas::vector<double,
boost::numeric::ublas::unbounded_array said:
> rw::dynamics::MechComputer::calcAcceleration(const
boost::numeric::ublas::vector<double,
boost::numeric::ublas::unbounded_array said:
>&, const boost::numeric::ublas::vector<double,
boost::numeric::ublas::unbounded_array said:
>&) const' used but never defined
--------------


I've used the -save-temps flag on the working copy (c-like casts) and
the non-working copy (c++ cast).

They can be found here:
http://jobc.dk/cpp/CRTPlanner.ii.working
http://jobc.dk/cpp/CRTPlanner.ii.notworking
http://jobc.dk/cpp/CRTPlanner.s.working
http://jobc.dk/cpp/CRTPlanner.s.notworking


The gcc-version is:
---------
preben@ul30vt ~/working/CRTPlanner/build/Debug $ g++ -v
Using built-in specs.
Target: x86_64-pc-linux-gnu
Configured with:
/var/tmp/portage/sys-devel/gcc-4.4.3-r2/work/gcc-4.4.3/configure
--prefix=/usr --bindir=/usr/x86_64-pc-linux-gnu/gcc-bin/4.4.3
--includedir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include
--datadir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.4.3
--mandir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.4.3/man
--infodir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.4.3/info
--with-gxx-include-dir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4
--host=x86_64-pc-linux-gnu --build=x86_64-pc-linux-gnu --disable-altivec
--disable-fixed-point --without-ppl --without-cloog --enable-nls
--without-included-gettext --with-system-zlib --disable-checking
--disable-werror --enable-secureplt --enable-multilib
--enable-libmudflap --disable-libssp --enable-libgomp
--with-python-dir=/share/gcc-data/x86_64-pc-linux-gnu/4.4.3/python
--enable-java-awt=gtk --enable-languages=c,c++,java,fortran
--enable-shared --enable-threads=posix --enable-__cxa_atexit
--enable-clocale=gnu --with-bugurl=http://bugs.gentoo.org/
--with-pkgversion='Gentoo 4.4.3-r2 p1.2'
Thread model: posix
gcc version 4.4.3 (Gentoo 4.4.3-r2 p1.2)
 
F

Francesco S. Carta

Hi

I'm having a strange problem here.

I can't get this working:
const rw::kinematics::Frame* f = static_cast<const
rw::kinematics::Frame*>(joints);


However this works with no problems:
const rw::kinematics::Frame* f = (rw::kinematics::Frame*) joints;


where "joints" are defined as
const std::vector<rw::models::Joint*>& joints = _serialDevice->getJoints();


In some cases the static_cast works, but in other cases it doesn't work.


static_cast is only one of the explicit casts that replace C-style
casts. Find information about dynamic_cast and reinterpret_cast also.

The exact explicit cast to use depends on the relation between
"rw::models::Joint" and "rw::kinematics::Frame".


The error above seems to tell that something is going wrong regardless
of the cast you use, though...
 
F

Francesco S. Carta

Hi

I'm having a strange problem here.

I can't get this working:
const rw::kinematics::Frame* f = static_cast<const
rw::kinematics::Frame*>(joints);


However this works with no problems:
const rw::kinematics::Frame* f = (rw::kinematics::Frame*) joints;


where "joints" are defined as
const std::vector<rw::models::Joint*>& joints =
_serialDevice->getJoints();


In some cases the static_cast works, but in other cases it doesn't work.


static_cast is only one of the explicit casts that replace C-style
casts. Find information about dynamic_cast and reinterpret_cast also.

The exact explicit cast to use depends on the relation between
"rw::models::Joint" and "rw::kinematics::Frame".


The error above seems to tell that something is going wrong regardless
of the cast you use, though...


Maybe the most important error is this one, which slipped past after my
sight in the first read:

invalid static_cast from type 'rw::models::Joint* const' to type 'const
rw::kinematics::Frame*'

As it appears, you're trying to convert a "const pointer to non-const
object" into a "non-const pointer to const object", where apart of the
types involved which might be not compatible for a static_cast, you're
trying to cast a const away and static_cast is not able to do such things.

This comes straight from the fact that "joints" is a const vector
containing "rw::models::Joint*", which means its elements will be of
type "rw::models::Joint * const", as the error reports.

You can avoid casting the const away by adding it in the final pointer
and in the cast as well:

const rw::kinematics::Frame * const f = static_cast<const
rw::kinematics::Frame * const>(joints);

Hope that helps.
 
J

Juha Nieminen

Preben said:
However this works with no problems:
const rw::kinematics::Frame* f = (rw::kinematics::Frame*) joints;


Of course it works. A C-style cast will cast from anything to anything.

A static_cast, however, is designed to reject casting between
incompatible types (or from const to non-const).
 
J

James Kanze

on 10/08/2010 said:
I can't get this working:
const rw::kinematics::Frame* f = static_cast<const
rw::kinematics::Frame*>(joints);
However this works with no problems:
const rw::kinematics::Frame* f = (rw::kinematics::Frame*) joints;


The fact that the first doesn't compile probably means that the
second will actually lead to subtle bugs in practice.

Generally speaking, it will work when the resulting code will
also work. (There are a few exceptions: there are no guarantees
when using static_cast on a void*, for example.) When
static_cast fails to compile, a C style cast will usually lead
to subtle (or not so subtle) undefined behavior, with unexpected
runtime errors.

Given the error, it seems likely that they're not related.

Possibly a missing include. Or simply something that has been
explicitly instantiated elsewhere.
Maybe the most important error is this one, which slipped past
after my sight in the first read:
invalid static_cast from type 'rw::models::Joint* const' to type 'const
rw::kinematics::Frame*'

This is the real problem.
As it appears, you're trying to convert a "const pointer to
non-const object" into a "non-const pointer to const object",

Forget the top level const here. They're completely irrelevant.
It's a little confusing because the compiler throws the
irrelevant top level const into the error message, and puts the
other const's in an unexpected place, but he's going (or trying
to go) from a rw::models::Joint* to a rw::kinematics::Frame
const*. Which is no problem as far as the const is concerned.
where apart of the types involved which might be not
compatible for a static_cast, you're trying to cast a const
away and static_cast is not able to do such things.

He's not trying to cast a const away; he's adding one.
This comes straight from the fact that "joints" is a const
vector containing "rw::models::Joint*", which means its
elements will be of type "rw::models::Joint * const", as the
error reports.

So. That's a top level const, which is ignored in most rvalue
contexts, including as the source in a type conversion. What he
has is an lvalue of type rw::models::Joint* const, but the first
thing that happens is an lvalue to rvalue conversion, which on
a pointer effectively removes the const.
You can avoid casting the const away by adding it in the final
pointer and in the cast as well:
const rw::kinematics::Frame * const f = static_cast<const
rw::kinematics::Frame * const>(joints);


That shouldn't change anything. You're initializing f with an
rvalue, the result of a conversion. In other words, indirectly
with a copy, which means that the const'ness of the initial
expression is irrelevant.
 
F

Francesco S. Carta

on 10/08/2010 said:
I can't get this working:
const rw::kinematics::Frame* f = static_cast<const
rw::kinematics::Frame*>(joints);
However this works with no problems:
const rw::kinematics::Frame* f = (rw::kinematics::Frame*) joints;


The fact that the first doesn't compile probably means that the
second will actually lead to subtle bugs in practice.

Generally speaking, it will work when the resulting code will
also work. (There are a few exceptions: there are no guarantees
when using static_cast on a void*, for example.) When
static_cast fails to compile, a C style cast will usually lead
to subtle (or not so subtle) undefined behavior, with unexpected
runtime errors.

Given the error, it seems likely that they're not related.

Possibly a missing include. Or simply something that has been
explicitly instantiated elsewhere.
Maybe the most important error is this one, which slipped past
after my sight in the first read:
invalid static_cast from type 'rw::models::Joint* const' to type 'const
rw::kinematics::Frame*'

This is the real problem.
As it appears, you're trying to convert a "const pointer to
non-const object" into a "non-const pointer to const object",

Forget the top level const here. They're completely irrelevant.
It's a little confusing because the compiler throws the
irrelevant top level const into the error message, and puts the
other const's in an unexpected place, but he's going (or trying
to go) from a rw::models::Joint* to a rw::kinematics::Frame
const*. Which is no problem as far as the const is concerned.
where apart of the types involved which might be not
compatible for a static_cast, you're trying to cast a const
away and static_cast is not able to do such things.

He's not trying to cast a const away; he's adding one.
This comes straight from the fact that "joints" is a const
vector containing "rw::models::Joint*", which means its
elements will be of type "rw::models::Joint * const", as the
error reports.

So. That's a top level const, which is ignored in most rvalue
contexts, including as the source in a type conversion. What he
has is an lvalue of type rw::models::Joint* const, but the first
thing that happens is an lvalue to rvalue conversion, which on
a pointer effectively removes the const.
You can avoid casting the const away by adding it in the final
pointer and in the cast as well:
const rw::kinematics::Frame * const f = static_cast<const
rw::kinematics::Frame * const>(joints);


That shouldn't change anything. You're initializing f with an
rvalue, the result of a conversion. In other words, indirectly
with a copy, which means that the const'ness of the initial
expression is irrelevant.


Uhm, that whole thing about the top level const wasn't absolutely among
my ideas. I've read your post a couple of times but didn't really
realize what you where speaking about till I've fiddled with some code
and noticed where the const keyword made a difference or not.

Thank you for your explanation, I think you've just helped me filling a
basic gap in my knowledge about C++. Now that I noticed it I see that it
was quite a straightforward concept... how embarrassing.

There are two things that still aren't clear to me:

- that error message that you have been able to interpret regardless of
being somewhat "messed up"... is that a correct error message? it would
strike me quite bad if I'd have to start suspecting my compiler's error
reports of playing tricks on me.

- if the things the OP is doing are:
-- adding a const (which should cause no problems)
-- implicitly removing the top level one (harmless and expected because
of the lvalue to rvalue, as I seem to have understood)
then the only problem should be the Joints* to Frame* conversion.

If that's the true problem, then such a static_cast could never have
worked correctly with such a conversion.
 
P

Preben

Uhm, that whole thing about the top level const wasn't absolutely among
my ideas. I've read your post a couple of times but didn't really
realize what you where speaking about till I've fiddled with some code
and noticed where the const keyword made a difference or not.

Thank you for your explanation, I think you've just helped me filling a
basic gap in my knowledge about C++. Now that I noticed it I see that it
was quite a straightforward concept... how embarrassing.


Okay... i might have an even higher gab in my knowledge about C++...

So you say, that
const Frame* const fc

means that the pointer is const and the object is also const?

but that
const Frame* fnc

only has a const pointer. Am I understanding you correctly?
So just to be clear, this is possible

fnc->changeSomething()

but not this

fnc->changeSomething()


where changeSomething is a non-const member function?


First of all, with the static_cast I tried several things like:

const Frame* fc = static_cast<const Frame*>
const Frame* fc = static_cast<Frame* const>
const Frame* fc = static_cast<Frame*>

and other combinations as well. Can you tell me, what I need to change?


I would normally say, that I'm pretty sure static_cast is the correct
one to use, even though I have "never ever" used the reinterpret_cast.


There are two things that still aren't clear to me:

- that error message that you have been able to interpret regardless of
being somewhat "messed up"... is that a correct error message? it would
strike me quite bad if I'd have to start suspecting my compiler's error
reports of playing tricks on me.

- if the things the OP is doing are:
-- adding a const (which should cause no problems)
-- implicitly removing the top level one (harmless and expected because
of the lvalue to rvalue, as I seem to have understood)
then the only problem should be the Joints* to Frame* conversion.

If that's the true problem, then such a static_cast could never have
worked correctly with such a conversion.

What do you mean?
 
F

Francesco S. Carta

Okay... i might have an even higher gab in my knowledge about C++...

So you say, that
const Frame* const fc

means that the pointer is const and the object is also const?

Yes, the above can be written also as:

Frame const * const fc;

Reading it right to left, we say that "fc is a const pointer to const
Frame".
but that
const Frame* fnc

only has a const pointer. Am I understanding you correctly?

No, that line means that "fnc is a pointer to const Frame".
So just to be clear, this is possible

fnc->changeSomething()

but not this

fnc->changeSomething()


where changeSomething is a non-const member function?

The two lines that you're opposing with "but not this" are identical, so
I can't answer your question directly.

The thing I can say is that the compiler will forbid you calling a
non-const member function on a const object, so, standing your declaration:

const Frame* fnc;

the call to:

fnc->changeSomething();

will be a compile time error if changeSomething() is not declared const.
First of all, with the static_cast I tried several things like:

const Frame* fc = static_cast<const Frame*>

the part in the the static_cast round parentheses is very important to
understand what the static_cast is used for. The above would be fine
because the assignment puts a "const Frame*" into a "const Frame*" -
assuming that the static_cast argument is correctly convertible to
"const Frame*".
const Frame* fc = static_cast<Frame* const>
const Frame* fc = static_cast<Frame*>

Both of the above are fine (standing the caveats of my previous
paragraph) because during an assignment like that the const referred to
Frame can be added, and the const referred to the pointer (the one after
the *) can be dropped (that's the top level const that James was
speaking about, the part that I just come to realize and set to mind).
and other combinations as well. Can you tell me, what I need to change?


I would normally say, that I'm pretty sure static_cast is the correct
one to use, even though I have "never ever" used the reinterpret_cast.

It all depends on how the various parts are declared and eventually
dependent one on the other (I'm referring to the relation between Joint
and Frame in particular).
What do you mean?

I mean that if they represent incompatible types then the static_cast
isn't expected to work. But all of these are just speculations, I'd need
to see what exactly all these parts are defined in order to understand
what should work and what shouldn't.

Could you reproduce a self contained example where you define all the
parts and illustrate what exactly you're trying to achieve?

Chances are that you might even get rid of all the casts at once by
slightly changing the design - just another speculation, of course.
 
J

James Kanze

James Kanze <[email protected]>, on 10/08/2010 11:25:39, wrote:

[...]
- that error message that you have been able to interpret regardless of
being somewhat "messed up"... is that a correct error message?

It's correct as far as it goes, but it could be more helpful.
The code was converting the return value of
std::vector<>::eek:perator[] const, or something like that, which
was a reference (an lvalue) to const. The type of the lvalue
was "Something* const&", and so the error message spoke of
conversion from a "Something* const". However, since the
conversion was not to an lvalue, the first thing that happens is
an lvalue to rvalue conversion, and the const disappears (for
pointer types---for class types, it is still there, and *may* be
relevant later). I suspect that it was the const in this
"Something* const" which was confusing you, and with a target
like "SomethingElse const*", it certainly looks like a const is
being removed by the cast. The message would be improved if the
compiler noticed that the conversion did lead to lvalue to
rvalue, that because of this the final const was irrelevant, and
dropped it in its message.
it would strike me quite bad if I'd have to start suspecting
my compiler's error reports of playing tricks on me.

G++'s messages aren't always perfect, or as clear as they could
be, and in the case of templates, they'll drown you in
verbosity. But the necessary information is always present
(often with a lot more), and I've not known them to be actually
wrong. VC++, on the other hand, has given me some really wrong
messages (on illegal code, but I had to go to g++ to figure out
what I was doing wrong).
- if the things the OP is doing are:
-- adding a const (which should cause no problems)
-- implicitly removing the top level one (harmless and expected because
of the lvalue to rvalue, as I seem to have understood)
then the only problem should be the Joints* to Frame* conversion.
Exactly.

If that's the true problem, then such a static_cast could never have
worked correctly with such a conversion.

Nope.
 
F

Francesco S. Carta

[...]
- that error message that you have been able to interpret regardless of
being somewhat "messed up"... is that a correct error message?

It's correct as far as it goes, but it could be more helpful.
The code was converting the return value of
std::vector<>::eek:perator[] const, or something like that, which
was a reference (an lvalue) to const. The type of the lvalue
was "Something* const&", and so the error message spoke of
conversion from a "Something* const". However, since the
conversion was not to an lvalue, the first thing that happens is
an lvalue to rvalue conversion, and the const disappears (for
pointer types---for class types, it is still there, and *may* be
relevant later). I suspect that it was the const in this
"Something* const" which was confusing you, and with a target
like "SomethingElse const*", it certainly looks like a const is
being removed by the cast. The message would be improved if the
compiler noticed that the conversion did lead to lvalue to
rvalue, that because of this the final const was irrelevant, and
dropped it in its message.
it would strike me quite bad if I'd have to start suspecting
my compiler's error reports of playing tricks on me.

G++'s messages aren't always perfect, or as clear as they could
be, and in the case of templates, they'll drown you in
verbosity. But the necessary information is always present
(often with a lot more), and I've not known them to be actually
wrong. VC++, on the other hand, has given me some really wrong
messages (on illegal code, but I had to go to g++ to figure out
what I was doing wrong).
- if the things the OP is doing are:
-- adding a const (which should cause no problems)
-- implicitly removing the top level one (harmless and expected because
of the lvalue to rvalue, as I seem to have understood)
then the only problem should be the Joints* to Frame* conversion.
Exactly.

If that's the true problem, then such a static_cast could never have
worked correctly with such a conversion.

Nope.

I'll take that as a "Yes, it could never have worked in such context".

Thank you for the explanations James.
 
F

Francesco S. Carta

It's correct as far as it goes, but it could be more helpful.
The code was converting the return value of
std::vector<>::eek:perator[] const, or something like that, which
was a reference (an lvalue) to const. The type of the lvalue
was "Something* const&", and so the error message spoke of
conversion from a "Something* const". However, since the
conversion was not to an lvalue, the first thing that happens is
an lvalue to rvalue conversion, and the const disappears (for
pointer types---for class types, it is still there, and*may* be
relevant later). I suspect that it was the const in this
"Something* const" which was confusing you

Missed to reply to this point: that was the exact point that confused
me. Now that I "see" (;-) I realize how silly that top level const
elision was to catch, and now I also see the difference between using
whatever_cast alone or putting it on the right side of the assignment,
as for what regards the fact that the top level const could be
meaningful or not.
 
J

Joshua Maurice

Preben said:
However this works with no problems:
const rw::kinematics::Frame* f = (rw::kinematics::Frame*) joints;


  Of course it works. A C-style cast will cast from anything to anything.

  A static_cast, however, is designed to reject casting between
incompatible types (or from const to non-const).


Well, no quite. IIRC, a C-style cast still won't do some kinds of
casts, such as:

1- a pointer type to an integer type which isn't big enough to hold
the pointer value.
void* p;
short s = (short)p;
http://www.comeaucomputing.com/tryitout/
"ComeauTest.c", line 2: error: invalid type conversion
short s = (short)p;

2- a class type to a pointer. Ex:
struct A {};
A a;
void* p = (void*)a;
http://www.comeaucomputing.com/tryitout/
"ComeauTest.c", line 3: error: no suitable conversion function from
"A" to "void *"
exists
void* p = (void*)a;

3- a derived class to ambiguous base class. Ex:
struct A {};
struct B : A {};
struct C : A {};
struct D : B, C {};
D* d = new D;
A* a = (A*)d;
http://www.comeaucomputing.com/tryitout/
"ComeauTest.c", line 6: error: base class "A" is ambiguous
A* a = (A*)d;
This one is my favorite example because a reinterpret_cast will "do
it". The answer is as long as the types are related by inheritance
(and the types's definitions are in scope), then the C-style cast is
equivalent to a static_cast, which can and will fail if it's a cast to
an ambiguous base class. The reinterpret_cast happily just copies the
bit pattern (for the common implementation).

And I'm sure there are other examples where a C-style cast will not
compile.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top