-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for 'std::variant' in C++17.
For C++17, if all the alternatives of a variant are formattable the variant is now also formattable. In addition 'std::monostate' is now formattable. The value of a variant is enclosed in '<' and '>', and the monostate is formatted as ' '.
- Loading branch information
Showing
5 changed files
with
184 additions
and
3 deletions.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Formatting library for C++ - experimental range support | ||
// | ||
// {fmt} support for variant interface. | ||
|
||
#ifndef FMT_VARIANT_H_ | ||
#define FMT_VARIANT_H_ | ||
|
||
#include <type_traits> | ||
#include <variant> | ||
|
||
#include "format.h" | ||
#include "ranges.h" | ||
|
||
FMT_BEGIN_NAMESPACE | ||
|
||
#define FMT_HAS_VARIANT FMT_CPLUSPLUS >= 201703L | ||
|
||
#if FMT_HAS_VARIANT | ||
|
||
template <typename Char> struct formatter<std::monostate, Char> { | ||
template <typename ParseContext> | ||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | ||
return ctx.begin(); | ||
} | ||
|
||
template <typename FormatContext = format_context> | ||
auto format(const std::monostate&, FormatContext& ctx) const | ||
-> decltype(ctx.out()) { | ||
auto out = ctx.out(); | ||
*out++ = ' '; | ||
return out; | ||
} | ||
}; | ||
|
||
namespace detail { | ||
|
||
template <typename T> | ||
using variant_index_sequence = make_index_sequence<std::variant_size<T>::value>; | ||
|
||
// variant_size and variant_alternative check. | ||
template <typename T> class is_variant_like_ { | ||
template <typename U> | ||
static auto check(U* p) -> decltype(std::variant_size<U>::value, int()); | ||
template <typename> static void check(...); | ||
|
||
public: | ||
static constexpr const bool value = | ||
!std::is_void<decltype(check<T>(nullptr))>::value; | ||
}; | ||
|
||
// formattable element check | ||
template <typename T, typename C, bool = is_variant_like_<T>::value> | ||
class is_variant_formattable_ { | ||
public: | ||
static constexpr const bool value = false; | ||
}; | ||
template <typename T, typename C> class is_variant_formattable_<T, C, true> { | ||
template <std::size_t... I> | ||
static std::integral_constant< | ||
bool, | ||
(fmt::is_formattable<std::variant_alternative_t<I, T>, C>::value && ...)> | ||
check(index_sequence<I...>); | ||
|
||
public: | ||
static constexpr const bool value = | ||
decltype(check(variant_index_sequence<T>{}))::value; | ||
}; | ||
|
||
} // namespace detail | ||
|
||
template <typename T> struct is_variant_like { | ||
static constexpr const bool value = detail::is_variant_like_<T>::value; | ||
}; | ||
|
||
template <typename T, typename C> struct is_variant_formattable { | ||
static constexpr const bool value = | ||
detail::is_variant_formattable_<T, C>::value; | ||
}; | ||
|
||
template <typename VariantT, typename Char> | ||
struct formatter< | ||
VariantT, Char, | ||
enable_if_t<fmt::is_variant_like<VariantT>::value && | ||
fmt::is_variant_formattable<VariantT, Char>::value>> { | ||
template <typename ParseContext> | ||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | ||
return ctx.begin(); | ||
} | ||
|
||
template <typename FormatContext = format_context> | ||
auto format(const VariantT& value, FormatContext& ctx) const | ||
-> decltype(ctx.out()) { | ||
auto out = ctx.out(); | ||
*out++ = '<'; | ||
std::visit( | ||
[&](const auto& v) { out = detail::write_range_entry<Char>(out, v); }, | ||
value); | ||
*out++ = '>'; | ||
return out; | ||
} | ||
}; | ||
|
||
#endif // FMT_HAS_VARIANT | ||
|
||
FMT_END_NAMESPACE | ||
|
||
#endif // FMT_VARIANT_H_ |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Formatting library for C++ - experimental variant API | ||
// | ||
// {fmt} support for variant interface. | ||
|
||
#include "fmt/variant.h" | ||
|
||
#include <string> | ||
|
||
#include "gtest/gtest.h" | ||
|
||
#if FMT_HAS_VARIANT | ||
|
||
TEST(variant_test, format_monostate) { | ||
EXPECT_EQ(fmt::format("{}", std::monostate{}), " "); | ||
} | ||
TEST(variant_test, format_variant) { | ||
using V0 = std::variant<int, float, std::string, char>; | ||
V0 v0(42); | ||
V0 v1(1.5f); | ||
V0 v2("hello"); | ||
V0 v3('i'); | ||
EXPECT_EQ(fmt::format("{}", v0), "<42>"); | ||
EXPECT_EQ(fmt::format("{}", v1), "<1.5>"); | ||
EXPECT_EQ(fmt::format("{}", v2), "<\"hello\">"); | ||
EXPECT_EQ(fmt::format("{}", v3), "<'i'>"); | ||
|
||
enum class noformatenum{b}; | ||
struct noformatstruct{}; | ||
EXPECT_FALSE((fmt::is_formattable<noformatenum>::value)); | ||
EXPECT_FALSE((fmt::is_formattable<noformatstruct>::value)); | ||
EXPECT_FALSE((fmt::is_formattable<std::variant<noformatenum>>::value)); | ||
EXPECT_FALSE((fmt::is_formattable<std::variant<noformatstruct>>::value)); | ||
EXPECT_FALSE((fmt::is_formattable<std::variant<noformatstruct,int>>::value)); | ||
EXPECT_FALSE((fmt::is_formattable<std::variant<int,noformatenum>>::value)); | ||
EXPECT_FALSE((fmt::is_formattable<std::variant<noformatstruct,noformatenum>>::value)); | ||
EXPECT_TRUE((fmt::is_formattable<std::variant<int,float>>::value)); | ||
|
||
using V1 = std::variant<std::monostate, std::string, std::string>; | ||
V1 v4{}; | ||
V1 v5{std::in_place_index<1>,"yes, this is variant"}; | ||
|
||
EXPECT_EQ(fmt::format("{}", v4), "< >"); | ||
EXPECT_EQ(fmt::format("{}", v5), "<\"yes, this is variant\">"); | ||
} | ||
|
||
#endif // FMT_HAS_VARIANT |