Search code examples
c++fmt

I'm trying to format a template using {fmt}


I have a simple template somewhat like:

template <typename T, T Min, T Max>
class LimitedInt {
public:
   static_assert(Min < Max, "Min must be less than Max");
   explicit LimitedInt(const T value)
   {
      setValue(value);
   }
   void setValue(const T value)
   {
      if (value < Min || value > Max) {
         throw std::invalid_argument("invalid value");
      }
      mValue = value;
   }
   T getValue() const
   {
      return mValue;
   }
private:
   T mValue{Min};
}

Which allows me to specialize it as:

using Vlan = LimitedInt<uint16_t, 0, 4094>;

I'd like to be able to format the value with something like

Vlan v{42};
fmt::format("{:04x}", v);

To this end I tried to forward the formatting duties to formatter<int> as described here but got nowhere. My attempt looks like:

namespace fmt {
template <>
struct formatter<LimitedInt> {
   formatter<int> int_formatter;
   template <typename ParseContext>
   constexpr auto parse(ParseContext& ctx)
   {
      return int_formatter.parse(ctx);
   }
   template <typename FormatContext>
   auto format(const LimtedInt& li, FormatContext& ctx)
   {
      return int_formatter.format(li.getValue(), ctx);
   }
};
}  // namespace fmt

I've tried several variations on this with no success, the errors tend to center around this:

In file included from /output/build/proj-local/src/networkinterface.h:8:0,
                 from /output/build/proj-local/src/networkinterface.cpp:3:
/output/build/proj-local/src/limitedints.h:78:38: error: type/value mismatch at argument 1 in template parameter list for 'template<class T, class Char, class Enable> struct fmt::v7::formatter'
 struct formatter<LimitedInt> {
                                      ^
/output/build/proj-local/src/limitedints.h:78:38: note:   expected a type, got 'LimitedInt'

My current workaround is to have a full-blown formatter with it's own parse() and format() method but for me to re-invent the wheel Victor already wrote seems silly at best.


Solution

  • The usual rules of specialization apply. Specifically, you should make formatter a template and pass the template parameters to LimitedInt:

    template <typename T, T Min, T Max>
    struct fmt::formatter<LimitedInt<T, Min, Max>> {
      constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
    
      auto format(const LimitedInt<T, Min, Max>& val, format_context& ctx) const {
        // Format val and write the output to ctx.out().
        return ctx.out();
      }
    };
    

    Godbolt: https://godbolt.org/z/oEMfqEqY9