lee
lee

Reputation:

C++ template specialization question

I'm trying to implement a generic toString() function that would work on all types. All our internal classes derive from Abstract which includes a signature for toString(). In other words, all our internal classes have in some form, a toString method.

The problem is, the primitive types (int, char, double..) don't have a native toString function. But we do have a utility function that calls the lexical_cast to give back the string value of the primitive. We don't want a whole bunch of if statements depending So I'm trying to create a templated utility class that would do the job.

My first hack at this is the below:

    template<class T>
    class ObjectToString {
    public:
        string objectToString(T value) {
            iil::stringUtils::StringSp ret(stringUtils::valueToString<T>(value));
            return ret;
        }
    };

    template<>
    class ObjectToString <Abstract<T>*> {
    public:
        iil::stringUtils::StringSp objectToString(Abstract<T>* value) {
            return iil::stringUtils::StringSp(new std::string("AAAA"));
        }
    };

The problem now is, since Abstract is a templated class, it needs the template value T. I have no idea how to set that. Could anyone advise?

Upvotes: 1

Views: 674

Answers (4)

Artyom
Artyom

Reputation: 31233

You just don't think in C++ way. C++ already has "toString" that is called operator<< to std::ostream. You need to implement it for your classes.

And if you want to support inheritence, do this:

#include <iostream>
#include <boost/lexical_cast.hpp>
#include <string>

class streamable {
public:
    virtual void stream(std::ostream &) const = 0;
};
std::ostream &operator<<(std::ostream &out,streamable const &obj)
{
    obj.stream(out);
    return out;
}

// Now anything derived from streamable can be written to std::ostream
// For example:

class bar : public streamable {
    int x;
    int y;
public:
    bar(int a,int b) : x(a),y(b){}
    virtual void stream(std::ostream &out) const { out<<x<<":"<<y; }
};

int main()
{
    bar b(1,3);
    std::cout<< b << std::endl;

    // and converted to string
    std::string str=boost::lexical_cast<std::string>(b);

    std::cout<< str <<std::endl;
}

This is C++ way, and as you can see you have boost::lexical_cast for free.

EDIT for your case:

template<typename T>
class Abstract {
public:
    virtual void stream(std::ostream &) const = 0;
};

template<typename T>
std::ostream &operator<<(std::ostream &out,Abstract<T> const &obj)
{
    obj.stream(out);
    return out;
}

Now if you don't like boost::lexical_cast, implement string_cast as simple as

template<typename T>
std::string string_cast(T const &obj)
{
   std::ostringstram ss;
   ss<<obj;
   return ss.str();
}

Upvotes: 1

dcw
dcw

Reputation: 3555

This has been dealt with at length by Matthew Wilson in the form of shims, as described in this Dr Dobb's article, and the books Imperfect C++ and Extended STL. They underlie the technology that allow the FastFormat and Pantheios libraries to deal with argument types generically.

Upvotes: 1

Frederik Slijkerman
Frederik Slijkerman

Reputation: 6529

How about simply providing a specialization for lexical_cast?

template<> string lexical_cast(Abstract* obj)
{
  return obj->toString();
}

Upvotes: 7

Frederik Slijkerman
Frederik Slijkerman

Reputation: 6529

Isn't your problem much simpler? On all Abstract objects you know what to do, so all you need is to provide overloaded functions for built-in types:

string getAsString(Abstract *obj)
{
  return obj->toString();
}

string getAsString(int x)
{
  return intToStr(x);
}

string getAsString(double x)
{
  return doubleToStr(x);
}

etc, where you implement intToStr() and doubleToStr() as appropriate.

Upvotes: 1

Related Questions