which of these 3 casts would you prefer?

A

A

int Callback(void *pThis)
{
TMyClass *pMC = (TMyClass*)pThis; // C style

TMyClass *pMC = static_cast<TMyClass*>(pThis); // C++ style but the
// same as above

TMyClass *pMC = reinterpret_cast<TMyClass*>(pThis); // Doesn't check if
// input type was
// pointer, possibly
// dangerous
}

these 3 do essentially the same. is there any preferred one for void* cast
like this and why? any difference in speed?
 
A

Andrey Tarasevich

int Callback(void *pThis)
{
TMyClass *pMC = (TMyClass*)pThis; // C style

TMyClass *pMC = static_cast<TMyClass*>(pThis); // C++ style but the
// same as above

TMyClass *pMC = reinterpret_cast<TMyClass*>(pThis); // Doesn't check if
// input type was
// pointer, possibly
// dangerous
}

these 3 do essentially the same. is there any preferred one for void* cast
like this and why? any difference in speed?

Casts from `void *` are handled in C++ by `static_cast`. So, that would
be the one to use in this case. There's no reason to involve a less
restrictive cast (`reinterpret_cast`) when a more restrictive does the
job. That's basically the rule I'd follow: in any situation prefer to
use the most restrictive cast that does the job.
 
A

Alf P. Steinbach

int Callback(void *pThis)
{
TMyClass *pMC = (TMyClass*)pThis; // C style

TMyClass *pMC = static_cast<TMyClass*>(pThis); // C++ style but the
// same as above

TMyClass *pMC = reinterpret_cast<TMyClass*>(pThis); // Doesn't check if
// input type was
// pointer, possibly
// dangerous
}

these 3 do essentially the same. is there any preferred one for void* cast
like this and why? any difference in speed?

Aim for clarity.

Cheers & hth.,
 
D

Duga Chernobyl

Dnia 25-08-2011 o 23:58:56 A said:
int Callback(void *pThis)
{
TMyClass *pMC = (TMyClass*)pThis; // C style

TMyClass *pMC = static_cast<TMyClass*>(pThis); // C++ style but the
// same as above

TMyClass *pMC = reinterpret_cast<TMyClass*>(pThis); // Doesn't check if
// input type was
// pointer, possibly
// dangerous
}

these 3 do essentially the same. is there any preferred one for void*
cast
like this and why? any difference in speed?

IMHO there is no place for preferences.

The first is used in c and should be used only in C.
The second is from C++ and should be used mostly (compiler check if
conversion is usable)
The third is a equivalent of the c solution. Compiler checks nothing.

In c++ should be used only c++ conversion, because mostly compiler (not in
reinterpret conv.) does some checks of common sense of this conversion.
And c++ conversion are long and easy to find. The creator of c++ some gave
arguments: the place of most errors is conversion ;-)

dynamic_cast is used for child class pointers conversion.
 
A

A

I do not understand though reluctance to use C style casts.

They can cast to reinterpret_cast but they also try out the weaker cast
before. It is just automatic way to do what you would do manually - first
try const_cast, then static_cast and finally reinterpret_cast... yes, it can
use reinterpret_cast but you would have to use it anyway in a particular
case where you really need it.

So what's wrong with little automation, providing that you know what you are
doing with casts?
 
D

Duga Chernobyl

Dnia 26-08-2011 o 14:40:27 A said:
I do not understand though reluctance to use C style casts.

They can cast to reinterpret_cast but they also try out the weaker cast
before. It is just automatic way to do what you would do manually - first
try const_cast, then static_cast and finally reinterpret_cast... yes, it
can
use reinterpret_cast but you would have to use it anyway in a particular
case where you really need it.

So what's wrong with little automation, providing that you know what you
are
doing with casts?

Sometimes you are tired and you don't want to check, if something is good
or bad. For me compiler is the best friend (in programming ;-)) and always
set this settings:

Try search all convertions with c++ style and all with c style. What, do
you think, is easier to find all of them?

-Wall -Wextra -pedantic -g -pg

It is not little automation, but great help ;-)

if you make a mistake f.e

if (a = 1) gcc will you ask, if really means it. If you are programmer,
the common sense makes you to be open for all suggestions of compiler.
Many people thinks, that computer is evil, but computer makes exactly what
we said (but not very often what we want ;-))
 
E

Edek

I do not understand though reluctance to use C style casts.

They can cast to reinterpret_cast but they also try out the weaker cast
before. It is just automatic way to do what you would do manually - first
try const_cast, then static_cast and finally reinterpret_cast... yes, it can
use reinterpret_cast but you would have to use it anyway in a particular
case where you really need it.

So what's wrong with little automation, providing that you know what you are
doing with casts?

C++ has many features you do not have to use. If you do not like
this part of type enforcement, then don't, just mind others working
on the same code.

I would prefer static_cast, because
- it is grepable (C-style cast is not so grepable)
- it is more strict than reinterpret_cast and C-style.
If I (or someone else) changes the type of pThis,
it might rightly generate an error

In both cases, it might save a lot of time otherwise spent
on debugging.

Edek
 
J

Jorgen Grahn

Sometimes you are tired and you don't want to check, if something is good
or bad. For me compiler is the best friend (in programming ;-))

Yes, in C++ programming anyway.
and always
set this settings:

Try search all convertions with c++ style and all with c style. What, do
you think, is easier to find all of them?

-Wall -Wextra -pedantic -g -pg

I'm surprised you're listing g++ warning options, but omit the topical
one: "-Wold-style-cast". I use it for all my code.

/Jorgen
 
A

Alf P. Steinbach

I'm surprised you're listing g++ warning options, but omit the topical
one: "-Wold-style-cast". I use it for all my code.

Thanks for that item.

Cheers,

- Alf
 
D

Duga Chernobyl

Yes, in C++ programming anyway.


I'm surprised you're listing g++ warning options, but omit the topical
one: "-Wold-style-cast". I use it for all my code.

/Jorgen

Thanks ;-)
 
B

BGB

I do not understand though reluctance to use C style casts.

They can cast to reinterpret_cast but they also try out the weaker cast
before. It is just automatic way to do what you would do manually - first
try const_cast, then static_cast and finally reinterpret_cast... yes, it can
use reinterpret_cast but you would have to use it anyway in a particular
case where you really need it.

So what's wrong with little automation, providing that you know what you are
doing with casts?

unlike the others here, I don't take much issue with C style casts.


then again, I am also primarily a C programmer (for various reasons),
and most of the C++ code I have written has been in a C-like style
(usually either because for some reason I needed C++, such as
interfacing with a C++ only library, or because there is C++/CLI but not
a C/CLI, ...).


one can also argue that function overloading and operator overloading
are nifty, but rarely have I found this to be a big issue (one can
typically sort-of make due with using short macro names to redirect to
longer operator function names or similar, and with the convention of
using short suffixes to function names to indicate the argument types or
similar...).


it is not that I don't know about OOP or whatever, just that:
what I usually want done can be done "well enough" in C explicitly using
structs and vtables (so C can still do OOP, sort of);
in other cases, I am using a language like C# or Java, or my own
scripting language (vaguely ActionScript-like);
it often just so happens to be a bit easier to do multi-direction
cross-language interfacing in C than in C++.


not that my case likely represents a normal development process though,
and if one is doing pure C++ (or pure C++ for application code with C
mostly in back-end libraries), then the comments made by others here
make more sense.

also, FWIW, my own scripting language doesn't have C-like casts (mostly
for syntactic reasons though, namely syntactic ambiguity), but instead
splits them into multiple casts indicated by a keyword ("x as type", "x
as! type", ...).


or such...
 
J

Juha Nieminen

BGB said:
one can also argue that function overloading and operator overloading
are nifty, but rarely have I found this to be a big issue (one can
typically sort-of make due with using short macro names to redirect to
longer operator function names or similar, and with the convention of
using short suffixes to function names to indicate the argument types or
similar...).

The reason why function and operator overloading is important is not
because it's "handy" or "convenient" or anything like that. It's because
it's *necessary* for generic programming. It's what allows you to do
things like this:

template<typename T>
T foo(T value)
{
std::cout << "The input value was " << value << std::endl;
return std::cos(value);
}

The above code would be impossible without function and operator
overloading. For example in C you have cos(), cosf() and cosl(), and
you just can't write a macro that would call the proper function based
on the type of the parameter. In C++ you have just different versions
of std::cos(), and the proper version will be automatically called
depending on the type of parameter.

(You could write *three* macros with different names, but now you have
not only triplicated your code, you have made it impossible to call
the macro from other macros without triplicating those too, so the
triplication becomes contagious. Also, it doesn't allow the user to use
a custom user-defined type.)

Likewise it's what allows the std::cout above to work properly.

This is something the most C programmers don't understand. They are
not to blame, though, because many C++ programmers don't know this either
(or have never thought about it).
it is not that I don't know about OOP or whatever, just that:
what I usually want done can be done "well enough" in C explicitly using
structs and vtables (so C can still do OOP, sort of);

The major problem in C is not the lack of dynamic binding. It's the
lack of RAII and templates. (And, to some extent, exceptions.)
 
N

Noah Roberts

I do not understand though reluctance to use C style casts.

They can cast to reinterpret_cast but they also try out the weaker cast
before. It is just automatic way to do what you would do manually - first
try const_cast, then static_cast and finally reinterpret_cast... yes, it can
use reinterpret_cast but you would have to use it anyway in a particular
case where you really need it.

So what's wrong with little automation, providing that you know what you are
doing with casts?

Perhaps a story would help you understand.

A while back I worked under a guy who used C++ but basically feared
everything about it. Boy did he love inheritance though. He used
inheritance as his only method of code reuse. He created these
gigantic higherarchies that spanned multiple concepts attempting to
tie them together. Trying to add a new object to this system was a
nightmare because a lot of the time he put in "assert(false); // this
function shouldn't be called" as the base object definition instead of
using pure virtuals. He did this because his higherarchy was a total
pain in the ass and required you to implement more functions than you
needed. This of course left one not knowing what functions they
needed to override until running the program and getting an assert.

Eventually this guy quit and moved on to other things and places.
Hopefully he learned something about code design but he was pretty
stubborn. Once he did, I set about trying to fix the project and put
it into a state that allowed for more code reuse and in which it was
easier to add new behavior.

Did I mention this guy hated everything C++? This included new-style
casts. He used C casts everywhere. He even used them where they were
not necessary. For example:

class Object
{
public:
Object* readObjectA(char* c) { return (Object*)ObjectA(c); }
... lots more of these ...
};

class ObjectA : public Object { ... };

Now, I did not realize this was going on at the time. I split all the
various responsibilities that Object had in its interface into
multiple base objects and used multiple inheritance to apply those
interfaces only where they were being used. Sent the code off to
testing and it starts acting really stupid. What happened?

Well, the original developer had seen something in the compiler output
like, "cannot convert ObjectA* to Object*, use a reinterpret or c-
style cast," and so he did that instead of realizing he needed to put
these functions in a place that could see the full definition of
ObjectA so that it COULD do that conversion the way it's supposed to.
Believing only in all things C this kind of thing comes completely
natural to him. Of course, a reinterpret cast to one base among many
is a very bad thing to do.

This problem was trivial to fix but later I ran into further issues.
Because there were C style casts all through the code, we had
opportunity for more issues to come up. This used the Win32 API and
had a lot of callback functions. These functions would take a type
number ID (guy hated RTTI and dynamic casting) and then cast to the
appropriate type, having already casted the long or void* to the base
type. Often this code would assume it's only going to get a certain
subset of objects passed to it because they're the only ones that have
some somewhat related functionality. If the type wasn't of one it
would assume the other. It would use c-style casts to do so. So the
code would promptly cast to the wrong derived class object and start
calling functions on it. This issue did not turn up until I started
messing with things; it was of course illiciting undefined behavior
all over the place but nobody was the wiser about it because it just
worked, and had worked for 15 years.

Because this guy used c-style casts instead of the appropriate new-
style (or even the inappropriate one), it was quite impractical to
hunt down casts like these and fix them. They had to turn up as
errors, which occasionally they did when we started adding new objects
to the tree that performed roles that were previously assumed to only
be performed by a group of classes. We knew that we needed to start
doing dynamic casts for these and shooting out error or exceptions
(which the guy also hated of course) instead of allowing undefined
behavior, but we were stuck because there is no such thing as a
regular expression that will match c-style casts.

This product tree had to be scrapped and redone. We were stuck with
old interfaces, lacked any amount of I18N and were trying to get into
foreign markets, etc...things that nobody really thought of when the
project started and because of poor design choices and especially
because of an abundant use of C-style casts...the amount of work
necessary to fix the code was astronomical compared to simply redoing
the whole thing. Cost the company many thousands of dollars to do so
and they're still at it 3 years later.

C-style casts are horrible because they can do anything at any time
without any warning. One minor code change can turn a static cast
into a reinterpret cast. You can't hunt these things down because
regular expressions are useless. The undefined behavior they cause
may or may not turn up at some time in the near future....it may be 2
decades before your code starts crashing in some place utterly
unrelated to the cast in an object that has nothing to do with what is
actually in the memory its using. NOBODY can keep track of every
place that needs to cast, especially within complex desktop
applications that use casting quite regularly. Stuff gets lost and
bad things happen. The new style casts provide a much better method
because they turn up more errors, you'll get a compiler error instead
of successful compile when your static_cast is no longer appropriate,
and can be searched for quite easily. They should be used for all
cases in which they can be, which is all cases in most projects.
 
N

Noah Roberts

  The reason why function and operator overloading is important is not
because it's "handy" or "convenient" or anything like that. It's because
it's *necessary* for generic programming. It's what allows you to do
things like this:

    template<typename T>
    T foo(T value)
    {
        std::cout << "The input value was " << value << std::endl;
        return std::cos(value);
    }

You don't even need to be doing this to run into the lack of function
overloading as a great annoyance. I recently started working in a
project where the lead architect insists it's in C (there are a bunch
of other things that are more frustrating to me about it than that,
but whatever). I've found the necessity to think of obscure names to
make sure to avoid any naming conflicts is a serious quandry. For
example, in writing a generic stack, vector, or managed string class
(I have no problem with C OOP, it can be elegant in its own way) you
have many functions that have a related concept but of course are very
different in implementation. You could use polymorphism to get by,
but what if you don't want to use polymorphism? You're quite stuck
having to name functions like isEmpty to things like
isNamespaceStackEmpty and isNamespaceStringEmpty, and you're always
going to be worried that somewhere someone else is naming their stack
or vector functions the same thing.

Luckily the project code is quite small and this isn't anything but a
minor annoyance, but I can't imagine having to deal with it on a
larger scale. In C++ these things just work themselves out and they
work themselves out quite well in a very direct manner that is easy to
detect errors. There are rules like ADL and more complex rules when
possible overloads are templates, but they're actually not that hard
to remember especially for the gain you get from them. Honestly this
issue has bugged me more than any lack of operator overloads, etc...
 
B

BGB

The reason why function and operator overloading is important is not
because it's "handy" or "convenient" or anything like that. It's because
it's *necessary* for generic programming. It's what allows you to do
things like this:

template<typename T>
T foo(T value)
{
std::cout<< "The input value was "<< value<< std::endl;
return std::cos(value);
}

The above code would be impossible without function and operator
overloading. For example in C you have cos(), cosf() and cosl(), and
you just can't write a macro that would call the proper function based
on the type of the parameter. In C++ you have just different versions
of std::cos(), and the proper version will be automatically called
depending on the type of parameter.

(You could write *three* macros with different names, but now you have
not only triplicated your code, you have made it impossible to call
the macro from other macros without triplicating those too, so the
triplication becomes contagious. Also, it doesn't allow the user to use
a custom user-defined type.)

Likewise it's what allows the std::cout above to work properly.

This is something the most C programmers don't understand. They are
not to blame, though, because many C++ programmers don't know this either
(or have never thought about it).

yes, fair enough. C does not do this...

however, it could be argued that *not* having to write duplicated code
in this case is what is nifty/convinient/...


as well, there is another partial way around this problem:
one can implement a dynamic type-system, and then use wrapping and
run-time type-checking for a lot of this. it is a tradeoff (performance
is worse, ...), but it works.

another variant is to use JVM-like "signature strings" which are used
alongside any data, with a lot of the type-specific behavior being
handled by character-driven logic.

both are fairly useful in implementing VMs, and have different merits
and drawbacks. in my case I end up using both strategies in different
situations (well, along with having parts of the typesystem being
essentially driven by queries to a hierarchical database system...).


otherwise, I had developed a much fancier macro preprocessor that can be
used with C (using it to implement such combinatorial/"generic" code was
one thing I had used it for), but I didn't really use it much for a few
reasons:
cases where it was needed were fairly rare;
it is likely that in cases where one feels the need for such a
mechanism, they are likely "doing it wrong" anyways;
it is kind of a build hassle to run code though an additional
preprocessor prior to running it through the main compiler.

worst case, one will copy/paste the logic a few times and specialize it
for each type or situation involved (or keep it in memory, and write out
a specialized version if/when a need for it pops up, so for example
people have the logic for linked-lists/hashes/quicksort/... generally
memorized).

The major problem in C is not the lack of dynamic binding. It's the
lack of RAII and templates. (And, to some extent, exceptions.)

RAII normally only really deals with cases which can be dealt with
manually though (apart from exceptions, which also don't exist in C, and
things like longjmp are rarely used).

also, several other major languages, such as Java and C#, also lack RAII
(with exceptions, this behavior is handled by the use of a "finally"
clause).
 
B

BGB

You don't even need to be doing this to run into the lack of function
overloading as a great annoyance. I recently started working in a
project where the lead architect insists it's in C (there are a bunch
of other things that are more frustrating to me about it than that,
but whatever). I've found the necessity to think of obscure names to
make sure to avoid any naming conflicts is a serious quandry. For
example, in writing a generic stack, vector, or managed string class
(I have no problem with C OOP, it can be elegant in its own way) you
have many functions that have a related concept but of course are very
different in implementation. You could use polymorphism to get by,
but what if you don't want to use polymorphism? You're quite stuck
having to name functions like isEmpty to things like
isNamespaceStackEmpty and isNamespaceStringEmpty, and you're always
going to be worried that somewhere someone else is naming their stack
or vector functions the same thing.

this is a problem of naming conventions...

for example, a fairly common naming convention is something like:
library_subsystem_function.

so, a person would name things more like:
int MyFooLib_MyContainerStuff_IsHashEmpty()
{
...
}

with "MyFooLib" generally being something a bit more accurate.

now, provided people don't create libraries with the same names, then
there is not a clash.

another convention is:
prefixSomeFunctionName();

where prefix is the short name of a library.
however, this needs to be used a little more sparingly, as the chances
of a 2 to 4 letter prefix or similar clashing are far higher than with a
longer name.

Luckily the project code is quite small and this isn't anything but a
minor annoyance, but I can't imagine having to deal with it on a
larger scale. In C++ these things just work themselves out and they
work themselves out quite well in a very direct manner that is easy to
detect errors. There are rules like ADL and more complex rules when
possible overloads are templates, but they're actually not that hard
to remember especially for the gain you get from them. Honestly this
issue has bugged me more than any lack of operator overloads, etc...


with a good naming convention, then code scales well into the Mloc range
without too many issues on this front.

granted, yes, not everyone agrees as to the ideal naming conventions,
and a lot of older code has had problems here (often due to people not
properly qualifying names, simply describing the action or type in a
terse form, and then running into naming conflicts).
 
I

Ian Collins

RAII normally only really deals with cases which can be dealt with
manually though (apart from exceptions, which also don't exist in C, and
things like longjmp are rarely used).

The beauty of RAII is you don't have to implement it manually! This
eliminates a whole class of common programming errors.
also, several other major languages, such as Java and C#, also lack RAII
(with exceptions, this behavior is handled by the use of a "finally"
clause).

Yet another manual kludge..
 

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

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top