Skip to content

Commit 83085b6

Browse files
committed
Add eckit::format_to
1 parent f4a90c7 commit 83085b6

File tree

4 files changed

+80
-1
lines changed

4 files changed

+80
-1
lines changed

src/eckit/format/Format.h

+27
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#include <fmt/ranges.h>
1616
#include <fmt/std.h>
1717

18+
#include <type_traits>
19+
#include <iterator>
20+
1821
#define ENABLE_FORMAT(typ) \
1922
template <> \
2023
struct fmt::formatter<typ> : fmt::ostream_formatter {}
@@ -49,4 +52,28 @@ std::string format(StringType&& formatString, Args&&... args) {
4952
}
5053

5154

55+
/// Format a string with compile time checks to an output iterator
56+
/// @param outputIt output iterator to write to
57+
/// @param formatString to use, see: <https://fmt.dev/11.1/syntax/> for description of syntax.
58+
/// Must be known at compiletime
59+
/// @param ... args to be upplied into formatString
60+
/// @throws fmt::format_error
61+
template <typename OutputIt, typename StringType, typename... Args, std::enable_if_t<!std::is_base_of_v<std::ostream, std::decay_t<OutputIt>>, bool> = true>
62+
void format_to(OutputIt&& outputIt, StringType&& formatString, Args&&... args) {
63+
fmt::format_to(std::forward<OutputIt>(outputIt), fmt::runtime(std::forward<StringType>(formatString)), std::forward<Args>(args)...);
64+
}
65+
66+
/// Format a string with compile time checks to an ostream
67+
/// @param os ostream to write to
68+
/// @param formatString to use, see: <https://fmt.dev/11.1/syntax/> for description of syntax.
69+
/// Must be known at compiletime
70+
/// @param ... args to be upplied into formatString
71+
/// @throws fmt::format_error
72+
template <typename Ostream, typename StringType, typename... Args, std::enable_if_t<std::is_base_of_v<std::ostream, std::decay_t<Ostream>>, bool> = true>
73+
void format_to(Ostream&& os, StringType&& formatString, Args&&... args) {
74+
// Have to either use `const char&` or `const wchar_t&` here
75+
fmt::format_to(std::ostream_iterator<const char&>(std::forward<Ostream>(os)), fmt::runtime(std::forward<StringType>(formatString)), std::forward<Args>(args)...);
76+
}
77+
78+
5279
} // namespace eckit

tests/format/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ ecbuild_add_test( TARGET eckit_test_format
22
SOURCES test_format.cc
33
LIBS eckit )
44

5+
ecbuild_add_test( TARGET eckit_test_format_to
6+
SOURCES test_format_to.cc
7+
LIBS eckit )
8+

tests/format/test_format.cc

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ CASE("ENABLE_FORMAT CodeLocation") {
5555
std::cout << eckit::format("{}", Here());
5656
}
5757

58-
5958
int main(int argc, char** argv) {
6059
return eckit::testing::run_tests(argc, argv);
6160
}

tests/format/test_format_to.cc

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* (C) Copyright 2025- ECMWF.
3+
*
4+
* This software is licensed under the terms of the Apache Licence Version 2.0
5+
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6+
* In applying this licence, ECMWF does not waive the privileges and immunities
7+
* granted to it by virtue of its status as an intergovernmental organisation nor
8+
* does it submit to any jurisdiction.
9+
*/
10+
#include <eckit/testing/Test.h>
11+
12+
#include "eckit/format/Format.h"
13+
#include "eckit/log/Log.h"
14+
#include "eckit/log/OStreamTarget.h"
15+
#include "eckit/log/Channel.h"
16+
17+
#include <sstream>
18+
19+
CASE("Format to output iterator") {
20+
{
21+
auto output = fmt::memory_buffer();
22+
eckit::format_to(std::back_inserter(output), "Hello {} {} {}", "through", "a", "stream");
23+
EXPECT_EQUAL(std::string(output.data(), output.size()), std::string("Hello through a stream"));
24+
}
25+
{
26+
std::vector<char> output;
27+
eckit::format_to(std::back_inserter(output), "Hello {} {} {}", "through", "a", "stream");
28+
EXPECT_EQUAL(std::string(output.data(), output.size()), std::string("Hello through a stream"));
29+
}
30+
}
31+
32+
CASE("Format to streams") {
33+
std::ostringstream oss;
34+
eckit::format_to(oss, "Hello {} {} {}", "through", "a", "stream");
35+
EXPECT_EQUAL(oss.str(), std::string("Hello through a stream"));
36+
}
37+
38+
CASE("Format to eckit channel") {
39+
std::ostringstream oss;
40+
eckit::Channel ch{new eckit::OStreamTarget{oss}};
41+
eckit::format_to(ch, "Hello {} {} {}", "through", "a", "stream");
42+
ch.flush();
43+
EXPECT_EQUAL(oss.str(), std::string("Hello through a stream"));
44+
}
45+
46+
47+
int main(int argc, char** argv) {
48+
return eckit::testing::run_tests(argc, argv);
49+
}

0 commit comments

Comments
 (0)