views:

109

answers:

3

Hi All I am trying to build small logger library. I am facig some problem with c++ templates. Here is what my class structure looks like.

class abstract_logger_t  {
public:
  typedef abstract_logger_t logger_type;
  template<typename data_t>
  abstract_logger_t& log(const data_t& data)  {
    return *this;
  }
};

class stdout_logger_t : public abstract_logger_t  {
public:
  typedef stdout_logger_t logger_type;
  template<typename data_t>
  stdout_logger_t& log(const data_t& data)  {
    cout << data << endl;
    return *this;
  }
};


template<typename logger_t, typename data_t>
void output(logger_t& logger, const data_t& data)  {
  static_cast<typename logger_t::logger_type&>(logger).log(data);
  cout << data;
}


template<typename data_t>
abstract_logger_t& operator<< (abstract_logger_t& logger, const data_t& data)  {
  output(logger, data);
  return logger;
}


stdout_logger_t logger;
logger << "Hi " << 1;

Here I am expecting stdout_logger_t::log to be called by output. but it looks like derived type is getting lost and abstract_logger_t::log ends up getting called. Can someone tell me if I am doing something wrong ?

+1  A: 
template<typename data_t>
abstract_logger_t& operator<< (abstract_logger_t& logger, const data_t& data)  {
  output(logger, data);
  return logger;
}

Here, whatever logger you pass in, the compiler will convert it into an abstract_logger_t&. You need to make the first argument templated too.

template<typename T, typename data_t>
T& operator<< (T& logger, const data_t& data)  {
  output(logger, data);
  return logger;
}
KennyTM
+2  A: 

There is no need to make things more complicated than they should be. Heritage here is useless, unless you want to use virtual methods, in which case you won't make much use of templates. Here is an updated version of your code. Simply add other logger classes when you need new ones.

class stdout_logger_t 
{
public:
  typedef stdout_logger_t logger_type;
  template<typename data_t>
  logger_type& log(const data_t& data)  {
    cout << data << endl;
    return *this;
  }
};
class lazy_logger_t 
{
public:
  typedef lazy_logger_t logger_type;
  template<typename data_t>
  logger_type& log(const data_t& data)  {
    return *this;
  }
};    
template<typename logger_t, typename data_t>
void output(logger_t& logger, const data_t& data)  {
  logger.log(data);
}
template<typename logger_t, typename data_t>
logger_t& operator<< (logger_t& logger, const data_t& data)  {
  output(logger, data);
  return logger;
}

stdout_logger_t logger;
lazy_logger_t lazyLogger;
logger << "Hi " << 1;
lazyLogger << "Hi " << 1;

Be careful, i am pretty sure this logger system won't work with std::endl.

Benoît
Also, this will put a `std::endl` after __every__ expression in a `<<` chain. So `logger < "Hi" << 1;` would result in `"Hi"` and `1` being emitted on separate lines.
sbi
A: 

It seems you are confusing templates with runtime polymorphism. Also, your abstract_logger_t class is not abstract. You're invokung output from operator<< with logger_t=abstract_logger_t. The template parameter is determined via checking static types, not the dynamic types. Template argument deduction and template instantiation are compile-time mechanisms. This should answer your question.

BTW: The convention is to use CamlCase names for template parameters.

sellibitze
Err.. whose convention? Certainly not mine....
Billy ONeal