1

I am writing a logging class, which uses source location as a defaulted argument.

I'd like to have a printf-like function call which I hope should be possible using CTAD, but in this case it is for a templated function rather than a templated class, so clearly vlog is not a type (it's a function) hence the error.

Not quite sure where I am going wrong or whether this is actually possible??

log takes a basic string view and works fine.

vlog is the "clever" bit that takes a printf style argument list, i.e. a const char* then variadic arguments of different types. I've tried to include as much code as possible in case there might be a better way of doing this!

C++17 and thanks in advance.

#include <chrono>
#include <experimental/source_location>
#include <mutex>
#include <string_view>
#include <string>
#include <iostream>

namespace LOGGING
{


class Logging
{
    using Ms                = std::chrono::milliseconds;                ///< typename alias
    using source_location   = std::experimental::source_location;       ///< typename alias

    constexpr static Ms defalt_mutex_wait{100};
    
public:
    static bool log(const std::string_view& msg,
                    const source_location& location = source_location::current())
    {
        if (mutx.try_lock_for(defalt_mutex_wait))
        {
            std::cout << location.file_name() << "[" << \
                location.line() << ":" << \
                location.function_name() << "]: " << \
                msg;
            std::cout << std::endl; // flush
            
            mutx.unlock();
            return true;
        }
        return false;
    }

    template <typename... T>
    static bool vlog(const char*&& fmt, T&&... vars,
                    const source_location& location = source_location::current())
    {
        if (mutx.try_lock_for(defalt_mutex_wait))
        {
            char buf[100]{};
            snprintf(buf, sizeof(buf), fmt, vars...);
            std::cout << location.file_name() << "[" << \
                location.line() << ":" << \
                location.function_name() << "]: " << \
                buf;
            std::cout << std::endl; // flush
            
            mutx.unlock();
            return true;
        }
        return false;
    }

    template <typename... T>
    vlog(const char*, T&&...) -> vlog<T...>;

    template<class Rep, class Period>
    [[nodiscard]] static bool lock(std::unique_lock<std::recursive_timed_mutex>& lock, 
                                        const std::chrono::duration<Rep, Period>& timeout_duration = Ms(100))
    {
        if (mutx.try_lock_for(timeout_duration))
        {
            lock = std::move(std::unique_lock<std::recursive_timed_mutex>(mutx));
            mutx.unlock();
            return true;
        }
        return false;
    }

    static bool lock(std::unique_lock<std::recursive_timed_mutex>& lock)
    {
        lock = std::move(std::unique_lock<std::recursive_timed_mutex>(mutx));
        return true;
    }

private:
    Ms              instance_mutex_wait{defalt_mutex_wait};

public:
    Logging(void) noexcept = default;

private:
    static std::recursive_timed_mutex mutx;
};

inline std::recursive_timed_mutex Logging::mutx{};

inline Logging LOG;

} // namespace LOGGING



int main()
{
    using namespace LOGGING;

    LOG.log("Hello world!");
    LOG.vlog<int>("Value is %d", 42);
    LOG.vlog("Value is %d", 42); // HERE

    return 0;
}
<source>:58:34: error: 'vlog<T ...>' does not name a type
   58 |     vlog(const char*, T&&...) -> vlog<T...>;
      |                                  ^~~~~~~~~~
<source>:58:44: error: expected constructor, destructor, or type conversion before ';' token
   58 |     vlog(const char*, T&&...) -> vlog<T...>;

Godbolt link

Context for anyone interested; it is for a multi-threaded and multi-core embedded system (ESP32) and the main need is to prevent different thread log messages getting interleaved.

  • 1
    This is an interesting question, but I had no idea what you're were trying to say until I looked at the code. Here are some suggestions how to make it more clear: Change the title to something like *"how to use `std::source_location::current()` default argument in a function with a parameter pack"*. Reduce the code as much as possible (see [mcve]). Make it ~15 lines: create a single empty function (your `vlog`), show how you call it from `main`, and the errors. Get rid of the deduction guides too. – HolyBlackCat Jul 04 '21 at 20:21
  • 1
    Does this answer your question? [How to use source\_location in a variadic template function?](https://stackoverflow.com/questions/57547273/how-to-use-source-location-in-a-variadic-template-function) – He3lixxx Jul 04 '21 at 20:21
  • "_CTAD for templated function_" - is deduction guides for functions even a thing? – Ted Lyngmo Jul 04 '21 at 20:23
  • 1
    Also, I just stumbled across [this blogpost](https://cor3ntin.github.io/posts/variadic/) which might also be interesting to read – He3lixxx Jul 04 '21 at 20:24
  • 2
    There is no CTAD for function templates. Function templates already get there template arguments deduced for them. CTAD was added for *class* template argument deduction. – NathanOliver Jul 04 '21 at 20:28
  • I always see comments asking for more code. I went too much the wrong way, apologies! I have seen a few of the other source location specific posts but they're not quite what I am after. They all seem to wrap the function in a class/struct and I see no need to construct an object. Even though I guess it would all be optimised away (I's have to look at the assembly). @He3lixxx that blog post actually looks like what I am after. I shall give it a go, thanks – GreenaGiant Jul 04 '21 at 20:33
  • 1
    @GreenaGiant I checked out the blog post, and it's just musings about how the language could've worked. Check the link in my last comment. – HolyBlackCat Jul 04 '21 at 20:36
  • @HolyBlackCat that's why I made the title the way it is, clearly specifying for a templated function. It is the CTAD type of thing I was trying to achieve but for a function, rather than for a class. I didn't mean to mislead, sorry if I did. None of the other questions I found really answered it imo hence why I tried to be specific and detailed. Again, sorry for missing the mark Thanks all for the help anyway. Edit: that came across mega defensive. Dare I apologise again! Cheers for the link, looks promising! – GreenaGiant Jul 04 '21 at 20:37
  • 1
    No problem, don't apologise. Recommended reading: [What is the XY problem?](https://meta.stackexchange.com/q/66377/353058) – HolyBlackCat Jul 04 '21 at 20:40

0 Answers0