fmtlib / fmt

A modern formatting library
https://fmt.dev
Other
19.83k stars 2.42k forks source link

Custom pointer formatter is invalid after fmt 9.0. #4037

Closed Minavery closed 1 week ago

Minavery commented 1 week ago

I find my old code is invalid when using fmt 10.2.1.

''' template<> struct fmt::formatter<float > : formatter { auto format(float pointer, fmt::format_context& ctx) const { if (pointer != nullptr) { auto&& str = fmt::format("{}={}", (void)pointer, *pointer); return formatter::format(str, ctx); } else { return formatter::format("nullptr", ctx); }
} };

int main() { float a = 0.0; fmt::print("{}\n",&a); } '''

error is "Formatting of non-void pointer is disallowed."

Compiler Explorer Code.cpp.zip

vitaut commented 1 week ago

The error is expected, formatting of non-void pointers is intentionally disallowed although there were some gaps in diagnostic in earlier versions. You should either format the value or convert to void* / use fmt::ptr.

Minavery commented 6 days ago

The error is expected, formatting of non-void pointers is intentionally disallowed although there were some gaps in diagnostic in earlier versions. You should either format the value or convert to void* / use fmt::ptr.

Okay.
I find my code pasted has some error. it should be:

template<> 
struct fmt::formatter<float *> : formatter<string_view> { 
    auto format(float* pointer, fmt::format_context& ctx) const { 
        if (pointer != nullptr) { 
             auto&& str = fmt::format("*{}={}", (void*)pointer, *pointer); 
             return formatter<fmt::string_view>::format(str, ctx); } 
        else { 
             return formatter<fmt::string_view>::format("nullptr", ctx); 
        }  
    } 
};

int main() {
    float a = 0.0;
    fmt::print("{}\n",&a);
}

but there is compile error "Formatting of non-void pointer is disallowed. " yet. How could I fomat a pointer same as fmt 8.0?

vitaut commented 6 days ago

You can do it by wrapping a pointer in your type and providing a formatter for it:

#include <fmt/format.h>

template <typename T>
struct ptr_view {
    T* value;
};

template <typename T>
auto ptr(T* p) -> ptr_view<T> { return {p}; }

template <typename T> 
struct fmt::formatter<ptr_view<T>> : formatter<string_view> { 
    auto format(::ptr_view<T> pointer, fmt::format_context& ctx) const { 
        if (pointer.value != nullptr) { 
             auto&& str = fmt::format("*{}={}", (void*)pointer.value, *pointer.value); 
             return formatter<fmt::string_view>::format(str, ctx); } 
        else { 
             return formatter<fmt::string_view>::format("nullptr", ctx); 
        }  
    } 
};

int main() {
    float a = 0.0;
    fmt::print("{}\n", ptr(&a));
}

https://www.godbolt.org/z/Y31c818hv

Minavery commented 5 days ago

You can do it by wrapping a pointer in your type and providing a formatter for it:

#include <fmt/format.h>

template <typename T>
struct ptr_view {
    T* value;
};

template <typename T>
auto ptr(T* p) -> ptr_view<T> { return {p}; }

template <typename T> 
struct fmt::formatter<ptr_view<T>> : formatter<string_view> { 
    auto format(::ptr_view<T> pointer, fmt::format_context& ctx) const { 
        if (pointer.value != nullptr) { 
             auto&& str = fmt::format("*{}={}", (void*)pointer.value, *pointer.value); 
             return formatter<fmt::string_view>::format(str, ctx); } 
        else { 
             return formatter<fmt::string_view>::format("nullptr", ctx); 
        }  
    } 
};

int main() {
    float a = 0.0;
    fmt::print("{}\n", ptr(&a));
}

https://www.godbolt.org/z/Y31c818hv

Thanks. It works fine. I want to know why there is difference between pointer and others?

vitaut commented 5 days ago

The motivation is explained in https://accu.org/journals/overload/17/89/wilson_1539/, Defective argument types.