Skip to content

Commit 68d10ea

Browse files
committed
Generator, promise type, iterator and related tests
1 parent 9cd172e commit 68d10ea

File tree

6 files changed

+218
-8
lines changed

6 files changed

+218
-8
lines changed

Makefile

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ INSTALL_DIR=/usr/include/fpgen
77
INCL_PATH=$(abspath ./inc)
88
DOC_DIR=$(abspath ./docs/)
99

10-
JOB_COUNT=4
11-
1210
CC=g++
13-
CXXARGS=-I$(abspath ./inc)
11+
CXXARGS=-I$(abspath ./inc) -g -c -std=c++20 -MMD
1412
LDARGS=
1513

1614
all:
@@ -33,10 +31,9 @@ all:
3331
@echo " -> INSTALL_DIR (for install and uninstall): sets the installation directory"
3432
@echo " -> INCL_PATH (for install, docs and test): the path to the directory with the headers, if you run make from another directory"
3533
@echo " -> DOC_DIR (for docs): the path to the build directory for the documentation"
36-
@echo " -> JOB_COUNT (for test): the amount of threads to start"
3734
@echo " Current/default arguments: "
3835
@echo " CC=$(CC) CXXARGS=$(CXXARGS) LDARGS=$(LDARGS) EXTRA_CXX=$(EXTRA_CXX) EXTRA_LD=$(EXTRA_LD)"
39-
@echo " BUILD_DIR=$(BUILD_DIR) BIN_DIR=$(BIN_DIR) TEST_DIR=$(TEST_DIR) INSTALL_DIR=$(INSTALL_DIR) INCL_PATH=$(INCL_PATH) DOC_DIR=$(DOC_DIR) JOB_COUNT=$(JOB_COUNT)"
36+
@echo " BUILD_DIR=$(BUILD_DIR) BIN_DIR=$(BIN_DIR) TEST_DIR=$(TEST_DIR) INSTALL_DIR=$(INSTALL_DIR) INCL_PATH=$(INCL_PATH) DOC_DIR=$(DOC_DIR)"
4037

4138
install:
4239
[ ! -d $(INSTALL_DIR) ] && mkdir -p $(INSTALL_DIR)
@@ -50,10 +47,10 @@ docs:
5047
OUTDIR=$(DOC_DIR) INDIR=$(INCL_PATH) doxygen doxyfile.mk
5148

5249
test:
53-
make CC="$(CC)" OBJD="$(BUILD_DIR)" BIND="$(BIN_DIR)" SRCD="$(TEST_DIR)" CXXARGS="$(CXXARGS) $(EXTRA_CXX) -I$(INCL_PATH)" LDARGS="$(LDARGS) $(EXTRA_LD)" -C $(TEST_DIR)/.. -j $(JOB_COUNT)
50+
make CC="$(CC)" OBJD="$(BUILD_DIR)" BIND="$(BIN_DIR)" SRCD="$(TEST_DIR)" CXXARGS="$(CXXARGS) $(EXTRA_CXX) -I$(INCL_PATH)" LDARGS="$(LDARGS) $(EXTRA_LD)" -C $(TEST_DIR)/..
5451

5552
clean:
56-
rm -rf $(DOC_DIR)/* $(BUILD_DIR)/* $(BIN_DIR)/*
57-
53+
rm -rf $(DOC_DIR)/*
54+
cd $(TEST_DIR)/.. && make clean OBJD="$(BUILD_DIR)" BIND="$(BIN_DIR)" SRCD="$(TEST_DIR)"
5855

5956
.PHONY: install uninstall test clean

inc/fpgen.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef _FPGEN_MAIN
2+
#define _FPGEN_MAIN
3+
4+
#include "generator.hpp"
5+
6+
#endif

inc/generator.hpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#ifndef _FPGEN_GENERATOR
2+
#define _FPGEN_GENERATOR
3+
4+
#ifdef __clang__
5+
#include <concepts>
6+
#include <experimental/coroutine>
7+
namespace std {
8+
using namespace experimental;
9+
}
10+
#else
11+
#include <concepts>
12+
#include <coroutine>
13+
#endif
14+
15+
#include <exception>
16+
17+
namespace fpgen {
18+
#ifdef __clang__
19+
template <typename T>
20+
using enabler =
21+
typename std::enable_if<std::is_copy_assignable<T>::value, bool>::type;
22+
23+
template <typename T, enabler<T> = true> class generator {
24+
#else
25+
template <std::copyable T> class generator {
26+
#endif
27+
public:
28+
struct promise_type {
29+
using value_type = T;
30+
using except_type = std::exception_ptr;
31+
using gen_type = generator<T>;
32+
using suspend_type = std::suspend_always;
33+
34+
value_type value;
35+
except_type ex;
36+
37+
gen_type get_return_object() { return gen_type(*this); }
38+
suspend_type initial_suspend() { return {}; }
39+
suspend_type final_suspend() noexcept { return {}; }
40+
void return_value(value_type v) { value = v; }
41+
suspend_type yield_value(value_type v) {
42+
value = v;
43+
return {};
44+
}
45+
46+
void unhandled_exception() { ex = std::current_exception(); }
47+
};
48+
struct iterator_type {
49+
using gen_t = generator<T>;
50+
using iter_t = iterator_type;
51+
using value_t = T;
52+
53+
gen_t &source;
54+
bool is_finished;
55+
value_t value;
56+
57+
iterator_type(gen_t &source, bool is_finised)
58+
: source{source}, is_finished{is_finised} {
59+
if (!is_finised)
60+
++(*this);
61+
}
62+
iterator_type(gen_t &source, bool is_finished, value_t value)
63+
: source{source}, is_finished{is_finished}, value{value} {}
64+
65+
iter_t operator++() {
66+
if (!source)
67+
is_finished = true;
68+
value = source();
69+
return {source, is_finished, value};
70+
}
71+
operator bool() { return static_cast<bool>(source); }
72+
bool operator!=(iter_t &other) { return is_finished != other.is_finished; }
73+
value_t operator*() { return value; }
74+
operator value_t() { return value; }
75+
};
76+
77+
using handle_type = std::coroutine_handle<promise_type>;
78+
using value_type = T;
79+
80+
generator(promise_type &p) : _h{handle_type::from_promise(p)} {}
81+
operator handle_type() const { return _h; }
82+
operator bool() const { return !_h.done(); }
83+
84+
iterator_type begin() { return {*this, false}; }
85+
iterator_type end() { return {*this, true}; }
86+
87+
value_type operator()() {
88+
if (*this)
89+
_h();
90+
if (_h.promise().ex)
91+
std::rethrow_exception(_h.promise().ex);
92+
return _h.promise().value;
93+
}
94+
95+
private:
96+
handle_type _h;
97+
};
98+
99+
} // namespace fpgen
100+
101+
#endif

test/Makefile

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
SOURCES=$(shell find $(SRCD) -name '*.cpp')
2+
DEPS=$(SOURCES:$(SRCD)/%.cpp=$(OBJD)/%.d)
3+
TESTS=generator
4+
5+
all: dirs conan/conanbuildinfo.mak $(BIND)/generator
6+
7+
-include $(DEPS)
8+
-include conan/conanbuildinfo.mak
9+
CXXXTRA=$(CONAN_INCLUDE_DIRS:%=-I%) $(CONAN_CXXFLAGS)
10+
LDXTRA=$(CONAN_LIB_DIRS:%=-L%) $(CONAN_LIBS:%=-l%) $(CONAN_SYSTEM_LIBS:%=-l%)
11+
12+
dirs:
13+
@([ ! -d $(OBJD)/generator ] && mkdir -p $(OBJD)/generator) || true
14+
15+
$(BIND)/generator: $(OBJD)/generator/test_generator.o
16+
$(CC) $< $(LDXTRA) $(LDARGS) -o $@
17+
$(BIND)/generator
18+
19+
$(OBJD)/%.o: $(SRCD)/%.cpp Makefile
20+
$(CC) $(CXXARGS) $(CXXXTRA) $< -o $@
21+
22+
conan/conanbuildinfo.mak: conanfile.txt
23+
@([ ! -d conan/ ] && mkdir -p conan/) || true
24+
cd conan && CC=gcc CXX=$(CC) conan install .. --build=gtest
25+
26+
clean:
27+
find ./bin/ -type f | grep -v '.gitkeep' | xargs rm -rf
28+
find ./obj/ -type f | grep -v '.gitkeep' | xargs rm -rf
29+
find ./conan/ -type f | grep -v '.gitkeep' | xargs rm -rf
30+
31+
.PHONY: all dirs clean

test/conanfile.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[requires]
2+
gtest/cci.20210126
3+
4+
[generators]
5+
make
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include "generator.hpp"
2+
#include "gmock/gmock.h"
3+
#include "gtest/gtest.h"
4+
#include <iostream>
5+
6+
fpgen::generator<float> empty() { co_return 0; }
7+
8+
fpgen::generator<int> infinite() {
9+
int value = 0;
10+
while (true) {
11+
co_yield value;
12+
value++;
13+
}
14+
}
15+
16+
fpgen::generator<long> finite_squares(int min, int max) {
17+
for (int val = min; val < max; val++) {
18+
co_yield val *val;
19+
}
20+
co_return max *max;
21+
}
22+
23+
TEST(generator, accept_empty_gen) {
24+
auto emptygen = empty();
25+
SUCCEED();
26+
}
27+
28+
TEST(generator, iterator_empty_gen) {
29+
auto gen = empty();
30+
gen();
31+
for (auto v : gen) {
32+
FAIL();
33+
}
34+
SUCCEED();
35+
}
36+
37+
TEST(generator, can_call_and_continue) {
38+
auto intgen = infinite();
39+
int value = intgen();
40+
EXPECT_EQ(0, value);
41+
value = intgen();
42+
EXPECT_EQ(1, value);
43+
}
44+
45+
TEST(generator, can_while_over) {
46+
int value;
47+
int expect = 0;
48+
auto intgen2 = finite_squares(0, 12);
49+
while (intgen2) {
50+
value = intgen2();
51+
EXPECT_THAT(expect, testing::AllOf(testing::Le(12), testing::Ge(0)));
52+
EXPECT_EQ(expect * expect, value);
53+
expect++;
54+
}
55+
}
56+
57+
TEST(generator, iterator) {
58+
int expect = -4;
59+
auto intgen3 = finite_squares(-4, 8);
60+
for (auto value : intgen3) {
61+
EXPECT_THAT(expect, testing::AllOf(testing::Le(8), testing::Ge(-4)));
62+
EXPECT_EQ(expect * expect, value);
63+
expect++;
64+
}
65+
}
66+
67+
int main(int argc, char **argv) {
68+
testing::InitGoogleTest(&argc, argv);
69+
return RUN_ALL_TESTS();
70+
}

0 commit comments

Comments
 (0)