1

I've been following along with The Cherno's game engine series, and I've run into an issue I can't seem to fix. This is the only part of the series so far that I don't understand, and nothing I've found on the internet has helped.

We're creating an event system, where certain events (input, window resizing, etc.) are logged to the console using a logging system built from the spdlog library. The event dispatcher dispatches the events to the appropriate function:

class EventDispatcher {
    template<typename T>
    using EventFn = std::function<bool(T&)>;
public:
    EventDispatcher(Event& event)
        : m_Event(event) {}

    template<typename T>
    bool Dispatch(EventFn<T> func) {
    if (m_Event.GetEventType() == T::GetStaticType()) {
        m_Event.m_Handled = func(*(T*)&m_Event);
        return true;
    }
    return false;
}
private:
    Event& m_Event;
};

When I build my project, I get the error error C2338: Cannot format argument. To make type T formattable provide a formatter<T> specialization. Maybe I don't understand C++ Templates well enough or I'm misusing the library, but I can't figure out for the life of me what is causing this error. I made sure I'm including <fmt/ostream.h> as suggested here, but it didn't solve the issue.

If anybody knows anything about spdlog and could help me out, I would appreciate it. Thank you!

Edit:

The full output error is

1>...\vendor\spdlog\include\spdlog\fmt\bundled\core.h(1568,58): error C2338: Cannot format an argument. To make type T formattable provide a formatter<T> specialization: https://fmt.dev/latest/api.html#udt

which points to line 1568 in spdlog\include\spdlog\fmt\core.h:

1563    template <bool IS_PACKED, typename Context, type, typename T,
1564              FMT_ENABLE_IF(IS_PACKED)>
1565    FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value<Context> {
1566      const auto& arg = arg_mapper<Context>().map(val);
1567      static_assert(
1568          std::is_same<decltype(arg), const unformattable&>::value, // <-- error
1569          "Cannot format an argument. To make type T formattable provide a "
1570          "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
1571      return {arg};
1572    }

I know this doesn't really tell us anything, but this is all the output console is giving me. The file depends on a few other files in the solution, so it would be pretty difficult for me to provide a sample that could run easily. I'm sorry it's so little information, but any help at all would go a long way.

Joe Pigott
  • 11
  • 3
  • What's the full error message from the compiler, including the informational lines? What line has the error? [C2338](https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2338) typically comes from a `static_assert`, which is not shown in the question. [Edit] the question to include a [mre]. – 1201ProgramAlarm Oct 04 '21 at 22:19
  • @1201ProgramAlarm edited – Joe Pigott Oct 05 '21 at 02:16

1 Answers1

1

You're passing a type in to a spdlog function, and spdlog doesn't know how to format it.

For example, if you try to do something like the following you'll receive the compilation error you're currently seeing:

class Person
{ 
public:
    explicit Person( std::string name )
        : name_{ std::move( name ) }
    { }
private:
    std::string name_;
};

int main( )
{
     Person person{ "Bill" };

     // spdlog has no idea how to format a Person object and as 
     // a result this will not compile.
     spdlog::info( "{}", person );
}

If you would like to print the contents of a custom type you can do the following:

#include "spdlog/fmt/ostr.h"

class Person
{ 
public:
    explicit Person( std::string name )
        : name_{ std::move( name ) }
    { }
    
    // You must overload the insertion operator so that
    // spdlog knows what to do when it encounters a Person.
    template<typename Stream>
    friend Stream& operator<<( Stream& os, const Person& person)
    {
        return os << "Person name: " << person.name_;
    }

private:
    std::string name_;
};

int main( )
{
     Person person{ "Bill" };

     // spdlog now knows how to format a Person object.
     spdlog::info( "{}", person );
}

What you've shown does not actually show where you're using spdlog. I suspect it's happening in your event handler, (the callable you're passing to Dispatch).

WBuck
  • 5,162
  • 2
  • 25
  • 36