Search code examples
c++templatesargument-dependent-lookup

Argument dependent lookup with stream operator for std::vector


I have created a stream output operator for std::vector in my namespace, and I was able to use that in my previous debug code:

namespace my_company {

template <typename T>
std::ostream &operator<<(std::ostream &os, std::vector<T> const &vec) {
  os << "{";
  for (int i = 0; i < vec.size(); ++i) {
    if (i != 0) {
      os << ", ";
    }
    os << vec[i];
  }
  os << "}";
  return os;
}

template <typename T>
void debug_impl(T const &var, char const *const expr, char const *const file, int const line) {
  using std::vector;
  std::lock_guard lg(debug_print_mutex);
  std::cout << "DEBUG " << file << ":" << line << " T" << std::this_thread::get_id() << "\n  " << expr << ": "
            << var << std::endl;}

#define DEBUG(var) ::my_company::debug_impl(var, #var, __FILE__, __LINE__)

}

But now I want to use my logger class:

namespace my_company {

class Logger {
 public:
  virtual ~Logger() = default;

  virtual void log_impl(LogLevel log_level, std::string message) = 0;

  template <typename... T>
  void log_bits(LogLevel log_level, T const &...ts) {
    if (log_level >= threshold)
      log_impl(log_level, format_bits(ts...));
  }
  
  template <typename... T>
  void trace(T const &...ts) {
    log_bits(LogLevel::trace, ts...);
  }

 private:
  LogLevel threshold = LogLevel::trace;
};

}

The format function is this one:

namespace my_company {

template <typename... T>
std::string format_bits(T const &...ts) {
  std::ostringstream oss;
  // The following is a C++17 [fold expression](https://en.cppreference.com/w/cpp/language/fold).
  (oss << ... << ts);
  return oss.str();
}

}

In the debug_impl function I just delegate to the logger now:

namespace my_company {

template <typename T>
void debug_impl(T const &var, char const *const expr, char const *const file, int const line) {
  logger->template trace(file, ":", line, " T", std::this_thread::get_id(), "\n  ", expr, ": ", var);
}

}

Clang doesn't like that:

logger.h:29:11: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
  (oss << ... << ts);
          ^
logger.h:44:27: note: in instantiation of function template specialization 'my_company::format_bits<const char *, char [2], int, char [3], std::thread::id, char [4], const char *, char [3], std::vector<bool, std::allocator<bool>>>' requested here
      log_impl(log_level, format_bits(ts...));
                          ^
logger.h:69:5: note: in instantiation of function template specialization 'my_company::Logger::log_bits<const char *, char [2], int, char [3], std::thread::id, char [4], const char *, char [3], std::vector<bool, std::allocator<bool>>>' requested here
    log_bits(LogLevel::trace, ts...);
    ^
util.h:693:20: note: in instantiation of function template specialization 'my_company::Logger::trace<const char *, char [2], int, char [3], std::thread::id, char [4], const char *, char [3], std::vector<bool, std::allocator<bool>>>' requested here
  logger->template trace(file, ":", line, " T", std::this_thread::get_id(), "\n  ", expr, ": ", var);
                   ^
rotating_buffer.h:40:5: note: in instantiation of function template specialization 'my_company::debug_impl<std::vector<bool, std::allocator<bool>>>' requested here
    DEBUG(used);
    ^
util.h:699:42: note: expanded from macro 'DEBUG'
#define DEBUG(var) ::my_company::debug_impl(var, #var, __FILE__, __LINE__)
                                         ^
logger.h:13:15: note: 'operator<<' should be declared prior to the call site
std::ostream &operator<<(std::ostream &os, std::vector<T> const &vec) {
              ^

I have already declared that prior to the call site, it is all declared in the logger.h file, which is included by the others. The operator<< is the first thing in that file, so it should be available. I presume that the additional layer of templates did not make it any easier.

I have read a few other questions, but I don't understand how I can solve this issue as putting the operator<< into std isn't allowed, but it doesn't work in my_company and putting it into the global namespace doesn't work either. What can I do?


We use Clang 11 and C++20 on Linux.


Somewhat related:


Solution

  • It seems to be a clang 11 bug.

    Clang 12 accept the code Demo.

    As work around, you might use, instead of (oss << ... << ts);:

    ((oss << ts), ...);