Skip to content

Commit 05d5353

Browse files
authored
Merge pull request #5 from jay-tux/feat/sources
Feat/sources
2 parents 5b11795 + c1cc5b1 commit 05d5353

File tree

8 files changed

+226
-22
lines changed

8 files changed

+226
-22
lines changed

inc/__helpers.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef _FPGEN___HELPERS
2+
#define _FPGEN___HELPERS
3+
4+
#ifdef __clang__
5+
#include <type_traits>
6+
#define _FPGEN_USE_CONCEPTS 0
7+
#else
8+
#include <concepts>
9+
#define _FPGEN_USE_CONCEPTS 1
10+
#endif
11+
12+
#endif

inc/fpgen.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
#define _FPGEN_MAIN
33

44
#include "generator.hpp"
5+
#include "sources.hpp"
56

67
#endif

inc/generator.hpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,22 @@
22
#define _FPGEN_GENERATOR
33

44
#ifdef __clang__
5-
#include <concepts>
65
#include <experimental/coroutine>
76
namespace std {
87
using namespace experimental;
98
}
109
#else
11-
#include <concepts>
1210
#include <coroutine>
1311
#endif
1412

13+
#include "__helpers.hpp"
1514
#include <exception>
1615

1716
/**
18-
* @brief The namespace containing all of fpgen's code.
17+
* \brief The namespace containing all of fpgen's code.
1918
*/
2019
namespace fpgen {
21-
#ifdef __clang__
22-
template <typename T>
23-
using enabler =
24-
typename std::enable_if<std::is_copy_assignable<T>::value, bool>::type;
25-
26-
template <typename T, enabler<T> = true> class generator {
27-
#else
20+
#if _FPGEN_USE_CONCEPTS
2821
/**
2922
* \brief The main generator type.
3023
*
@@ -39,6 +32,12 @@ template <typename T, enabler<T> = true> class generator {
3932
* `std::copyable`, or on (older) CLang versions, `std::is_copy_assignable<T>`.
4033
*/
4134
template <std::copyable T> class generator {
35+
#else
36+
template <typename T>
37+
using enabler =
38+
typename std::enable_if<std::is_copy_assignable<T>::value, bool>::type;
39+
40+
template <typename T, enabler<T> = true> class generator {
4241
#endif
4342
public:
4443
/**
@@ -202,6 +201,8 @@ template <std::copyable T> class generator {
202201
if (!source)
203202
is_finished = true;
204203
value = source();
204+
if (!source)
205+
is_finished = true;
205206
return {source, is_finished, value};
206207
}
207208

inc/sources.hpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#ifndef _FPGEN_SOURCES
2+
#define _FPGEN_SOURCES
3+
4+
#include "generator.hpp"
5+
#include <iostream>
6+
#include <iterator>
7+
8+
/**
9+
* \brief The namespace containing all of fpgen's code.
10+
*/
11+
namespace fpgen {
12+
/**
13+
* \brief Creates a generator over a data source.
14+
*
15+
* The data source should have an iterator (using `std::begin` and `std::end`).
16+
* For most builtin containers (`std::vector`, ...) this is already satisfied.
17+
* For `std::map`, see fpgen::from_tup.
18+
*
19+
* \tparam T The type contained in the container.
20+
* \tparam TArgs Any other template parameters passed to the container.
21+
* \tparam Container The container type.
22+
* \param[in] cont The container to iterate over.
23+
* \returns A new generator which will iterate over the container.
24+
* \see fpgen::from_tup, fpgen::enumerate
25+
*/
26+
template <typename T, typename... TArgs,
27+
template <typename...> typename Container>
28+
generator<T> from(const Container<T, TArgs...> &cont) {
29+
for (auto it = std::begin(cont); it != std::end(cont); ++it) {
30+
co_yield *it;
31+
}
32+
}
33+
34+
/**
35+
* \brief Creates a generator over a data source, with indexing.
36+
*
37+
* The data source does not have to allow indexing, but it is recommended for
38+
* repeatable behaviour. If the index is not needed, use fpgen::from. The data
39+
* source should support iterating (using `std::begin` and `std::end`).
40+
*
41+
* \tparam T The type contained in the container.
42+
* \tparam TArgs Any other template parameters passed to the container.
43+
* \tparam Container The container type.
44+
* \param[in] cont The container to iterate over.
45+
* \returns A new generator which will iterate over the container using index
46+
* and value.
47+
* \see fpgen::from_tup, fpgen::enumerate
48+
*/
49+
template <typename T, typename... TArgs,
50+
template <typename...> typename Container>
51+
generator<std::tuple<size_t, T>> enumerate(const Container<T, TArgs...> &cont) {
52+
size_t i = 0;
53+
for (auto it = std::begin(cont); it != std::end(cont); ++it) {
54+
co_yield {i, *it};
55+
i++;
56+
}
57+
}
58+
59+
/**
60+
* \brief Creates a generator over an associative data source.
61+
*
62+
* The data source should have an iterator (using `std::begin` and `std::end`).
63+
* For most builtin containers (`std::map`, ...) this is already satisfied.
64+
* The container should have two type arguments. For single-type containers, see
65+
* fpgen::from.
66+
*
67+
* \tparam TKey The first type contained in the container (key type).
68+
* \tparam TValue The second type contained in the container (value type).
69+
* \tparam TArgs Any other template parameters passed to the container.
70+
* \tparam Container The container type.
71+
* \param[in] cont The container to iterate over.
72+
* \returns A new generator which will iterate over the container.
73+
* \see fpgen::from
74+
*/
75+
template <typename TKey, typename TVal, typename... TArgs,
76+
template <typename...> typename Container>
77+
generator<std::tuple<TKey, TVal>>
78+
from_tup(const Container<TKey, TVal, TArgs...> &cont) {
79+
for (auto it = cont.begin(); it != cont.end(); ++it) {
80+
co_yield *it;
81+
}
82+
}
83+
84+
/**
85+
* \brief Creates an infinitely incrementing generator.
86+
*
87+
* The generator is contstructed by continuously incrementing (a copy of) the
88+
* given value. While mainly meant for integral types, any type supporting
89+
* operator++() (the prefix increment operator) can be used. The first value
90+
* returned is the start value itself.
91+
*
92+
* \tparam T The type to increment.
93+
* \param[in] start The initial value.
94+
* \returns An infinite generator which increments a value.
95+
*/
96+
template <typename T> generator<T> inc(T start) {
97+
T value = start;
98+
while (true) {
99+
co_yield value;
100+
++value;
101+
}
102+
}
103+
104+
} // namespace fpgen
105+
106+
#endif

test/Makefile

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
SOURCES=$(shell find $(SRCD) -name '*.cpp')
22
DEPS=$(SOURCES:$(SRCD)/%.cpp=$(OBJD)/%.d)
3-
TESTS=generator
3+
TESTS=generator sources
4+
TESTOBJ=$(TESTS:%=$(OBJD)/test_%.o)
45

5-
all: dirs conan/conanbuildinfo.mak $(BIND)/generator
6+
CONAN_PKG_OVERRIDE=gtest
7+
CONAN_MODIFY=$(CONAN_PKG_OVERRIDE:%=-s %:compiler.version=11.2)
8+
9+
all: dirs conan/conanbuildinfo.mak $(BIND)/_test
610

711
-include $(DEPS)
812
-include conan/conanbuildinfo.mak
@@ -11,21 +15,22 @@ LDXTRA=$(CONAN_LIB_DIRS:%=-L%) $(CONAN_LIBS:%=-l%) $(CONAN_SYSTEM_LIBS:%=-l%)
1115

1216
dirs:
1317
@([ ! -d $(OBJD)/generator ] && mkdir -p $(OBJD)/generator) || true
18+
@([ ! -d $(OBJD)/sources ] && mkdir -p $(OBJD)/sources) || true
1419

15-
$(BIND)/generator: $(OBJD)/generator/test_generator.o
16-
$(CC) $< $(LDXTRA) $(LDARGS) -o $@
17-
$(BIND)/generator --gtest_output="xml:$(BIND)/"
20+
$(BIND)/_test: $(TESTOBJ) $(OBJD)/test_main.o
21+
$(CC) $(LDXTRA) $(LDARGS) $^ -o $@
22+
$(BIND)/_test
1823

1924
$(OBJD)/%.o: $(SRCD)/%.cpp Makefile
2025
$(CC) $(CXXARGS) $(CXXXTRA) $< -o $@
2126

2227
conan/conanbuildinfo.mak: conanfile.txt
2328
@([ ! -d conan/ ] && mkdir -p conan/) || true
24-
cd conan && CC=gcc CXX=$(CC) conan install .. --build=gtest
29+
cd conan && CC=gcc CXX=$(CC) conan install .. $(CONAN_MODIFY) --build=gtest
2530

2631
clean:
2732
find ./bin/ -type f | grep -v '.gitkeep' | xargs rm -rf
2833
find ./obj/ -type f | grep -v '.gitkeep' | xargs rm -rf
2934
find ./conan/ -type f | grep -v '.gitkeep' | xargs rm -rf
3035

31-
.PHONY: all dirs clean
36+
.PHONY: all dirs clean $(TESTS:%=$(BIND)/%)

test/src/generator/test_generator.cpp renamed to test/src/test_generator.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,3 @@ TEST(generator, iterator) {
6363
expect++;
6464
}
6565
}
66-
67-
int main(int argc, char **argv) {
68-
testing::InitGoogleTest(&argc, argv);
69-
return RUN_ALL_TESTS();
70-
}

test/src/test_main.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "generator.hpp"
2+
#include "gmock/gmock.h"
3+
#include "gtest/gtest.h"

test/src/test_sources.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#include "generator.hpp"
2+
#include "sources.hpp"
3+
#include "gmock/gmock.h"
4+
#include "gtest/gtest.h"
5+
6+
#include <map>
7+
#include <set>
8+
#include <string>
9+
#include <vector>
10+
11+
TEST(sources, from_vector) {
12+
std::vector<int> values = {0, 5, 1, 4, 2, 3};
13+
size_t idx = 0;
14+
for (auto v : fpgen::from(values)) {
15+
EXPECT_EQ(values[idx], v);
16+
idx++;
17+
}
18+
}
19+
20+
TEST(sources, from_set) {
21+
std::set<std::string> srcs = {"key 1", "key 2", "key 3", "something"};
22+
std::set<std::string> todo = {"key 1", "key 2", "key 3", "something"};
23+
24+
for (auto v : fpgen::from(srcs)) {
25+
EXPECT_NE(todo.find(v), todo.end());
26+
todo.erase(v);
27+
}
28+
EXPECT_TRUE(todo.empty());
29+
}
30+
31+
TEST(sources, enumerate_vector) {
32+
std::vector<char> values = { 'a', 'c', 'e', 'k', 'j', 't' };
33+
size_t prev = 0;
34+
for(auto v : fpgen::enumerate(values)) {
35+
EXPECT_EQ(std::get<0>(v), prev);
36+
EXPECT_EQ(values[prev], std::get<1>(v));
37+
prev++;
38+
}
39+
}
40+
41+
TEST(sources, from_map_tup) {
42+
std::map<std::string, std::string> map = {{"key 1", "value 1"},
43+
{"key 2", "value 2"},
44+
{"key 3", "value 3"},
45+
{"something", "else"}};
46+
std::set<std::string> todo = {"key 1", "key 2", "key 3", "something"};
47+
for (auto v : fpgen::from_tup(map)) {
48+
EXPECT_NE(todo.find(std::get<0>(v)), todo.end());
49+
EXPECT_EQ(map[std::get<0>(v)], std::get<1>(v));
50+
todo.erase(std::get<0>(v));
51+
}
52+
EXPECT_TRUE(todo.empty());
53+
}
54+
55+
TEST(sources, incrementable) {
56+
auto gen = fpgen::inc<int>(0);
57+
for (int i = 0; i < 25; i++) {
58+
EXPECT_EQ(gen(), i);
59+
}
60+
}
61+
62+
TEST(sources, incrementable_struct) {
63+
struct inc_struct {
64+
int value;
65+
inline inc_struct operator++() {
66+
value++;
67+
return {value};
68+
}
69+
inline inc_struct operator++(int) {
70+
inc_struct next = {value};
71+
value++;
72+
return next;
73+
}
74+
} v1;
75+
v1.value = 0;
76+
77+
auto gen = fpgen::inc<inc_struct>(v1);
78+
for (int i = 0; i < 25; i++) {
79+
EXPECT_EQ(gen().value, i);
80+
}
81+
}

0 commit comments

Comments
 (0)