0

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:

Martin Ueding
  • 8,245
  • 6
  • 46
  • 92
  • I would say clang bug, using `((oss << ts), ...);` passes [Demo](https://godbolt.org/z/T8xbdP8vc). – Jarod42 Apr 16 '21 at 08:54
  • Yes, that works for me! And the old variant works with Clang 12 in your Demo. So the bug has already been fixed. Would you want to turn that into an answer such that I can accept that? – Martin Ueding Apr 16 '21 at 12:34

1 Answers1

1

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), ...);
Jarod42
  • 203,559
  • 14
  • 181
  • 302