Skip to content

Commit c0cc897

Browse files
authored
Merge pull request #11 from jay-tux/feat/filestream
Feat/filestream
2 parents f5eb0ad + 50d9c0c commit c0cc897

File tree

6 files changed

+246
-2
lines changed

6 files changed

+246
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# fpgen
22
*Functional programming in C++ using C++20 coroutines*
3-
![](https://img.shields.io/badge/test_coverage-97%25-brightgreen)
3+
![](https://img.shields.io/badge/test_coverage-96.9%25-brightgreen)
44

55

66
## Aim

inc/aggregators.hpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "generator.hpp"
55
#include <forward_list>
6+
#include <ostream>
67
#include <tuple>
78
#include <type_traits>
89

@@ -200,6 +201,94 @@ template <typename T, typename Fun> void foreach (generator<T> gen, Fun func) {
200201
func(gen());
201202
}
202203
}
204+
205+
/**
206+
* \brief Sends each value to the stream.
207+
*
208+
* Calls `stream << value` for each value in the generator, then returns the
209+
* resulting (modified) stream.
210+
*
211+
* \tparam T The type of values in the stream.
212+
* \param[in,out] gen The generator supplying the values.
213+
* \param[in,out] stream The stream to output to.
214+
* \returns The resulting stream.
215+
*/
216+
template <typename T>
217+
std::ostream &to_stream(generator<T> gen, std::ostream &stream) {
218+
while (gen) {
219+
stream << gen();
220+
}
221+
return stream;
222+
}
223+
224+
/**
225+
* \brief Sends each value to the stream. Values are separated by the given
226+
* separator.
227+
*
228+
* Calls `stream << value` for each value in the generator, then returns the
229+
* resulting (modified) stream. Between each pair of values in the generator,
230+
* the separator is added.
231+
*
232+
* \tparam T The type of values in the stream.
233+
* \tparam T2 The type of the separator.
234+
* \param[in,out] gen The generator supplying the values.
235+
* \param[in,out] stream The stream to output to.
236+
* \param[in] separator The separator to use.
237+
* \returns The resulting stream.
238+
*/
239+
template <typename T, typename T2>
240+
std::ostream &to_stream(generator<T> gen, std::ostream &stream, T2 separator) {
241+
if (gen) {
242+
stream << gen();
243+
}
244+
while (gen) {
245+
stream << separator << gen();
246+
}
247+
return stream;
248+
}
249+
250+
/**
251+
* \brief Sends each value to the stream on a separate line.
252+
*
253+
* Calls `stream << value << std::endl` for each value in the generator, then
254+
* returns the resulting (modified) stream. To avoid a trailing newline, see
255+
* `fpgen::to_lines_no_trail`
256+
*
257+
* \tparam T The type of values in the stream.
258+
* \param[in,out] gen The generator supplying the values.
259+
* \param[in,out] stream The stream to output to.
260+
* \returns The resulting stream.
261+
*/
262+
template <typename T>
263+
std::ostream &to_lines(generator<T> gen, std::ostream &stream) {
264+
while (gen) {
265+
stream << gen() << std::endl;
266+
}
267+
return stream;
268+
}
269+
270+
/**
271+
* \brief Sends each value to the stream on a separate line, without trailing
272+
* newline.
273+
*
274+
* Calls `stream << std::endl << value` for each value in the generator (except
275+
* the first), then returns the resulting (modified) stream.
276+
*
277+
* \tparam T The type of values in the stream.
278+
* \param[in,out] gen The generator supplying the values.
279+
* \param[in,out] stream The stream to output to.
280+
* \returns The resulting stream.
281+
*/
282+
template <typename T>
283+
std::ostream &to_lines_no_trail(generator<T> gen, std::ostream &stream) {
284+
if (gen) {
285+
stream << gen();
286+
}
287+
while (gen) {
288+
stream << std::endl << gen();
289+
}
290+
return stream;
291+
}
203292
} // namespace fpgen
204293

205294
#endif

inc/sources.hpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#define _FPGEN_SOURCES
33

44
#include "generator.hpp"
5+
#include <istream>
56
#include <iterator>
7+
#include <type_traits>
68

79
/**
810
* \brief The namespace containing all of fpgen's code.
@@ -103,6 +105,49 @@ template <typename T> generator<T> inc(T start) {
103105
}
104106
}
105107

108+
/**
109+
* \brief Creates a generator from an input stream.
110+
*
111+
* The generator will apply the given function on the stream each time the
112+
* generator is invoked. The function should return a value of the type you want
113+
* in the generator. Once the stream fails (`!stream.good()`) or the stream
114+
* reaches EOF, the generator stops. Trailing whitespace may result in
115+
* unpredictable behaviour. Since the stream isn't copied, using the generator
116+
* after the stream goes out of scope is undefined behaviour.
117+
*
118+
* \tparam Fun The type of the function. Should have the signature
119+
* (std::istream &) -> T.
120+
* \param[in,out] stream The input stream.
121+
* \param[in] func The function to extract data from the stream.
122+
* \returns A new generator which will iterate over the given stream, each time
123+
* applying the given function to retrieve the next value.
124+
*/
125+
template <typename Fun>
126+
generator<typename std::invoke_result<Fun, std::istream &>::type>
127+
from_stream(std::istream &stream, Fun func) {
128+
while (stream.good() && !stream.eof()) {
129+
co_yield func(stream);
130+
}
131+
co_return;
132+
}
133+
134+
/**
135+
* \brief Creates a generator supplying each line from a file.
136+
*
137+
* This generator is formed by combining `fpgen::from_stream<>` with
138+
* `std::getline` on the stream.
139+
*
140+
* \param[in,out] stream The stream to extract lines from.
141+
* \returns A new generator which yields each line in the stream.
142+
*/
143+
inline generator<std::string> from_lines(std::istream &stream) {
144+
return from_stream(stream, [](std::istream &stream) {
145+
std::string value;
146+
std::getline(stream, value);
147+
return value;
148+
});
149+
}
150+
106151
} // namespace fpgen
107152

108153
#endif

test/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
SOURCES=$(shell find $(SRCD) -name '*.cpp')
22
DEPS=$(SOURCES:$(SRCD)/%.cpp=$(OBJD)/%.d)
3-
TESTS=generator sources manip aggreg stream chain
3+
TESTS=generator sources manip aggreg chain
44
TESTOBJ=$(TESTS:%=$(OBJD)/test_%.o)
55

66
CONAN_PKG_OVERRIDE=gtest

test/src/test_aggreg.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "gtest/gtest.h"
77

88
#include <map>
9+
#include <sstream>
910
#include <vector>
1011

1112
fpgen::generator<size_t> a_empty() { co_return; }
@@ -170,3 +171,40 @@ TEST(foreach, normal) {
170171
fpgen::foreach (gen, [&res](size_t val) { res += val; });
171172
EXPECT_EQ(res, fpgen::sum(gen2));
172173
}
174+
175+
TEST(stream, nosep) {
176+
std::vector<int> vals = {1, 2, 3, 4, 5, 6};
177+
auto gen = fpgen::from(vals);
178+
std::stringstream strm;
179+
fpgen::to_stream(gen, strm);
180+
EXPECT_EQ(strm.str(), "123456");
181+
}
182+
183+
TEST(stream, sep) {
184+
std::vector<int> vals = {1, 2, 3, 4, 5, 6, 7};
185+
auto gen = fpgen::from(vals);
186+
std::stringstream strm;
187+
fpgen::to_stream(gen, strm, " ");
188+
EXPECT_EQ(strm.str(), "1 2 3 4 5 6 7");
189+
}
190+
191+
TEST(stream, lines_trail) {
192+
std::vector<int> vals = {1, 2, 3, 4};
193+
auto gen = fpgen::from(vals);
194+
std::stringstream strm;
195+
std::stringstream expect;
196+
for (auto v : vals)
197+
expect << v << std::endl;
198+
fpgen::to_lines(gen, strm);
199+
EXPECT_EQ(strm.str(), expect.str());
200+
}
201+
202+
TEST(stream, lines_no_trail) {
203+
std::vector<int> vals = {1, 2, 3, 4};
204+
auto gen = fpgen::from(vals);
205+
std::stringstream strm;
206+
std::stringstream expect;
207+
expect << 1 << std::endl << 2 << std::endl << 3 << std::endl << 4;
208+
fpgen::to_lines_no_trail(gen, strm);
209+
EXPECT_EQ(strm.str(), expect.str());
210+
}

test/src/test_sources.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <map>
77
#include <set>
8+
#include <sstream>
89
#include <string>
910
#include <vector>
1011

@@ -79,3 +80,74 @@ TEST(sources, incrementable_struct) {
7980
EXPECT_EQ(gen().value, i);
8081
}
8182
}
83+
84+
TEST(sources, instream) {
85+
std::vector<int> numbers = {1, 2, 3, 4, 5};
86+
std::stringstream str;
87+
for (auto v : numbers)
88+
str << " " << v;
89+
auto func = [](std::istream &strm) {
90+
int i;
91+
strm >> i;
92+
return i;
93+
};
94+
95+
auto gen = fpgen::from_stream(str, func);
96+
for (int i = 0; i < 5; i++) {
97+
bool genstate = gen;
98+
EXPECT_TRUE(genstate);
99+
auto tmp = gen();
100+
EXPECT_EQ(tmp, numbers[i]);
101+
std::cout << i << "," << tmp << std::endl;
102+
}
103+
104+
bool genstate = gen;
105+
EXPECT_FALSE(genstate);
106+
}
107+
108+
TEST(sources, lipsum_lines) {
109+
std::string lipsum = R"(
110+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque diam magna,
111+
laoreet non dictum eget, scelerisque eu nibh. Cras luctus purus sit amet
112+
sodales aliquet. Proin vulputate risus quam. Curabitur ultricies, elit nec
113+
pharetra accumsan, leo eros mollis nibh, pulvinar lobortis dolor diam non quam.
114+
Vivamus odio arcu, aliquet ornare leo quis, mollis porta nisl. Mauris malesuada
115+
semper efficitur. Vestibulum nulla diam, hendrerit in diam a, tempor dignissim
116+
turpis. Maecenas eleifend laoreet velit id semper. Aliquam quis mattis enim.
117+
Cras gravida, felis vitae porta auctor, magna purus aliquet lorem, ut maximus
118+
tortor tortor sit amet mauris. Mauris eleifend enim eget arcu blandit auctor.
119+
Etiam vel porta augue. Maecenas volutpat odio in lacus sagittis fermentum.
120+
)";
121+
std::string lines[] = {"Lorem ipsum dolor sit amet, consectetur adipiscing "
122+
"elit. Quisque diam magna,",
123+
"laoreet non dictum eget, scelerisque eu nibh. Cras "
124+
"luctus purus sit amet",
125+
"sodales aliquet. Proin vulputate risus quam. "
126+
"Curabitur ultricies, elit nec",
127+
"pharetra accumsan, leo eros mollis nibh, pulvinar "
128+
"lobortis dolor diam non quam.",
129+
"Vivamus odio arcu, aliquet ornare leo quis, mollis "
130+
"porta nisl. Mauris malesuada",
131+
"semper efficitur. Vestibulum nulla diam, hendrerit "
132+
"in diam a, tempor dignissim",
133+
"turpis. Maecenas eleifend laoreet velit id semper. "
134+
"Aliquam quis mattis enim.",
135+
"Cras gravida, felis vitae porta auctor, magna purus "
136+
"aliquet lorem, ut maximus",
137+
"tortor tortor sit amet mauris. Mauris eleifend enim "
138+
"eget arcu blandit auctor.",
139+
"Etiam vel porta augue. Maecenas volutpat odio in "
140+
"lacus sagittis fermentum."};
141+
std::stringstream strm;
142+
strm << lipsum;
143+
auto gen = fpgen::from_lines(strm);
144+
EXPECT_EQ("", gen());
145+
for (size_t i = 0; i < 10; i++) {
146+
bool gens = gen;
147+
EXPECT_TRUE(gens);
148+
EXPECT_EQ(gen(), lines[i]);
149+
}
150+
EXPECT_EQ(gen(), "");
151+
bool gens = gen;
152+
EXPECT_FALSE(gens);
153+
}

0 commit comments

Comments
 (0)