3

I am integrating a third-party library into my project. The library provides hooks to redirect its log messages and provides a struct with file, line, severity, message, etc. My project uses std::source_location for logging. How do I construct std::source_location from the values provided by the library? I want the original file/line to show up in my logs rather than the hook function. Thanks

1
  • if location is fixed, #line directive (msvc, gcc) might help. Commented Jul 31, 2023 at 10:23

3 Answers 3

4

How do I construct std::source_location from the values provided by the library?

You don't.

std::source_location is specifically designed to ensure that any given instance got its data from the compiler, not "values provided by the library". This requires that the object is created by the library's source code internally. If it wasn't written to do that, then you can't get a source_location for it.

You need to create an overload of your logging function that can take the values directly as opposed to using source_location.

Sign up to request clarification or add additional context in comments.

Comments

4

This is not generally possible; the only standardized ways to obtain a source_location are its default constructor (which gives you an empty object) and current() (which gives you an object with values corresponding to the call site). There are no standardized setters, so you cannot portably modify the field values of an existing source_location.

If we examine major implementations: MS-STL, libstdc++, libc++; you can see that while it is possible to supply arguments to current() these are different in each case (so the code would be non-portable, even targeting just these three implementations, notwithstanding that the interface could be changed at any time, since it is not intended to be called by user code) and in the case of libstdc++ the type to be passed in is private. In addition, the source_location type does not have ownership semantics so you would need to ensure lifetime to avoid dangling pointers. Finally, current() is consteval so you would not be able to call it with runtime values in any case.

That leaves only modifying the private data of a source_location instance; while possible via the usual tricks, this would still have lifetime issues and would be fragile and have undefined behavior since modifying Standard library objects is not permitted.

It would be better to switch your library to use a source location class designed to be constructible with runtime values. For example, you could use boost::source_location, or write your own.

Comments

1
namespace notstd {
  struct source_state {
    std::uint_least_t line = 0;
    std::uint_least_t column = 0;
    std::string file;
    std::string function;
  };

  struct source_location : std::source_location {
    std::optional<source_state> ss;
    constexpr source_location(std::source_location const& base):
      std::source_location(base)
    {}
    source_location(source_state manual):
      ss(std::move(manual))
    {}
    constexpr std::uint_least_t line() const {
      if (ss) return ss->line;
      return std::source_location::line();
    }
    constexpr std::uint_least_t column() const {
      if (ss) return ss->column;
      return std::source_location::column();
    }
    char const* file_name() const {
      if (ss) return ss->file.c_str();
      return std::source_location::file_name();
    }
    char const* function_name() const {
      if (ss) return ss->function.c_str();
      return std::source_location::function_name();
    }
  };
}

this is a pretty close to drop-in replacement for std::source_location that permits you to manually specify parameters.

Simply replace your own use of std::source_location with the above. Your existing code will probably compile. (the biggest API difference is that file name/function name are no longer constexpr; that is hard to pull off, so I didn't)

And now you can use notstd::source_state to pass in a manual location.

3 Comments

Since the methods are non-virtual, if you use your nostd::source_location as a std::source_location, it won't return the custom state but the default values for std::source_location. So it's probably better/safer to not inherit from std::source_location and to fill in source_state with the value from std::source_location during construction. Something like this
@Nahor I mean, you have to replace all uses of std source location with the above, yes, or you'll get slicing.
@yakk-adam-nevraumont indeed, and by not inheriting, you're forced to do that replacement everywhere, and remove the risk of slicing, and thus why it's a "better/safer" solution (and as a bonus, it's half the size, "don't pay for what you don't use")

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.