Reputation: 26943
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
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
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