#Why does this compile?

17 messages · Page 1 of 1 (latest)

toxic beacon
#

why can I call set_log_level(...) when it is part of the flogger namespace thus needing flogger::set_log_level()?

main.cpp

#include <thread>
#include <chrono>

#include "flogger.h"

auto print_error_main = flogger::bind_error("test");
auto print_info_main  = flogger::bind_info("test2135");

int main() {
  set_log_level(flogger::log_level::trace);  // should be flogger::set_log_level, but still compiles
  fprint_debug(print_error_main, "hello world! %d", 32);
  std::this_thread::sleep_for(std::chrono::milliseconds(250));
  fprint_trace(print_info_main, "info path active");
}
floral adderBOT
#

When your question is answered use !solved or the button below to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

toxic beacon
#

flogger.h

### flogger.h

#pragma once
#include <cstdarg>

namespace flogger {

  enum class log_level {
    trace = 0,
    debug = 1,
    info  = 2,
    error = 3,
    off   = 4
  };

  void set_log_level(log_level lvl);
  log_level get_log_level();

  void reset_timer();
  void set_time_enabled(bool v);
  bool is_time_enabled();

  struct logger {
    const char* program;

    void error(const char* file, const char* func, int line, const char* fmt, ...) const;
    void info(const char* file, const char* func, int line, const char* fmt, ...) const;
    void debug(const char* file, const char* func, int line, const char* fmt, ...) const;
    void trace(const char* file, const char* func, int line, const char* fmt, ...) const;
  };

  logger bind_error(const char* log_section);
  logger bind_info(const char* log_section);
  logger bind_debug(const char* log_section);
  logger bind_trace(const char* log_section);
}

#define fprint_error(ctx, fmt, ...) (ctx).error(__FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define fprint_info(ctx, fmt, ...)  (ctx).info(__FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define fprint_debug(ctx, fmt, ...) (ctx).debug(__FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define fprint_trace(ctx, fmt, ...) (ctx).trace(__FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
indigo crescent
toxic beacon
indigo crescent
#

yes

#

this is just very obvious

#

and I have cppreference on speed-dial ;D

toxic beacon
#

so this ADL stuff allows me to write set_log_level() even though this symbol/function is part of a namespace flogger

#

or well, 'allows me' -> has the side-effect of allowing me to do that

#

I am very confused

indigo crescent
#

when you have an unqualified call (i.e. you have no :: in the function call), then the compiler will not only look "normally" for the function in the current namespace scope but also in all namespaces that are "associated" with the types of the arguments that were given in the call
"associated" means the namespaces to which the type belongs if it is a class or enumeration and some more (don't want to list everything here, see the link for that)

toxic beacon
#

that are "associated" with the types of the arguments that were given in the call
aaaah ok

indigo crescent
#

in your example, flogger::log_level::trace has enumeration type flogger::log_level, so in addition to looking for a function named set_log_level in the global namespace scope, lookup will also look for it in flogger

#

this is how operator overloading is made to work

#

since you can't qualify the namespace scope to look into for operator+ if you use +, they came up with this behavior (not only for operators, but all unqualified calls)

toxic beacon
#

😎 ok, thank you C++ helpdesk
20 sec response time was pretty good
cheers!