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

woiglAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

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

An explicit cast will unfortunately be needed.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Infinity08Commented:
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

0
woiglAuthor Commented:
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* ?
0
Become a Certified Penetration Testing Engineer

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

Infinity08Commented:
>> What is the data type of the argument in printf?? is it void* ?

It is whatever datatype you passed ... It isn't modified. So, in your case, it will be the Integer class instance. printf will then (at run time) try to interpret that Integer class instance as an int, but it can't, because they're incompatible types.

Remember that the cast happens at compile time, and the compiler just doesn't have the information to know that it needs to do the cast to int ... you need to do it explicitly.


>> But i dont wanna go for COUT

I assume you have a good reason for that ...


>> as well as i dont want to go for a explicit type cast.

There's no way around it. printf is a C interface making use of variadic arguments. They require a lot of attention of the programmer, and that often includes explicit casts.

For example, this :

        double d = 1.0;
        printf("%d", d);

will not work. An explicit cast is needed :

        double d = 1.0;
        printf("%d", (int) d);
0
Infinity08Commented:
>> What is the data type of the argument in printf?? is it void* ?

Maybe to be a bit more clear : this is how printf is declared :

        int printf(const char *format, ...);

The ... can be any number of arguments of any type. These arguments are interpreted at run time based on the format specifiers in the format string. A %d for example will mean that the corresponding argument will be interpreted as an int. If that argument is anything other than an int, then you're in trouble ;) And in your case, it's an Integer class instance, so ...
0
woiglAuthor Commented:
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?
0
ravenplCommented:
If You look for separate confirmation, You just can't - look at the error message.
What's more - my gcc casts warning only
 cannot pass objects of non-POD type class Integer through ...; call will abort at runtime
but compiles fine - unfortunately running compiled binary results in 'Illegal instruction'.
It's just c++ is not compatible with C va_args calls...
0
Infinity08Commented:
So, you want to modify this :

        int val = 5;
        printf("%d", val);

to this :

        Integer val(5);           // <--- only this line modified
        printf("%d", val);

and it should work ?

If so, then there's really no way but to add the cast explicitly. You can however automate that if you wish by using an intelligent pre-compiler stage. One that knows how printf works, and can add the correct casts to the arguments.
You would have to write that pre-compiler yourself though, and call it on the code before the compiler compiles it.

Depending on the size of the project, this might or might not be worth it. It's not gonna be simple though ...
0
Infinity08Commented:
>> 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.
0
woiglAuthor Commented:
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...
0
ravenplCommented:
BTW: printf("%d\n", (int) theInt);
is not really what You want.  What if the class is virtual, or more complex?
printf("%d\n", static_cast<int>(theInt));
0
ravenplCommented:
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.
0
evilrixSenior Software Engineer (Avast)Commented:
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

0
evilrixSenior Software Engineer (Avast)Commented:
>> 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.
0
Infinity08Commented:
>> 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

0
Infinity08Commented:
>> 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.
0
evilrixSenior Software Engineer (Avast)Commented:
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

0
evilrixSenior Software Engineer (Avast)Commented:
>> 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 ;-)
0
Infinity08Commented:
>> 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 ...
0
evilrixSenior Software Engineer (Avast)Commented:
With a few minor exceptions (I never use c-style casting in C++ [ever]) I think we (sort of) agree. :)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Editors IDEs

From novice to tech pro — start learning today.