Reputation: 1339
How does one define a formatter for private class types that are inside of namespaces? Buidlable code at https://github.com/cmorganBE/fmttest. I have a number of classes like:
namespace example_namespace {
class someclass {
private:
enum class someinternalvalue {
these = 2,
are = 9,
examples = 10
};
someinternalvalue val;
public:
someclass() : val(someinternalvalue::are){
}
void somefunction() {
spdlog::info("{}", val);
}
};
}
And a formatter outside of the namespace (because of compiler errors about fmt not being in the namespace):
template<>
struct fmt::formatter<example_namespace::someclass::someinternalvalue> : formatter<string_view> {
public:
template <typename FmtContext>
constexpr auto format(example_namespace::someclass::someinternalvalue const &t, FmtContext& ctx) const {
const char* str;
switch (t)
{
case example_namespace::someclass::someinternalvalue::these:
str = "these";
break;
case example_namespace::someclass::someinternalvalue::are:
str = "are";
break;
case example_namespace::someclass::someinternalvalue::examples:
str = "examples";
default:
str = "unknown ValveStateComplex state";
break;
}
return formatter<string_view>::format(str, ctx);
}
};
Understandably this results in errors as the formatter is outside of the class:
home/cmorgan/projects/test/fmttest/main.cpp:53:15: required from here
/home/cmorgan/projects/test/fmttest/main.cpp:67:18: error: ‘enum class example_namespace::someclass::someinternalvalue’ is private within this context
67 | case example_namespace::someclass::someinternalvalue::these:
| ^~~~~~~~~~~~~~~~~
/home/cmorgan/projects/test/fmttest/main.cpp:40:20: note: declared private here
40 | enum class someinternalvalue {
| ^~~~~~~~~~~~~~~~~
/home/cmorgan/projects/test/fmttest/main.cpp:70:18: error: ‘enum class example_namespace::someclass::someinternalvalue’ is private within this context
70 | case example_namespace::someclass::someinternalvalue::are:
| ^~~~~~~~~~~~~~~~~
/home/cmorgan/projects/test/fmttest/main.cpp:40:20: note: declared private here
40 | enum class someinternalvalue {
| ^~~~~~~~~~~~~~~~~
/home/cmorgan/projects/test/fmttest/main.cpp:73:18: error: ‘enum class example_namespace::someclass::someinternalvalue’ is private within this context
73 | case example_namespace::someclass::someinternalvalue::examples:
| ^~~~~~~~~~~~~~~~~
/home/cmorgan/projects/test/fmttest/main.cpp:40:20: note: declared private here
40 | enum class someinternalvalue {
| ^~~~~~~~~~~~~~~~~
But if you put the formatter inside of the class I get:
/home/cmorgan/projects/test/fmttest/main.cpp:55:10: error: explicit specialization in non-namespace scope ‘class example_namespace::someclass’
55 | template<>
| ^
/home/cmorgan/projects/test/fmttest/main.cpp:56:13: error: declaration of ‘struct fmt::v9::formatter<example_namespace::someclass::someinternalvalue>’ in ‘class example_namespace::someclass’ which does not enclose ‘fmt’
56 | struct fmt::formatter<example_namespace::someclass::someinternalvalue> : formatter<string_view> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/spdlog/fmt/fmt.h:27,
from /usr/include/spdlog/common.h:45,
from /usr/include/spdlog/spdlog.h:12,
from /home/cmorgan/projects/test/fmttest/main.cpp:1:
/usr/include/fmt/core.h: In instantiation of ‘constexpr fmt::v9::detail::value<Context> fmt::v9::detail::make_value(T&&) [with Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; T = example_namespace::someclass::someinternalvalue&]’:
/usr/include/fmt/core.h:1753:29: required from ‘constexpr fmt::v9::detail::value<Context> fmt::v9::detail::make_arg(T&&) [with bool IS_PACKED = true; Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; type <anonymous> = fmt::v9::detail::type::custom_type; T = example_namespace::someclass::someinternalvalue&; typename std::enable_if<IS_PACKED, int>::type <anonymous> = 0]’
/usr/include/fmt/core.h:1877:77: required from ‘constexpr fmt::v9::format_arg_store<Context, Args>::format_arg_store(T&& ...) [with T = {example_namespace::someclass::someinternalvalue&}; Context = fmt::v9::basic_format_context<fmt::v9::appender, char>; Args = {example_namespace::someclass::someinternalvalue}]’
/usr/include/fmt/core.h:1894:38: required from ‘constexpr fmt::v9::format_arg_store<Context, typename std::remove_cv<typename std::remove_reference<Args>::type>::type ...> fmt::v9::make_format_args(Args&& ...) [with Context = basic_format_context<appender, char>; Args = {example_namespace::someclass::someinternalvalue&}]’
/usr/include/spdlog/logger.h:370:68: required from ‘void spdlog::logger::log_(spdlog::source_loc, spdlog::level::level_enum, spdlog::string_view_t, Args&& ...) [with Args = {example_namespace::someclass::someinternalvalue&}; spdlog::string_view_t = fmt::v9::basic_string_view<char>]’
/usr/include/spdlog/logger.h:90:13: required from ‘void spdlog::logger::log(spdlog::source_loc, spdlog::level::level_enum, fmt::v9::format_string<T ...>, Args&& ...) [with Args = {example_namespace::someclass::someinternalvalue&}; fmt::v9::format_string<T ...> = fmt::v9::basic_format_string<char, example_namespace::someclass::someinternalvalue&>]’
/usr/include/spdlog/logger.h:96:12: required from ‘void spdlog::logger::log(spdlog::level::level_enum, fmt::v9::format_string<T ...>, Args&& ...) [with Args = {example_namespace::someclass::someinternalvalue&}; fmt::v9::format_string<T ...> = fmt::v9::basic_format_string<char, example_namespace::someclass::someinternalvalue&>]’
/usr/include/spdlog/logger.h:158:12: required from ‘void spdlog::logger::info(fmt::v9::format_string<T ...>, Args&& ...) [with Args = {example_namespace::someclass::someinternalvalue&}; fmt::v9::format_string<T ...> = fmt::v9::basic_format_string<char, example_namespace::someclass::someinternalvalue&>]’
/usr/include/spdlog/spdlog.h:157:31: required from ‘void spdlog::info(fmt::v9::format_string<T ...>, Args&& ...) [with Args = {example_namespace::someclass::someinternalvalue&}; fmt::v9::format_string<T ...> = fmt::v9::basic_format_string<char, example_namespace::someclass::someinternalvalue&>]’
/home/cmorgan/projects/test/fmttest/main.cpp:53:15: required from here
/usr/include/fmt/core.h:1733:7: 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
1733 | formattable,
| ^~~~~~~~~~~
What is the recommended approach here? I don't want to leak types out of classes by moving the enum classes both out of the classes and out of the namespaces.
Upvotes: 4
Views: 1285
Reputation: 55595
You need to declare the formatter
specialization a friend:
#include <fmt/format.h>
namespace example_namespace {
class someclass {
private:
enum class someinternalvalue {
these = 2,
are = 9,
examples = 10
};
friend struct fmt::formatter<someinternalvalue>;
// ...
};
}
template<>
struct fmt::formatter<example_namespace::someclass::someinternalvalue>
: formatter<string_view> {
// ...
};
https://godbolt.org/z/boqcPjfhz
Upvotes: 3