Search code examples
c++unique-ptrfmtspdlog

C++ : how to use spdlog to print custom class pointer?


I am facing a little problem. I need to use spdlog to log and I have custom classes. And since, spdlog is able to deal with user defined classes, I could use it log my classes.

But, I my real application, I would like to feed spdlog with a pointer of my class (because there is polymorphism but it's not the point here). And here goes my troubles. When I try to feed spdlog with a unique_ptr of my class, it does not compile.

So here a MWE:

#include <stdio.h>
#include <iostream>  
#include <fstream>
#include <string.h>  

#include <spdlog/spdlog.h>    //
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ostr.h" // must be included to log object 

using namespace std;

struct my_type
{
    int i;
    template<typename OStream>
    friend OStream &operator<<(OStream &os, const my_type &c)
    {
        return os << "[my_type i=" << c.i << "]";
    }
};

template<typename OStream>
OStream &operator<<(OStream &os,const my_type* c)
{
    return os << "[my_type i=" << "pointer" << "]";
}

int main()  { 
    
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    spdlog::logger logger("log_test", console_sink);
    logger.set_level(spdlog::level::trace);
    auto pLog =std::make_shared<spdlog::logger>(logger); //register it if you need to access it globally
        
    std::unique_ptr<my_type> ptrA(new my_type{12});
    pLog->info("user defined type: {}", ptrA);  // of course *ptrA simply works, but in my application I have to give ptrA ...

    return 0;
}

and I get errors from the compiler gcc:

spdlog/fmt/bundled/core.h:1566:15: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = my_type; _Dp = std::default_delete<my_type>]’
const auto& arg = arg_mapper<Context>().map(val);

spdlog/fmt/bundled/core.h:1567:3: error: static assertion failed: Cannot format an argument. To make type T formattable provide a formatter<T> specialization: https://fmt.dev/latest/api.html#udt
static_assert(

spdlog/fmt/bundled/core.h:1184:15: error: use of deleted function ‘fmt::v8::detail::fallback_formatter<T, Char, Enable>::fallback_formatter() [with T = fmt::v8::detail::unformattable; Char = char; Enable = void]’
 Formatter f;
           ^
/spdlog/fmt/bundled/core.h:963:3: note: declared here
fallback_formatter() = delete;
^~~~~~~~~~~~~~~~~~
 spdlog/fmt/bundled/core.h:1185:28: error: ‘struct fmt::v8::detail::fallback_formatter<fmt::v8::detail::unformattable, char, void>’ has no member named ‘parse’
 parse_ctx.advance_to(f.parse(parse_ctx));
                      ~~^~~~~
 spdlog/fmt/bundled/core.h:1186:22: error: ‘struct fmt::v8::detail::fallback_formatter<fmt::v8::detail::unformattable, char, void>’ has no member named ‘format’
 ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
                ~~^~~~~~

I guess the problem is comming from the interaction between template<typename OStream> OStream &operator<<(OStream &os,const my_type* c) and spdlog or fmt. So I tried to play a bit around but I am stuck.

Do you have ideas to solve this problem, keeping pLog->info("user defined type: {}", ptrA); ?


Solution

  • The problem comes from the library fmt since version 8 (used by spdlog since version 1.9.0), pointers are no longer supported. A solution can be to use a wrapper class to store the pointer and precise how fmt should deal with it.