Link to home
Start Free TrialLog in
Avatar of woigl
woigl

asked on

Operator overloading and typecast for printf

I would like to overload an operator to even assign a class value to printf(...);

can someone help me to solve that issue?

Note: i know it is not nice coding style, but based on a specific issue i have to do it like that.
class Integer
{
private:
   int value;
public:
   Integer(int val) : value( val ) {};
   operator int() { return value; };
}
 
int main(int argc, char** argv)
{
   Integer theInt(5);
   int i = theInt; //here it works fine and the value of i will be 5
   printf("%d\n", theInt); //here i dont know what should i overload, plz help
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Infinity08
Infinity08
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
printf is not like other functions ... It EXPECTS a certain type for the arguments, but they are NOT checked at compile time. They are checked at run time. Since code for casting is generated at compile time, the compiler does not know that it needs to cast it to an int (that info is only available at run time), so it doesn't do the cast.


Why not use C++ streams ?
#include <iostream>
 
using namespace std;
 
class Integer {
  private :
    int value;
 
  public :
    Integer(int val) : value( val ) {}
    operator int() { return value; }
 
    friend ostream & operator<<(ostream &os, const Integer &i);
};
 
ostream & operator<<(ostream &os, const Integer &i) {
  os << i.value;
  return os;
}
 
 
int main(void) {
   Integer theInt(5);
   
   cout << theInt << endl;
   
   return 0;
}

Open in new window

Avatar of woigl
woigl

ASKER

Okay, i understand.

But i dont wanna go for COUT as well as i dont want to go for a explicit type cast.

What is the data type of the argument in printf?? is it void* ?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of woigl

ASKER

Okay, the reason for it is, that i have a lot of code which is old and i want now to update this thru a parser. but i dont want to add the type cast.

But it seems like there is no way around as to explicit cast it... or?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>> It's just c++ is not compatible with C va_args calls...

It's not just that ... A C compiler is not able to do the correct casts either, because the information is just not available at compile time. Variadic arguments are unfortunately a run-time construct, which means that the programmer has to ensure the validity of all arguments. If you don't do that (by applying proper explicit casts), then you better prepare for all kinds of errors, bugs, crashes, corruptions, etc.
Avatar of woigl

ASKER

Okay i got it...

i wrote already a precompiler which does a lot of stuff... then i will add this too.

Thanks for your help guys...
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hmm, I always thought that c-cast is rather reinterpret_cast than static_cast, but after verification of this example with gcc - (int)theInt works quite well.
What you are attempting to do here will end up creating you more work than you will save yourself.

a) The implicit cast operator (operator int() { return value; };) is a well know source of defects. Its inclusion in the C++ standard is considered, by many, to be a bad move. It is a case of just because you can do something doesn't mean you should. The problem with this operator is that it implicitly allows the compiler to convert a strongly typed object, in this case Integer, to another type, in this case int. Now whilst that may ostensibly seem ok the problem is that the Integer class is a strong type and int is not. The compiler will normally explicitly stop you passing Integer where another type is expected and as such prevents ambiguity. With the int cast operator for int you'll now find the compiler will perform automatic conversions to not only int but many other types too (such as bool, long, double), which may have unexpected side effects. IT is far better to add an explicit operator to extract the value (int Integer::get_value()), which will prevent this implicit conversion and retain the strong typing semantics of Integer.

e.g. I meant to type this...
   bool b = theInt == 5;
I accidently typed this...
   bool b = theInt = 5;

The compiler doesn't want me since theInt is now a value l-value but without the implicit conversion I get this error: "error C2676: binary '==' : 'Integer' does not define this operator or a conversion to a type acceptable to the predefined operator"

This would still work fine...
   bool b = theInt.get_value() == 5;
The compiler would, rightly, complain about this...
   bool b = theInt.get_value() = 5;

I've just saved myself from a nasty and difficult to detect defect thanks to a typo. Isn't this the point of using a strongly typed class instead of int?


b) printf is completely type unsafe. The following code is complete nonsense, yet it'll compile without warning...

class foo{};
printf("%d", f);

The object of any type system is to try and allow the compiler to identify typing isues at compile time so that you can resolve them. The printf and scanf family of functions completely side-step this type system and, effectively, rendering useless. The solution is to use streams (cout, which you state you don't wish to use) will save you from making wholesale code changes that are completely wrong but almost indetectable until runtime. The problem is, your testing may not pick all these up and once the code is given to your users the problems may then start to manifest. The solution you are looking for here is as follows...

a) Implement a namespace level streaming operator for your Integer class
b) Change all instances of printf to use cout

Understandably this is more work short term but the long term result is you will have code that is typesafe and therefore less likely to be riddled with defects, which are likely to be more time consuming in the long run to try and test and eradicate! Sometimes there are no shortcuts!

-Rx.
#include <iostream>
 
class Integer
{
private:
   int value;
public:
   Integer(int val) : value( val ) {};
   int get_value() const { return value; };
};
 
std::ostream & operator << (std::ostream & os, Integer const & theInt)
{
	os << theInt.get_value();
	return os;
}
 
int main(int argc, char** argv)
{
   Integer theInt(5);
   int i = theInt.get_value();
   std::cout << theInt;
}

Open in new window

>> I always thought that c-cast is rather reinterpret_cast than static_cast, but after verification of this example with gcc - (int)theInt works quite well.
C-style cast performs no sensible compile time checks, therefore your assertion to use static_cast is most definitely favorable since it will flush out un-safe casts. The c style cast is considered deprecated.
>> C-style cast performs no sensible compile time checks

Sure they do ... They check whether it's possible to do the cast or not, and will throw an error if not :


class Integer {
  private :
    int value;
  public :
    Integer(int val) : value( val ) {};
    //operator int() { return value; };
};
 
int main(void) {
  Integer theInt(5);
  int i = (int) theInt;       // <--- won't compile : int expected but Integer found
  return 0;
}

Open in new window

>> The c style cast is considered deprecated.

That's not true either. Every C++ compiler has to still fully support it (there's too much code around that uses C style casts that it would not have been wise to deprecate it). The standard merely advises against its usage in favor of the new (C++) cast operators.

The reason that C style casts are considered more dangerous than the new cast operators, is because you can do any cast you want with them, including casting a pointer to an int for example. It can also cast away const-ness without warning. This unsafe behavior is not possible with static_cast.

That said : in this case, these three lines :

        int i = theInt;
        int i = (int) theInt;
        int i = static_cast<int>(theInt);

are completely equivalent ... The same instructions will be generated. There's no problem.
I think you'll find I said *sensible* compile time checks!

The problem with c-style casts is it's easy just to bang them out without thinking about what you are doing since the syntax is always the same no matter how dangerous or stupid the cast you are attempting. The compiler will pick up obvious issues but some less obvious ones can go undetected. With static_cast and reinterpret_cast your intentions are explicit, specific and type checked. This, at least, was the intention of the new cast operators although I am aware that not everyone agrees with their usage.
#include <iostream>
 
class Integer {
  private :
    int value;
  public :
    Integer(int val) : value( val ) {};
    //operator int() { return value; };
};
 
int main(void) {
  Integer theInt(5);
 
  // This allows for a potentially unsafe cast but it is not obvious
  int * p1 = (int *) &theInt;
 
  // This refuses to compile as an unsafe cast
  int * p2 = static_cast<int *>(&theInt);
 
  // Here we are explicit with our intentions, there is no room for ambiguity!
  int * p3 = reinterpret_cast<int *>(&theInt);
 
  return 0;
}

Open in new window

>> The standard merely advises against its usage in favor of the new (C++) cast operators
Um, that's what depreciated means!
http://en.wikipedia.org/wiki/Deprecation

Let's not get into an argument over the semantics of the wording in the standard, we've been down this road before -- it leads us nowhere ;-)
>> it's easy just to bang them out without thinking about what you are doing

That's what it indeed comes down to. But, a good programmer doesn't just "bang out" code ... (s)he makes sure that the code does exactly what it needs to do.


>> Um, that's what depreciated means!

Ah, you're thinking of the programmer level ... I was thinking of the compiler level. A compiler HAS to implement C style cast support - it is NOT deprecated at that level.



I see that there might be some confusion as to my point of view, so I'll clarify it. In order of preference here are the ways of casting I use :

        1) implicit cast : if the cast will be done correctly implicitly, there's no need to add an explicit cast
        2) C-style cast : if the cast can be safely done with a C-style cast, then I use that since it's easier to read (imo)
        3) C++ cast operators : in all other cases, I'll use these.

In this specific case, we're clearly in 2), so I prefer :

        printf("%d\n", (int) theInt);

over :

        printf("%d\n", static_cast<int>(theInt));

The result is exactly the same, and I think the first is easier to read ...
With a few minor exceptions (I never use c-style casting in C++ [ever]) I think we (sort of) agree. :)