Skip to content

Instantly share code, notes, and snippets.

@Gnomorian
Created December 22, 2021 06:00
Show Gist options
  • Save Gnomorian/5b8de689d23c8fc1d0067faea34d105b to your computer and use it in GitHub Desktop.
Save Gnomorian/5b8de689d23c8fc1d0067faea34d105b to your computer and use it in GitHub Desktop.
Custom Formatter for non-copyable references
std::format is a nice simple way to do string interpolation in c++
std::format("hello, {}!", "World); would print "`hello, World!`" for example.
I wanted to learn to create my own formatters so i could provide custom objects and have them printed nicely by just providing
it. There is not currently much documentation on the subject, but i managed to make it happen however one problem is the
std::formatter implementations will only take values, not references which means either you pass std::format a pointer to your object or
you allow it to take copies of your object.
## What if you dont want std::format to copy your object
Either you can define a formatter for a pointer of your object, or you can copy the code in this gist.
- The object you want formatted needs to subclass FormattableReference and implement FormattableReference::format(). Remember c++ has multiple
inheritence, so any object can implement this.
- When you call std::format, you need to pass ReferenceFormatter as the argument so this can be copied instead of your object, similar to
a unique_ptr.
#include <iostream>
#include <format>
#include "formattable.h"
class Thing : public FormattableReference
{
public:
Thing() { std::cout << "Created" << std::endl; }
Thing(Thing& copyFrom) : s{ copyFrom.s } {std::cout << "Copied" << std::endl; }
Thing(Thing&& moveFrom) : s { std::move(moveFrom.s) } {std::cout << "Movied" << std::endl; }
~Thing() { std::cout << "Deleted" << std::endl; }
std::string s{ "some text" };
// Inherited via Formattable
virtual std::string format() const override
{
std::string buffer{ "Thing{" };
buffer += s;
buffer += "}";
return buffer;
}
};
int wmain(int argc, wchar_t* args[])
{
Thing t;
std::cout << std::format("{}", ReferenceFormatter{ t }) << std::endl;
}
#pragma once
#include <string>
#include <format>
struct FormattableReference
{
virtual std::string format() const = 0;
};
struct ReferenceFormatter
{
const FormattableReference& formattable;
constexpr ReferenceFormatter(decltype(formattable) formattable)
: formattable{formattable}
{}
constexpr std::string format() const
{
return formattable.format();
}
};
template<class CharT>
struct std::formatter<ReferenceFormatter, CharT> : std::formatter<std::string, CharT>
{
// Define format() by calling the base class implementation with the wrapped value
template<class FormatContext>
auto format(ReferenceFormatter fmt, FormatContext& fc)
{
std::formatter<std::string, CharT>::format(fmt.format(), fc);
return fc.out();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment