views:

200

answers:

6

I'm currently learning C++ and i run into the simple problem of converting an int to a string. I've worked around it using:

string IntToString(int Number)
{
    stringstream Stream;
    Stream << Number;
    return Stream.str();
}

but though it would be more elegant to use something like:

int x = 5;
string y = x.toString();

but how do i add the toString() method to a built in type?

or am i missing something totally fundamental?

+2  A: 

As an alternative, you could overload operator << :

string& operator<<(string& str, int Number)
{
    stringstream Stream;
    Stream << Number;
    str = Stream.str();
    return str;
}

int x = 5;
string y;
y << x;
Péter Török
Using `operator=()` it would be possible to do things like `const string y = x`. But may be not as easy to spot and could cause confusion.
mxp
Are you sure this does not lead to nasty side effects?
Nikko
@Nikko, no, I am not :-) although we have used this in a project and it worked for us. Do you have anything concrete in mind?
Péter Török
Opening a can of worms. `std::string` is in the std namespace, but this operator isn't. That means that ADL (Koenig lookup) won't find it. To see this, add an extra line `namespace foo { y << x; }` to the example.
MSalters
@MSalters, good point. One has to use namespaces carefully. In the project I mentioned, AFAIR this was defined in the project's outermost namespace, and every component was embedded within its own child namespace like `project::module1` etc.
Péter Török
@mxp: I don't think you can overload `operator=` from outside of the class
David Rodríguez - dribeas
+2  A: 

Extension methods are not supported by the C++ language. The ideal workaround is a separate function, like you've shown already with IntToString.

Other alternatives which are not recommended (included only for completeness!) is to derive a class from std::string and 'extend' it with your own member functions - but deriving from the standard container classes is a bad idea. You could also write your own string class, but why reinvent the wheel? Your IntToString method is fine, so stick to that.

AshleysBrain
No. Don't write your own string class. This is the beginner mistake! ( and don't derive from standard containers, because their destructors are virtual and they are not meant to be).
Nikko
Fair point, clarified the answer.
AshleysBrain
+4  A: 

Built-in types or Plain Old Datas like ints do not have methods. The idea of streams is that you can convert anything from anything, not only PODs.

What you are looking for, is a boost::lexical_cast<>, which can work like this:

int i = 12;
std::string s = boost::lexical_cast< std::string >( i );

It can be found in the boost library.

You can do your basic version of this method yourself based on your IntToString method, but templated to work with any types.

template<typename To, typename From >
To my_own_cast(const From & f)
{
    std::stringstream stream;
    stream << f;

    To t;
    stream >> t;
    return t;
}

used like this:

std::string s;
s = my_own_cast< std::string >( 12 );

int i= my_own_cast< int >( s );

Normally, as far as I know, lexical_cast could be a bit more complex in the last version than just using a stream, and try to be more effecient when it can. It was discussed, I'm not sure it's implemented.

Nikko
A: 

I would not derive any class from basic_string/string. This is not recommended and no method in string is virtual, it does not have a virtual destructor (hence deriving could reault in memory leaks)

Make it pretty for yourself:

You could create a static class (class containing only static methods) in order keep it all in the one place. Use method overloading for int, float, double, etc.

class Converter {
   private:
       Converter() {}

   public:
     static string toString(int i) {
        ... your implementation ...
     }

     static string toString(float f) {
        ... same but floats.
     }
     .... other overloads ....
};

... somewhere else in your code ....

string c = Converter::toString(5);
Adrian Regan
Isn't it better style to use a namespace than such a class that happens to only have static functions? You're only doing it for the grouping anyway.
harms
That's true, it is syntactic, but I like to keep things as 'Object Oriented' as possible. On reflection, I would also use a single templated method within the Converter class.
Adrian Regan
Also, it leaves the door open to add methods for encoding conventions, etc, in a more consise and object oriented way.
Adrian Regan
@Adrian, how does creating a dummy class to use as a namespace is more object oriented that using a namespace? Why work around features in the languages designed expressly for the task at hand?
iconiK
Gave you a good comment for this. Namespaces are not specifically designed to group functions together, they are used to counteract naming collisions in large scale code bases and libraries. Making Converter a static class implementation is more in line with using C++ as an object oriented language versus a mismash of C/C++. You could therefore put the Converter class definition in a namespace of other utility classes for example. Good comment though, possibly requires a whole discussion in itself.
Adrian Regan
I think you're wrong about namespaces and that this code is not very C++ but well...
Nikko
Like I said, you could devote a complete thread to what is and is not C++...
Adrian Regan
So-called "toolbox classes" like this are really only useful in languages like Java and C# where there are no free functions. They disallow free functions because it's not object oriented, but then in a fit of pragmatism they allow us to avoid object orientation anyway, namely static methods. Make no mistake, static classes are in *no way* more object oriented than namespaces. It's simply a more convoluted and less clear way to achieve some of the same.
harms
I really don't think it's fair to downgrade the vote on this one. I have after all said that what is being argued here is outside of what was asked in the question and deserves a discussion of it's own.
Adrian Regan
+7  A: 

No, you can't extend classes like that - in C++ the preferred method is to write free functions, as you are doing. These can profitably often be templates:

template <typename T>
string ToString(const T & t)
{
    stringstream Stream;
    Stream << t;
    return Stream.str();
}

which is effectively what lexical_cast does.

anon
+1  A: 

They are not supported, but the only drawback is a slight lack of notational consistency. You have to write:

SomeClass someInstance;
ToString(someInstance);

instead of:

someInstance.ToString();

This can be somewhat annoying, but it is not a serious problem.

Dimitri C.