tags:

views:

315

answers:

5

In C++ you can cast a couple ways, C-style casting or C++ casts. Bjarne Stroustrup and a host of other C++ experts say that a good design should have no casting.

Can you help me out here with redesigning the code below to get rid of the cast?

void CProgressBar::SetPosition( int nPos );  //unable to change

void CSaveDialog::UpdatePosition( double dProgress )
{
   double percentOfProgress = dProgress * 100;
   m_pProgressBar->SetPosition( static_cast<int>( percentOfProgress ) );
}

I can modify UpdatePosition, but not SetPosition.

+5  A: 

I think casting in arithmetic can be OK. The type of casting that should really be avoided is up and down (or across) your own class hierarchy.

Also, for casting in arithmetic like this you probably want to be more careful. You should probably apply ceil or floor before casting to an int. I'm not sure it's completely determined which way a cast to int will round. (ie towards +inf, towards -inf, or towards zero)

pauldoo
I'll second this. Just use floor(), because, after all, what you're doing is downcasting from a double to an int - and floor() was built for just that.
Chris
It is actually defined to be floor automatically: 4.9.1 "An rvalue of a floating point type can be converted to an rvalue of an integer type. The conversion truncates; that is, the fractional part is discarded." Personally, I prefer it without the floor.
Todd Gardner
floor for positive values, that is :) For negative values, it's a ceil
Johannes Schaub - litb
@Chris - the cast works as floor() for positive numbers and ceil() for negative numbers (dropping the fractional part like Todd said). I personally would only use floor if I wanted a floating point result, not if I wanted a int result.
Dolphin
It seems like in this case, there wouldn't be a great way of getting around the cast, but I did find boost::numeric_cast that will check for overflows: http://www.boost.org/doc/libs/1_37_0/libs/numeric/conversion/doc/html/boost_numericconversion/improved_numeric_cast__.html
Jared
+3  A: 

I don't think the cast is necessary, there is an implicit cast from double to int. Comeau compiles this:

struct CProgressBar {
  void SetPosition( int nPos );  //unable to change
};

struct CSaveDialog {
  void UpdatePosition( double dProgress )
  {
     m_pProgressBar->SetPosition( dProgress * 100  );
  }

  CProgressBar* m_pProgressBar;
};

Without errors. These kind of cross numeric casts are expected, and that is why there are implicit casts between them.

Todd Gardner
But then you use implicit behaviour - the reader of your code might not even suspect a cast is done.
xtofl
There is still a cast involved. Your compiler just generates it for you. I think there is something along the lines of [ int operator(int)(double d) ] intrinsically defined.
KitsuneYMG
@xtofl - They might not suspect a cast, but I don't think they need to. Numbers "flow" from one type to another. It also makes the rest of the code for UpdatePosition cleaner, easier to read, and more robust, since the implicit cast is the result of the function signature. If the function signature changes to float or double, this will produce correct behavior.
Todd Gardner
With the exception of signed vs unsigned types, I agree that it's usually not confusing to use implicit casts like this. Mixing unsigned types with negative values is obviously another matter, and there I'd argue that you should put the casts in explicitly to help clear things up, *and then* try to get rid of the casts by not mixing types so much.
Steve Jessop
I think most compilers will issue a warning about this (when you enable warnings of course).
pauldoo
+1  A: 

You don't need to cast, doubles are casted automatically to int, if needed. You should add 0.5 to the dProgress value when setting position in order for it to be properly rounded. When casting double to int, the decimal places are truncated and not rounded.

haffax
+1  A: 

Just make percentOfProgress an int.

void CProgressBar::SetPosition( int nPos );  //unable to change

void CSaveDialog::UpdatePosition( double dProgress )
{
   int percentOfProgress = dProgress * 100;
   m_pProgressBar->SetPosition( percentOfProgress );
}
John Pirie
+1  A: 

When people say to avoid casts, they usually mean between user-defined types. You shouldn't, for example, need to downcast from a base class to the derived one very often. If you need that, you should probably take a close look at your class hierarchy and see what's wrong with it. The same goes for reinterpret_cast. If you use that often, to cast between unrelated pointer types, it's probably a sign that you're doing some C-style low-level bit-hackery, which could and should be avoided.

Casting between int and float, or other numeric types, is pretty much to be expected.

jalf