Created
December 22, 2021 06:00
-
-
Save Gnomorian/5b8de689d23c8fc1d0067faea34d105b to your computer and use it in GitHub Desktop.
Custom Formatter for non-copyable references
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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