Question

I want to make a function called debug that outputs some info about objects. My system contains objects of many different types; some of them contain other objects.

using namespace std; // for brevity
struct dog {string name;};
struct human {string name; string address;};
struct line {list<human*> contents;};
struct pack {vector<dog*> contents;};

I want the function to output the member name of the argument if it has one, or debug the contents member of the argument if it has one. I came up with the following code:

template <class T>
void debug(T object) // T here is a simple object like dog, human, etc
{
    cout << object.name.c_str() << '\n';
}

// A helper function, not really important
template <class T>
void debug_pointer(T* object)
{
    debug(*object);
}

void debug(pack object)
{
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<dog>);
}

void debug(line object)
{
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<human>);
}

Here, the code for pack and line is nearly identical! I would like to avoid writing the same code several times:

struct line {list<human*> contents; typedef human type;};
struct pack {vector<dog*> contents; typedef dog type;};

template <class T>
void debug(T object) // T here is a compound object (having contents)
{
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<T::type>);
}

But this syntax conflicts with the function template for the "simple" objects (has the same signature).

How can i rewrite my code? I don't want to rewrite the first part (declarations for dog, human, etc) because that part of my program is already very complicated, and adding stuff (base classes, member functions, etc) to it just for debugging seems out of place.

Was it helpful?

Solution

The basic code could look like this:

template <typename T> void debug(T const & x)
{
    debug_helper<T, has_name<T>::value>::print(x);
}

We need a helper class:

template <typename, bool> struct debug_helper;

template <typename T> struct debug_helper<T, true>
{
    static void print(T const & x) { /* print x.name */ }
};
template <typename T> struct debug_helper<T, false>
{
    static void print(T const & x) { /* print x.content */ }
};

Now we just need a SFINAE trait class has_name<T>, and a mechanism to print containers. Both those problems are solved almost verbatim in the pretty printer code.

OTHER TIPS

Make the container a template parameter as well:

template <template <typename> class Container, typename T>
void debug(Container<T> object)
{
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<T>);
}

BTW, most of the cases you may want to pass by const reference instead of by value (which requires copying the whole vector/list):

template <template <typename> class Container, typename T>
void debug(const Container<T>& object)

If C++11 can be used, you could use decltype to determine the T from the contents:

template <typename T>
void debug(const T& object)
{
    typedef decltype(*object.contents.front()) T;
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<T>);
}

GCC also has typeof when C++11 cannot be used.

Using C++11, decltype and SFINAE make things easy :)

#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <algorithm>

struct dog { std::string name; };
struct human { std::string name; std::string address; };
struct line { std::list<human*> contents; };
struct pack { std::vector<dog*> contents; };

template <typename T>
auto debug(T const& t) -> decltype(t.name, void(0)) {
  std::cout << t.name << '\n';
}

template <typename T>
auto debug(T const* t) -> decltype(t->name, void(0)) {
  if (t != 0) std::cout << t->name << '\n';
}

struct Debugger {
  template <typename T>
  void operator()(T const& t) { debug(t); }
};

template <typename C>
auto debug(C const& c) -> decltype(c.contents, void(0)) {
  typedef decltype(c.contents) contents_type;
  typedef typename contents_type::value_type type;
  std::for_each(c.contents.begin(), c.contents.end(), Debugger());
}


int main() {
  dog dog1 = { "dog1" }, dog2 = { "dog2" };
  human h1 = { "h1" }, h2 = { "h2" };

  line l; l.contents.push_back(&h1); l.contents.push_back(&h2);

  debug(l);

}

In action at ideone this yields:

h1
h2

as expected :)

Without C++11, it requires some little craft but the principle remains the same, using boost::enable_if you need to create a structure that will provoke a compilation error based on the presence and accessibility of name and contents.

Of course, it would all easier if you simply hooked up the methods in the structures themselves :)

You can use SFINAE to select the overload being used.

I forget the exact details, but you can use it to detect the presence of a "contents" member or a "name" member and then overload based on that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top