Skip to content

Commit 3fe45ea

Browse files
committed
Add formatters for std::format (C++20)
1 parent 17f8c7d commit 3fe45ea

File tree

13 files changed

+329
-28
lines changed

13 files changed

+329
-28
lines changed

src/ffmpeg.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,76 @@ struct PixSampleFmtWrapper
177177
protected:
178178
T m_fmt = NoneValue;
179179
};
180+
181+
182+
#ifdef __cpp_lib_format
183+
# include <format>
184+
185+
namespace av {
186+
187+
template <typename B>
188+
concept has_name_method_with_ec = requires(const B& type, std::error_code ec) {
189+
{ type.name(ec) } -> std::convertible_to<std::string_view>;
190+
};
191+
192+
template <typename B>
193+
concept has_name_method_without_ec = requires(const B& type) {
194+
{ type.name() } -> std::convertible_to<std::string_view>;
195+
};
196+
197+
template <typename B>
198+
concept has_long_name_method_with_ec = requires(const B& type, std::error_code ec) {
199+
{ type.longName(ec) } -> std::convertible_to<std::string_view>;
200+
};
201+
202+
template <typename B>
203+
concept has_long_name_method_without_ec = requires(const B& type) {
204+
{ type.longName() } -> std::convertible_to<std::string_view>;
205+
};
206+
} // ::av
207+
208+
template <class T, class CharT>
209+
requires av::has_name_method_with_ec<T> || av::has_name_method_without_ec<T>
210+
struct std::formatter<T, CharT>
211+
{
212+
bool longName = false;
213+
214+
template<typename ParseContext>
215+
constexpr ParseContext::iterator parse(ParseContext& ctx)
216+
{
217+
auto it = ctx.begin();
218+
if constexpr (requires { requires av::has_long_name_method_with_ec<T> || av::has_long_name_method_without_ec<T>; }) {
219+
if (it == ctx.end())
220+
return it;
221+
if (*it == 'l') {
222+
longName = true;
223+
++it;
224+
}
225+
if (it != ctx.end() && *it != '}')
226+
throw std::format_error("Invalid format args");
227+
}
228+
return it;
229+
}
230+
231+
template<typename ParseContext>
232+
auto format(const T& value, ParseContext& ctx) const
233+
{
234+
if (longName) {
235+
if constexpr (requires { requires av::has_long_name_method_with_ec<T>; }) {
236+
std::error_code dummy;
237+
return std::format_to(ctx.out(), "{}", value.longName(dummy));
238+
} else if constexpr (requires { requires av::has_long_name_method_without_ec<T>; }) {
239+
return std::format_to(ctx.out(), "{}", value.longName());
240+
}
241+
} else {
242+
if constexpr (requires { requires av::has_name_method_with_ec<T>; }) {
243+
std::error_code dummy;
244+
return std::format_to(ctx.out(), "{}", value.name(dummy));
245+
} else {
246+
return std::format_to(ctx.out(), "{}", value.name());
247+
}
248+
}
249+
return ctx.out();
250+
}
251+
};
252+
#endif

src/format.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,3 @@ OutputFormat guessOutputFormat(const std::string& name,
109109
const std::string& mime = std::string());
110110

111111
} // namespace av
112-

src/rational.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,23 @@ inline std::istream& operator>> (std::istream &stream, Rational &value)
145145

146146

147147
} // ::av
148+
149+
150+
#ifdef __cpp_lib_format
151+
#include <format>
152+
// std::format
153+
template <typename CharT>
154+
struct std::formatter<av::Rational, CharT>
155+
{
156+
template<typename ParseContext>
157+
constexpr auto parse(ParseContext& ctx)
158+
{
159+
return ctx.begin();
160+
}
161+
template<typename ParseContext>
162+
auto format(const av::Rational& value, ParseContext& ctx) const
163+
{
164+
return std::format_to(ctx.out(), "{}/{}", value.getNumerator(), value.getDenominator());
165+
}
166+
};
167+
#endif

src/rect.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@
22

33
namespace av {
44

5-
Rect::Rect()
6-
: x(0), y(0), width(0), height(0)
5+
Rect::Rect(int width, int height) noexcept
6+
: width(width),
7+
height(height)
78
{
89
}
910

10-
Rect::Rect(int width, int height)
11-
: x(0), y(0), width(width), height(height)
12-
{
13-
}
14-
15-
Rect::Rect(int x, int y, int width, int height)
16-
: x(x), y(y), width(width), height(height)
11+
Rect::Rect(int x, int y, int width, int height) noexcept
12+
: x(x),
13+
y(y),
14+
width(width),
15+
height(height)
1716
{
1817
}
1918

src/rect.h

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,46 @@ namespace av {
55
class Rect
66
{
77
public:
8-
Rect();
9-
Rect(int width, int height);
10-
Rect(int x, int y, int width, int height);
8+
Rect() noexcept = default;
9+
Rect(int width, int height) noexcept;
10+
Rect(int x, int y, int width, int height) noexcept;
1111

12-
void setX(int x) { this->x = x; }
13-
void setY(int y) { this->y = y; }
14-
void setWidth(int w) { width = w; }
15-
void setHeight(int h) { height = h; }
12+
void setX(int x) noexcept { this->x = x; }
13+
void setY(int y) noexcept { this->y = y; }
14+
void setWidth(int w) noexcept { width = w; }
15+
void setHeight(int h) noexcept { height = h; }
1616

17-
int getX() { return x; }
18-
int getY() { return y; }
19-
int getWidth() { return width; }
20-
int getHeight() { return height; }
17+
int getX() const noexcept { return x; }
18+
int getY() const noexcept { return y; }
19+
int getWidth() const noexcept { return width; }
20+
int getHeight() const noexcept { return height; }
2121

2222
private:
23-
int x;
24-
int y;
25-
int width;
26-
int height;
23+
int x{};
24+
int y{};
25+
int width{};
26+
int height{};
2727
};
2828

2929
} // ::av
3030

31+
#if __has_include(<format>)
32+
#include <format>
33+
#ifdef __cpp_lib_format
34+
// std::format
35+
template <typename CharT>
36+
struct std::formatter<av::Rect, CharT>
37+
{
38+
template<typename ParseContext>
39+
constexpr auto parse(ParseContext& ctx)
40+
{
41+
return ctx.begin();
42+
}
43+
template<typename ParseContext>
44+
auto format(const av::Rect& value, ParseContext& ctx) const
45+
{
46+
return std::format_to(ctx.out(), "{}x{}{:+}{:+}", value.getWidth(), value.getHeight(), value.getX(), value.getY());
47+
}
48+
};
49+
#endif // __cpp_lib_format
50+
#endif // __has_include

src/timestamp.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,27 @@ std::ostream& operator<<(std::ostream &ost, const Timestamp &ts)
142142
}
143143

144144
} // ::av
145+
146+
#ifdef __cpp_lib_format
147+
#include <format>
148+
149+
// std::format
150+
template <typename CharT>
151+
struct std::formatter<av::Timestamp, CharT>
152+
{
153+
template<typename ParseContext>
154+
constexpr auto parse(ParseContext& ctx)
155+
{
156+
return ctx.begin();
157+
}
158+
159+
template<typename ParseContext>
160+
auto format(const av::Timestamp& ts, ParseContext& ctx) const
161+
{
162+
if (ts.isNoPts()) {
163+
return std::format_to(ctx.out(), "NO_PTS");
164+
}
165+
return std::format_to(ctx.out(), "{}*{}", ts.timestamp(), ts.timebase());
166+
}
167+
};
168+
#endif

tests/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ add_executable(test_executor
1515
Packet.cpp
1616
Format.cpp
1717
Rational.cpp
18-
Timestamp.cpp)
18+
Timestamp.cpp
19+
Codec.cpp
20+
PixelSampleFormat.cpp
21+
Common.cpp)
1922
target_link_libraries(test_executor PUBLIC Catch2::Catch2 test_main avcpp::avcpp)
2023

2124
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../catch2/contrib")

tests/Codec.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include <catch2/catch.hpp>
2+
3+
#include <vector>
4+
5+
#ifdef __cpp_lib_print
6+
# include <format>
7+
#endif
8+
9+
#include "codec.h"
10+
#include "ffmpeg.h"
11+
12+
#ifdef _MSC_VER
13+
# pragma warning(disable : 4702) // Disable warning: unreachable code
14+
#endif
15+
16+
TEST_CASE("Codec", "[Codec]")
17+
{
18+
#ifdef __cpp_lib_print
19+
SECTION("std::format formatter")
20+
{
21+
av::Codec codec = av::findDecodingCodec("h264");
22+
auto str = std::format("{}", codec);
23+
CHECK(str == "h264");
24+
25+
str = std::format("{:l}", codec);
26+
CHECK(str == "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10");
27+
}
28+
#endif
29+
}

tests/Common.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <catch2/catch.hpp>
2+
3+
#include <vector>
4+
5+
#ifdef __cpp_lib_print
6+
# include <format>
7+
#endif
8+
9+
#include <rect.h>
10+
11+
#ifdef _MSC_VER
12+
# pragma warning(disable : 4702) // Disable warning: unreachable code
13+
#endif
14+
15+
TEST_CASE("Common tools and helpers", "[Common]")
16+
{
17+
#ifdef __cpp_lib_print
18+
SECTION("std::format formatter for Rect")
19+
{
20+
av::Rect rect {50, 50, 640, 480};
21+
auto str = std::format("{}", rect);
22+
CHECK(str == "640x480+50+50");
23+
24+
CHECK(std::format("{}", av::Rect(-50, -50, 640, 480)) == "640x480-50-50");
25+
}
26+
#endif
27+
}

tests/Format.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22

33
#include <vector>
44

5+
#ifdef __cpp_lib_print
6+
# include <format>
7+
#endif
8+
59
#include "format.h"
610
#include "codec.h"
11+
#include "ffmpeg.h"
712

813
#ifdef _MSC_VER
914
# pragma warning (disable : 4702) // Disable warning: unreachable code
1015
#endif
1116

12-
1317
TEST_CASE("Format Core functionality", "[Format]")
1418
{
1519
SECTION("guessEncodingCodec()") {
@@ -29,4 +33,28 @@ TEST_CASE("Format Core functionality", "[Format]")
2933

3034
CHECK(tmp_format.raw() == of.raw());
3135
}
36+
37+
#ifdef __cpp_lib_print
38+
SECTION("std::format formatter")
39+
{
40+
{
41+
auto const format = av::guessOutputFormat("matroska");
42+
auto str = std::format("{}", format);
43+
CHECK(str == "matroska");
44+
45+
str = std::format("{:l}", format);
46+
CHECK(str == "Matroska");
47+
}
48+
49+
{
50+
av::InputFormat format {"matroska"};
51+
auto str = std::format("{}", format);
52+
CHECK(str == "matroska,webm");
53+
54+
str = std::format("{:l}", format);
55+
CHECK(str == "Matroska / WebM");
56+
}
57+
}
58+
#endif
59+
3260
}

0 commit comments

Comments
 (0)