lurscher
lurscher

Reputation: 26943

template string representation of vector of objects

I want to create a template function that generates a string representation of an array of things that have a name() method. The things might be kept by value, or by reference (raw or smart)

        template< typename T>
        struct GetStringRepresentation;

        template< typename T>
        struct GetStringRepresentation< std::vector< std::unique_ptr< T > > >
        {
            inline void ()( const std::vector< std::unique_ptr< T > >& seq, std::string& out )
            {
                size_t size = seq.size();
                for (int i=0; i< size; i++)
                {
                    if (i > 0)
                        out += ", ";
                    out += seq[i]->name();
                }
            }
        };

       template< typename T>
        struct GetStringRepresentation< std::vector< std::shared_ptr< T > > >
        {
            inline void ()( const std::vector< std::shared_ptr< T > >& seq, std::string& out )
            {
                size_t size = seq.size();
                for (int i=0; i< size; i++)
                {
                    if (i > 0)
                        out += ", ";
                    out += seq[i]->name();
                }
            }
        };

        template< typename T>
        struct GetStringRepresentation< std::vector< T* > >
        {
            inline void ()( const std::vector< T* >& seq, std::string& out )
            {
                size_t size = seq.size();
                for (int i=0; i< size; i++)
                {
                    if (i > 0)
                        out += ", ";
                    out += seq[i]->name();
                }
            }
        };

        template< typename T>
        struct GetStringRepresentation< std::vector< T > >
        {
            inline void ()( const std::vector< T >& seq, std::string& out )
            {
                size_t size = seq.size();
                for (int i=0; i< size; i++)
                {
                    if (i > 0)
                        out += ", ";
                    out += seq[i].name();
                }
            }
        };

As you can clearly see, there is a bunch of duplication, specially between the reference specializations. I'm not very up to speed with the best way to do this, i would like to see a better way that removes some, or all of the code duplication

Upvotes: 1

Views: 199

Answers (2)

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361302

I would write this:

struct GetStringRepresentation
{
     template<typename T>
     std::string operator()(std::vector<T> const & seq)
     {
        std::string out;
        size_t size = seq.size();
        for (size_t i=0; i< size; i++)
        {
           if (i > 0)
              out += ", ";
           out += get(seq[i]); //call get() to get the string
        }
        return out;
     }

private:

     template<typename T>
     std::string const & get(T const & t) { return t.name; }

     template<typename T>
     std::string const & get(T const * t) { return t->name; }

     template<typename T>
     std::string const & get(std::unique_ptr<T> const & t) { return t->name; }

     template<typename T>
     std::string const & get(std::shared_ptr<T> const & t) { return t->name; }
};

Upvotes: 1

Kerrek SB
Kerrek SB

Reputation: 476950

Here's a tip to get you started: Specialize the print method only:

template <typename T> void print(T const & x)
{
    std::cout << printer<T>::print(x) << std::endl;
}

template <typename T> struct printer
{
    static std::string print(T const & x) { return x.name(); }
};
template <typename U> struct printer<U*>
{
    static std::string print(U * p) { return p->name(); }
};

This way you only have to write the loop once, and the printer takes care of the details. You can even abstract this further and make a sort of is_pointer_like trait (everything that supports ->), which in turn you specialize for all the smart pointers:

printer<T, is_pointer_like<T>::value>::print(x); // etc.

template <typename T, bool> struct printer { /* as before */ }

template <typename T> struct printer<T, true>
{
    static std::string print(T const & p) { return p->name(); }
};

// Traits:
template <typename T> struct is_pointer_like : std::false_type { };
template <typename U> struct is_pointer_like<U*> : std::true_type { };
template <typename U> struct is_pointer_like<std::shared_ptr<U>> : std::true_type { };

For a similar idea, see the pretty printer.

Upvotes: 4

Related Questions