如何解决如何在C ++ 20中获得运行良好的生成器类模板?
我只是一个简单的编码器,经常使用Python,并且沉迷于其生成器。据我了解当前的情况,可以使用协程在C ++ 20中完全实现它们,但是,至少直到C ++ 23为止,这并不是一件容易的事,因为需要编写一个生成器类(模板)。我怎么得到那个
- 运行速度相当快(至少不比旧的generators hack with macros慢)
- 我可以在其上使用基于范围的
for
,ranges库和Python的next
的等效项。如果有一种方法可以测试发电机是否耗尽,那也很好。 - 当(如果有)等效项添加到标准库中时,我(很有可能)不需要更改我的代码太多
这有可能吗?
解决方法
如评论中所述,libcoro提供了更高级别的抽象,并可能解决您的某些问题。
对于point2,如果您确实需要一个公共方法来告诉您生成器已用尽,我想增强libcoro生成器将使其变得很容易。这是一个(未经测试)可能的示例。但是检查generator.end()对您来说是一个问题吗?
namespace libcoro {
template<typename T>
class [[nodiscard]] generator
{
public:
using reference_type = std::conditional_t<std::is_reference_v<T>,T,T&>;
//.. libcoro stuff
// ADDED
bool done() const {
m_coroutine.done();
}
reference_type value() const {
return m_coroutine.promise().value();
}
void resume() {
m_coroutine.resume();
}
// ...
};
}
然后您可以做:
while (true) {
gen.resume();
if(gen.done()) {
std::cout << "this is the end!" << std::endl;
break;
}
std::cout << "new value: " << gen.value() << std::endl;
}
,
在这里,您可以找到一些速度较快的生成器implementation,该生成器更喜欢std实现(如在MSVC中):
在我的真实代码中,它被拆分为多个文件,但是为了显示在这里,我将其合并了。
它适用于所有主要的编译器(gcc,clang,msvc)。
//------------------
//general coroutine support
//------------------
#if defined(__clang__)
//see: https://developercommunity.visualstudio.com/content/problem/502513/unable-to-use-clang-cl-coroutines-due-to-unresolve.html
namespace std {
namespace experimental {
inline namespace coroutines_v1 {
template <typename R,typename...>
struct coroutine_traits {
using promise_type = typename R::promise_type;
};
template <typename Promise = void>
struct coroutine_handle;
template <> struct coroutine_handle<void> {
static coroutine_handle from_address(void* addr) noexcept {
coroutine_handle me;
me.ptr = addr;
return me;
}
void operator()() { resume(); }
void* address() const { return ptr; }
void resume() const { __builtin_coro_resume(ptr); }
void destroy() const { __builtin_coro_destroy(ptr); }
bool done() const { return __builtin_coro_done(ptr); }
coroutine_handle& operator=(decltype(nullptr)) {
ptr = nullptr;
return *this;
}
coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
coroutine_handle() : ptr(nullptr) {}
// void reset() { ptr = nullptr; } // add to P0057?
explicit operator bool() const { return ptr; }
protected:
void* ptr;
};
template <typename Promise>
struct coroutine_handle : coroutine_handle<> {
using coroutine_handle<>::operator=;
using coroutine_handle<>::coroutine_handle;
static coroutine_handle from_address(void* addr) noexcept {
coroutine_handle me;
me.ptr = addr;
return me;
}
Promise& promise() const {
return *reinterpret_cast<Promise*>(
__builtin_coro_promise(ptr,alignof(Promise),false));
}
static coroutine_handle from_promise(Promise& promise) {
coroutine_handle p;
p.ptr = __builtin_coro_promise(&promise,true);
return p;
}
};
template <typename _PromiseT>
bool operator==(coroutine_handle<_PromiseT> const& _Left,coroutine_handle<_PromiseT> const& _Right) noexcept
{
return _Left.address() == _Right.address();
}
template <typename _PromiseT>
bool operator!=(coroutine_handle<_PromiseT> const& _Left,coroutine_handle<_PromiseT> const& _Right) noexcept
{
return !(_Left == _Right);
}
struct suspend_always {
bool await_ready() noexcept { return false; }
void await_suspend(coroutine_handle<>) noexcept {}
void await_resume() noexcept {}
};
struct suspend_never {
bool await_ready() noexcept { return true; }
void await_suspend(coroutine_handle<>) noexcept {}
void await_resume() noexcept {}
};
}
}
}
#define NATIVE_COROUTINE_IMPL_NS std::experimental
#elif __has_include(<coroutine>)
#include <coroutine>
#define NATIVE_COROUTINE_IMPL_NS std
#elif __has_include(<experimental/coroutine>)
#include <experimental/coroutine>
#define NATIVE_COROUTINE_IMPL_NS std::experimental
#endif
//------------------
//Generator defintion
//-----------------
#if __has_include(<generator>)
#include <generator>
namespace CORE_NATIVE_NS
{
template<typename T>
using YieldEnum = std::generator<T>;
}
#elif __has_include(<experimental/generator>) && !defined(__clang__)
#include <experimental/generator>
namespace CORE_NATIVE_NS
{
template<typename T>
using YieldEnum = std::experimental::generator<T>;
}
#else
#include <memory>
#include <cstddef>
#include <type_traits>
#include <utility>
#include <exception>
#include <iterator>
#include <functional>
namespace CORE_NATIVE_NS
{
template<typename T>
class generator;
namespace detail
{
template<typename T>
class generator_promise
{
public:
using value_type = std::remove_reference_t<T>;
using reference_type = std::conditional_t<std::is_reference_v<T>,T&>;
using pointer_type = value_type*;
generator_promise() = default;
generator<T> get_return_object() noexcept;
constexpr NATIVE_COROUTINE_IMPL_NS::suspend_always initial_suspend() const noexcept
{ return {}; }
constexpr NATIVE_COROUTINE_IMPL_NS::suspend_always final_suspend() const noexcept
{ return {}; }
template<
typename U = T,std::enable_if_t<!std::is_rvalue_reference<U>::value,int> = 0
>
NATIVE_COROUTINE_IMPL_NS::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
{
m_value = std::addressof(value);
return {};
}
NATIVE_COROUTINE_IMPL_NS::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept
{
m_value = std::addressof(value);
return {};
}
void unhandled_exception() noexcept
{
m_exception = std::current_exception();
}
void return_void() const noexcept
{
}
reference_type value() const noexcept
{
return static_cast<reference_type>(*m_value);
}
// Don't allow any use of 'co_await' inside the generator coroutine.
template<typename U>
NATIVE_COROUTINE_IMPL_NS::suspend_never await_transform(U&& value) = delete;
void rethrow_if_exception()
{
if (m_exception)
{
std::rethrow_exception(m_exception);
}
}
private:
pointer_type m_value;
std::exception_ptr m_exception;
};
struct generator_sentinel {};
template<typename T>
class generator_iterator
{
using coroutine_handle = NATIVE_COROUTINE_IMPL_NS::coroutine_handle<generator_promise<T>>;
public:
using iterator_category = std::input_iterator_tag;
// What type should we use for counting elements of a potentially infinite sequence?
using difference_type = std::ptrdiff_t;
using value_type = typename generator_promise<T>::value_type;
using reference = typename generator_promise<T>::reference_type;
using pointer = typename generator_promise<T>::pointer_type;
// Iterator needs to be default-constructible to satisfy the Range concept.
generator_iterator() noexcept
: m_coroutine(nullptr)
{}
explicit generator_iterator(coroutine_handle coroutine) noexcept
: m_coroutine(coroutine)
{}
friend bool operator==(const generator_iterator& it,generator_sentinel) noexcept
{
return !it.m_coroutine || it.m_coroutine.done();
}
friend bool operator!=(const generator_iterator& it,generator_sentinel s) noexcept
{
return !(it == s);
}
friend bool operator==(generator_sentinel s,const generator_iterator& it) noexcept
{
return (it == s);
}
friend bool operator!=(generator_sentinel s,const generator_iterator& it) noexcept
{
return it != s;
}
generator_iterator& operator++()
{
m_coroutine.resume();
if (m_coroutine.done())
{
m_coroutine.promise().rethrow_if_exception();
}
return *this;
}
// Need to provide post-increment operator to implement the 'Range' concept.
void operator++(int)
{
(void)operator++();
}
void next(){
(void)operator++();
}
reference operator*() const noexcept
{
return m_coroutine.promise().value();
}
pointer operator->() const noexcept
{
return std::addressof(operator*());
}
private:
coroutine_handle m_coroutine;
};
}
template<typename T>
class [[nodiscard]] generator
{
public:
using promise_type = detail::generator_promise<T>;
using iterator = detail::generator_iterator<T>;
generator() noexcept
: m_coroutine(nullptr)
{}
generator(generator&& other) noexcept
: m_coroutine(other.m_coroutine)
{
other.m_coroutine = nullptr;
}
generator(const generator& other) = delete;
~generator()
{
if (m_coroutine)
{
m_coroutine.destroy();
}
}
generator& operator=(generator other) noexcept
{
swap(other);
return *this;
}
iterator begin()
{
if (m_coroutine)
{
m_coroutine.resume();
if (m_coroutine.done())
{
m_coroutine.promise().rethrow_if_exception();
}
}
return iterator{ m_coroutine };
}
detail::generator_sentinel end() noexcept
{
return detail::generator_sentinel{};
}
void swap(generator& other) noexcept
{
std::swap(m_coroutine,other.m_coroutine);
}
private:
friend class detail::generator_promise<T>;
explicit generator(NATIVE_COROUTINE_IMPL_NS::coroutine_handle<promise_type> coroutine) noexcept
: m_coroutine(coroutine)
{}
NATIVE_COROUTINE_IMPL_NS::coroutine_handle<promise_type> m_coroutine;
};
template<typename T>
void swap(generator<T>& a,generator<T>& b)
{
a.swap(b);
}
namespace detail
{
template<typename T>
generator<T> generator_promise<T>::get_return_object() noexcept
{
using coroutine_handle = NATIVE_COROUTINE_IMPL_NS::coroutine_handle<generator_promise<T>>;
return generator<T>{ coroutine_handle::from_promise(*this) };
}
}
template<typename FUNC,typename T>
generator<std::invoke_result_t<FUNC&,typename generator<T>::iterator::reference>> fmap(FUNC func,generator<T> source)
{
for (auto&& value : source)
{
co_yield std::invoke(func,static_cast<decltype(value)>(value));
}
}
template<typename T>
using YieldEnum = CORE_NATIVE_NS::generator<T>;
}
#endif
inline CORE_NATIVE_NS::YieldEnum<int> GetNumbers() noexcept
{
for(int i=0; i<= 10; ++i){
co_yield 10;
}
}
#include <iostream>
int main()
{
int sum = 0;
for(auto x : GetNumbers()){
sum += x;
}
std::cout << sum;
return 0;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。