Skip to content

Commit

Permalink
Merge branch 'root' of github.com:jay-tux/fpgen into root
Browse files Browse the repository at this point in the history
  • Loading branch information
jay-tux committed Feb 22, 2022
2 parents f693a6b + c0cc897 commit 3b07e7f
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 32 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# fpgen
*Functional programming in C++ using C++20 coroutines*
![](https://img.shields.io/badge/test_coverage-97%25-brightgreen)
![](https://img.shields.io/badge/test_coverage-96.9%25-brightgreen)


## Aim
Expand Down
89 changes: 89 additions & 0 deletions inc/aggregators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "generator.hpp"
#include <forward_list>
#include <ostream>
#include <tuple>
#include <type_traits>

Expand Down Expand Up @@ -200,6 +201,94 @@ template <typename T, typename Fun> void foreach (generator<T> gen, Fun func) {
func(gen());
}
}

/**
* \brief Sends each value to the stream.
*
* Calls `stream << value` for each value in the generator, then returns the
* resulting (modified) stream.
*
* \tparam T The type of values in the stream.
* \param[in,out] gen The generator supplying the values.
* \param[in,out] stream The stream to output to.
* \returns The resulting stream.
*/
template <typename T>
std::ostream &to_stream(generator<T> gen, std::ostream &stream) {
while (gen) {
stream << gen();
}
return stream;
}

/**
* \brief Sends each value to the stream. Values are separated by the given
* separator.
*
* Calls `stream << value` for each value in the generator, then returns the
* resulting (modified) stream. Between each pair of values in the generator,
* the separator is added.
*
* \tparam T The type of values in the stream.
* \tparam T2 The type of the separator.
* \param[in,out] gen The generator supplying the values.
* \param[in,out] stream The stream to output to.
* \param[in] separator The separator to use.
* \returns The resulting stream.
*/
template <typename T, typename T2>
std::ostream &to_stream(generator<T> gen, std::ostream &stream, T2 separator) {
if (gen) {
stream << gen();
}
while (gen) {
stream << separator << gen();
}
return stream;
}

/**
* \brief Sends each value to the stream on a separate line.
*
* Calls `stream << value << std::endl` for each value in the generator, then
* returns the resulting (modified) stream. To avoid a trailing newline, see
* `fpgen::to_lines_no_trail`
*
* \tparam T The type of values in the stream.
* \param[in,out] gen The generator supplying the values.
* \param[in,out] stream The stream to output to.
* \returns The resulting stream.
*/
template <typename T>
std::ostream &to_lines(generator<T> gen, std::ostream &stream) {
while (gen) {
stream << gen() << std::endl;
}
return stream;
}

/**
* \brief Sends each value to the stream on a separate line, without trailing
* newline.
*
* Calls `stream << std::endl << value` for each value in the generator (except
* the first), then returns the resulting (modified) stream.
*
* \tparam T The type of values in the stream.
* \param[in,out] gen The generator supplying the values.
* \param[in,out] stream The stream to output to.
* \returns The resulting stream.
*/
template <typename T>
std::ostream &to_lines_no_trail(generator<T> gen, std::ostream &stream) {
if (gen) {
stream << gen();
}
while (gen) {
stream << std::endl << gen();
}
return stream;
}
} // namespace fpgen

#endif
45 changes: 45 additions & 0 deletions inc/sources.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#define _FPGEN_SOURCES

#include "generator.hpp"
#include <istream>
#include <iterator>
#include <type_traits>

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

/**
* \brief Creates a generator from an input stream.
*
* The generator will apply the given function on the stream each time the
* generator is invoked. The function should return a value of the type you want
* in the generator. Once the stream fails (`!stream.good()`) or the stream
* reaches EOF, the generator stops. Trailing whitespace may result in
* unpredictable behaviour. Since the stream isn't copied, using the generator
* after the stream goes out of scope is undefined behaviour.
*
* \tparam Fun The type of the function. Should have the signature
* (std::istream &) -> T.
* \param[in,out] stream The input stream.
* \param[in] func The function to extract data from the stream.
* \returns A new generator which will iterate over the given stream, each time
* applying the given function to retrieve the next value.
*/
template <typename Fun>
generator<typename std::invoke_result<Fun, std::istream &>::type>
from_stream(std::istream &stream, Fun func) {
while (stream.good() && !stream.eof()) {
co_yield func(stream);
}
co_return;
}

/**
* \brief Creates a generator supplying each line from a file.
*
* This generator is formed by combining `fpgen::from_stream<>` with
* `std::getline` on the stream.
*
* \param[in,out] stream The stream to extract lines from.
* \returns A new generator which yields each line in the stream.
*/
inline generator<std::string> from_lines(std::istream &stream) {
return from_stream(stream, [](std::istream &stream) {
std::string value;
std::getline(stream, value);
return value;
});
}

} // namespace fpgen

#endif
2 changes: 1 addition & 1 deletion test/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
SOURCES=$(shell find $(SRCD) -name '*.cpp')
DEPS=$(SOURCES:$(SRCD)/%.cpp=$(OBJD)/%.d)
TESTS=generator sources manip aggreg stream chain
TESTS=generator sources manip aggreg chain
TESTOBJ=$(TESTS:%=$(OBJD)/test_%.o)

CONAN_PKG_OVERRIDE=gtest
Expand Down
38 changes: 38 additions & 0 deletions test/src/test_aggreg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "gtest/gtest.h"

#include <map>
#include <sstream>
#include <vector>

fpgen::generator<size_t> a_empty() { co_return; }
Expand Down Expand Up @@ -170,3 +171,40 @@ TEST(foreach, normal) {
fpgen::foreach (gen, [&res](size_t val) { res += val; });
EXPECT_EQ(res, fpgen::sum(gen2));
}

TEST(stream, nosep) {
std::vector<int> vals = {1, 2, 3, 4, 5, 6};
auto gen = fpgen::from(vals);
std::stringstream strm;
fpgen::to_stream(gen, strm);
EXPECT_EQ(strm.str(), "123456");
}

TEST(stream, sep) {
std::vector<int> vals = {1, 2, 3, 4, 5, 6, 7};
auto gen = fpgen::from(vals);
std::stringstream strm;
fpgen::to_stream(gen, strm, " ");
EXPECT_EQ(strm.str(), "1 2 3 4 5 6 7");
}

TEST(stream, lines_trail) {
std::vector<int> vals = {1, 2, 3, 4};
auto gen = fpgen::from(vals);
std::stringstream strm;
std::stringstream expect;
for (auto v : vals)
expect << v << std::endl;
fpgen::to_lines(gen, strm);
EXPECT_EQ(strm.str(), expect.str());
}

TEST(stream, lines_no_trail) {
std::vector<int> vals = {1, 2, 3, 4};
auto gen = fpgen::from(vals);
std::stringstream strm;
std::stringstream expect;
expect << 1 << std::endl << 2 << std::endl << 3 << std::endl << 4;
fpgen::to_lines_no_trail(gen, strm);
EXPECT_EQ(strm.str(), expect.str());
}
72 changes: 72 additions & 0 deletions test/src/test_sources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>

Expand Down Expand Up @@ -79,3 +80,74 @@ TEST(sources, incrementable_struct) {
EXPECT_EQ(gen().value, i);
}
}

TEST(sources, instream) {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::stringstream str;
for (auto v : numbers)
str << " " << v;
auto func = [](std::istream &strm) {
int i;
strm >> i;
return i;
};

auto gen = fpgen::from_stream(str, func);
for (int i = 0; i < 5; i++) {
bool genstate = gen;
EXPECT_TRUE(genstate);
auto tmp = gen();
EXPECT_EQ(tmp, numbers[i]);
std::cout << i << "," << tmp << std::endl;
}

bool genstate = gen;
EXPECT_FALSE(genstate);
}

TEST(sources, lipsum_lines) {
std::string lipsum = R"(
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque diam magna,
laoreet non dictum eget, scelerisque eu nibh. Cras luctus purus sit amet
sodales aliquet. Proin vulputate risus quam. Curabitur ultricies, elit nec
pharetra accumsan, leo eros mollis nibh, pulvinar lobortis dolor diam non quam.
Vivamus odio arcu, aliquet ornare leo quis, mollis porta nisl. Mauris malesuada
semper efficitur. Vestibulum nulla diam, hendrerit in diam a, tempor dignissim
turpis. Maecenas eleifend laoreet velit id semper. Aliquam quis mattis enim.
Cras gravida, felis vitae porta auctor, magna purus aliquet lorem, ut maximus
tortor tortor sit amet mauris. Mauris eleifend enim eget arcu blandit auctor.
Etiam vel porta augue. Maecenas volutpat odio in lacus sagittis fermentum.
)";
std::string lines[] = {"Lorem ipsum dolor sit amet, consectetur adipiscing "
"elit. Quisque diam magna,",
"laoreet non dictum eget, scelerisque eu nibh. Cras "
"luctus purus sit amet",
"sodales aliquet. Proin vulputate risus quam. "
"Curabitur ultricies, elit nec",
"pharetra accumsan, leo eros mollis nibh, pulvinar "
"lobortis dolor diam non quam.",
"Vivamus odio arcu, aliquet ornare leo quis, mollis "
"porta nisl. Mauris malesuada",
"semper efficitur. Vestibulum nulla diam, hendrerit "
"in diam a, tempor dignissim",
"turpis. Maecenas eleifend laoreet velit id semper. "
"Aliquam quis mattis enim.",
"Cras gravida, felis vitae porta auctor, magna purus "
"aliquet lorem, ut maximus",
"tortor tortor sit amet mauris. Mauris eleifend enim "
"eget arcu blandit auctor.",
"Etiam vel porta augue. Maecenas volutpat odio in "
"lacus sagittis fermentum."};
std::stringstream strm;
strm << lipsum;
auto gen = fpgen::from_lines(strm);
EXPECT_EQ("", gen());
for (size_t i = 0; i < 10; i++) {
bool gens = gen;
EXPECT_TRUE(gens);
EXPECT_EQ(gen(), lines[i]);
}
EXPECT_EQ(gen(), "");
bool gens = gen;
EXPECT_FALSE(gens);
}
30 changes: 0 additions & 30 deletions test/src/test_stream.cpp

This file was deleted.

0 comments on commit 3b07e7f

Please sign in to comment.