'CPPCORO'라이브러리는 N4680에 설명 된 Coroutines TS 제안을 사용하기위한 대규모 일반 목적 프리미티브 세트를 제공합니다.
여기에는 다음이 포함됩니다.
task<T>shared_task<T>generator<T>recursive_generator<T>async_generator<T>single_consumer_eventsingle_consumer_async_auto_reset_eventasync_mutexasync_manual_reset_eventasync_auto_reset_eventasync_latchsequence_barriermulti_producer_sequencersingle_producer_sequencersync_wait()when_all()when_all_ready()fmap()schedule_on()resume_on()cancellation_tokencancellation_sourcecancellation_registrationstatic_thread_poolio_service 및 io_work_scopefile , readable_file , writable_fileread_only_file , write_only_file , read_write_filesocketip_address , ipv4_address , ipv6_addressip_endpoint , ipv4_endpoint , ipv6_endpointis_awaitable<T>awaitable_traits<T>Awaitable<T>Awaiter<T>SchedulerDelayedScheduler이 라이브러리는 C ++ 코 루틴 제안서 위에 구축 될 수있는 고성능의 확장 가능한 비동기 프로그래밍 추상화의 공간을 탐색하는 실험 라이브러리입니다.
다른 사람들이 유용 할 것이며 C ++ 커뮤니티가 이에 대한 피드백과이를 개선하는 방법을 제공 할 수 있기를 희망하면서 오픈 소스를 받았습니다.
코 루틴 TS를 지원하는 컴파일러가 필요합니다.
Linux 버전은 아직 Linux 용으로 구현되지 않은 io_context 및 파일 I/O 관련 클래스를 제외하고는 기능적입니다 (자세한 내용은 15 호 참조).
task<T>작업은 작업이 기다릴 때까지 코 루틴의 실행이 시작되지 않는다는 점에서 게으르게 실행되는 비동기 계산을 나타냅니다.
예:
# include < cppcoro/read_only_file.hpp >
# include < cppcoro/task.hpp >
cppcoro::task< int > count_lines (std::string path)
{
auto file = co_await cppcoro::read_only_file::open (path);
int lineCount = 0 ;
char buffer[ 1024 ];
size_t bytesRead;
std:: uint64_t offset = 0 ;
do
{
bytesRead = co_await file. read (offset, buffer, sizeof (buffer));
lineCount += std::count (buffer, buffer + bytesRead, ' n ' );
offset += bytesRead;
} while (bytesRead > 0 );
co_return lineCount;
}
cppcoro::task<> usage_example ()
{
// Calling function creates a new task but doesn't start
// executing the coroutine yet.
cppcoro::task< int > countTask = count_lines ( " foo.txt " );
// ...
// Coroutine is only started when we later co_await the task.
int lineCount = co_await countTask;
std::cout << " line count = " << lineCount << std::endl;
}API 개요 :
// <cppcoro/task.hpp>
namespace cppcoro
{
template < typename T>
class task
{
public:
using promise_type = <unspecified>;
using value_type = T;
task () noexcept ;
task (task&& other) noexcept ;
task& operator =(task&& other);
// task is a move-only type.
task ( const task& other) = delete ;
task& operator =( const task& other) = delete ;
// Query if the task result is ready.
bool is_ready () const noexcept ;
// Wait for the task to complete and return the result or rethrow the
// exception if the operation completed with an unhandled exception.
//
// If the task is not yet ready then the awaiting coroutine will be
// suspended until the task completes. If the the task is_ready() then
// this operation will return the result synchronously without suspending.
Awaiter<T&> operator co_await () const & noexcept ;
Awaiter<T&&> operator co_await () const && noexcept ;
// Returns an awaitable that can be co_await'ed to suspend the current
// coroutine until the task completes.
//
// The 'co_await t.when_ready()' expression differs from 'co_await t' in
// that when_ready() only performs synchronization, it does not return
// the result or rethrow the exception.
//
// This can be useful if you want to synchronize with the task without
// the possibility of it throwing an exception.
Awaitable< void > when_ready () const noexcept ;
};
template < typename T>
void swap (task<T>& a, task<T>& b);
// Creates a task that yields the result of co_await'ing the specified awaitable.
//
// This can be used as a form of type-erasure of the concrete awaitable, allowing
// different awaitables that return the same await-result type to be stored in
// the same task<RESULT> type.
template <
typename AWAITABLE,
typename RESULT = typename awaitable_traits<AWAITABLE>:: await_result_t >
task<RESULT> make_task (AWAITABLE awaitable);
} task<T> 반환하는 코 루틴 함수를 호출하여 task<T> 객체를 만들 수 있습니다.
코 루틴은 co_await 또는 co_return 의 사용법을 포함해야합니다. task<T> coroutine은 co_yield 키워드를 사용하지 않을 수 있습니다.
task<T> 반환하는 코 루틴이 호출되면 필요한 경우 코 루틴 프레임이 할당되고 매개 변수는 코 루틴 프레임에 캡처됩니다. 코 루틴은 코 루틴 본체의 시작 부분에서 일시 중지되고 실행이 발신자에게 반환되고 task<T> 계산을 나타내는 값이 함수 호출에서 반환됩니다.
task<T> 값이 co_await Ed 일 때 코 루틴 본문이 실행되기 시작합니다. 이것은 기다려온 코 루틴을 중단하고 기다려온 task<T> 값과 관련된 코 루틴의 실행을 시작합니다.
기다리고있는 코 루틴은 나중에 대기하는 task<T> 의 코 루틴의 실행을 완료하는 스레드에서 재개됩니다. 즉. co_return 을 실행하거나 코 루틴의 실행을 종료하는 처리되지 않은 예외를 던지는 스레드.
작업이 이미 완료되면 다시 기다리는 것은 기다리고있는 코 루틴을 중단하지 않고 이미 컴퓨터 결과를 얻게됩니다.
task 객체가 기다리기 전에 파괴되면 코 루틴이 실행되지 않으며 소멸자는 단순히 캡처 된 매개 변수를 파괴하고 코 루틴 프레임에 사용되는 메모리를 해방시킵니다.
shared_task<T> shared_task<T> 클래스는 단일 값을 비동기 적으로 산출하는 코 루틴 유형입니다.
작업의 실행이 일부 코 루틴이 기다릴 때까지 시작되지 않는다는 것은 '게으른'입니다.
작업 값을 복사 할 수 있으므로 작업 결과에 대한 여러 참조가 생성 될 수 있다는 점에서 '공유'됩니다. 또한 여러 코 루틴이 결과를 동시에 기다릴 수 있습니다.
작업은 첫 번째 co_await 작업을 수행하는 스레드에서 실행되기 시작합니다. 후속 AWAITERS는 작업이 완료되면 재개를 위해 정지되고 재개를 위해 대기열이 발생하거나 작업이 이미 완료된 경우 동기식으로 계속됩니다.
작업이 완료되기를 기다리는 동안 Awaiter가 정지 된 경우 작업의 실행을 완료하는 스레드에서 재개됩니다. 즉. co_return 을 실행하거나 코 루틴의 실행을 종료하는 처리되지 않은 예외를 던지는 스레드.
API 요약
namespace cppcoro
{
template < typename T = void >
class shared_task
{
public:
using promise_type = <unspecified>;
using value_type = T;
shared_task () noexcept ;
shared_task ( const shared_task& other) noexcept ;
shared_task (shared_task&& other) noexcept ;
shared_task& operator =( const shared_task& other) noexcept ;
shared_task& operator =(shared_task&& other) noexcept ;
void swap (shared_task& other) noexcept ;
// Query if the task has completed and the result is ready.
bool is_ready () const noexcept ;
// Returns an operation that when awaited will suspend the
// current coroutine until the task completes and the result
// is available.
//
// The type of the result of the 'co_await someTask' expression
// is an l-value reference to the task's result value (unless T
// is void in which case the expression has type 'void').
// If the task completed with an unhandled exception then the
// exception will be rethrown by the co_await expression.
Awaiter<T&> operator co_await () const noexcept ;
// Returns an operation that when awaited will suspend the
// calling coroutine until the task completes and the result
// is available.
//
// The result is not returned from the co_await expression.
// This can be used to synchronize with the task without the
// possibility of the co_await expression throwing an exception.
Awaiter< void > when_ready () const noexcept ;
};
template < typename T>
bool operator ==( const shared_task<T>& a, const shared_task<T>& b) noexcept ;
template < typename T>
bool operator !=( const shared_task<T>& a, const shared_task<T>& b) noexcept ;
template < typename T>
void swap (shared_task<T>& a, shared_task<T>& b) noexcept ;
// Wrap an awaitable value in a shared_task to allow multiple coroutines
// to concurrently await the result.
template <
typename AWAITABLE,
typename RESULT = typename awaitable_traits<AWAITABLE>:: await_result_t >
shared_task<RESULT> make_shared_task (AWAITABLE awaitable);
} shared_task<T> 의 모든 const 방법은 여러 스레드의 동일한 인스턴스에서 다른 const- 방법과 동시에 호출하는 데 안전합니다. shared_task<T> 의 동일한 인스턴스에 대한 다른 방법과 동시에 shared_task<T> 의 비 초가 방법을 동시에 호출하는 것은 안전하지 않습니다.
task<T> shared_task<T> 클래스는 task<T> 와 비슷합니다. 코 루틴 기능이 호출되면 즉시 작업이 실행을 시작하지 않는다는 점에서 비슷합니다. 작업은 처음 기다릴 때만 실행을 시작합니다.
결과적인 작업 객체를 복사 할 수 있다는 점에서 task<T> 와 다릅니다. 여러 작업 객체가 동일한 비동기 결과를 참조 할 수 있습니다. 또한 작업 결과를 동시에 기다리는 여러 코 루틴을 지원합니다.
트레이드 오프는 결과가 항상 결과에 대한 L- 값 참조이며, 결과를 로컬 변수로 이동하는 능력을 제한 할 수있는 r- 값 참조 (결과를 공유 할 수 있음)는 절대 공유 할 수 없다는 것입니다. 또한 참조 수를 유지하고 다수의 기다리는 사람을 지원해야하기 때문에 런타임 비용이 약간 높습니다.
generator<T> generator 값이 게으르게 생성되고 동기식으로 생성되는 유형 T 값을 생성하는 코 루틴 유형을 나타냅니다.
Corootine 본문은 co_yield 키워드를 사용하여 T 형 값을 산출 할 수 있습니다. 그러나 Coroutine 본문은 co_await 키워드를 사용할 수 없습니다. 값은 동시에 생성되어야합니다.
예를 들어:
cppcoro::generator< const std:: uint64_t > fibonacci ()
{
std:: uint64_t a = 0 , b = 1 ;
while ( true )
{
co_yield b;
auto tmp = a;
a = b;
b += tmp;
}
}
void usage ()
{
for ( auto i : fibonacci ())
{
if (i > 1'000'000 ) break ;
std::cout << i << std::endl;
}
} generator<T> 라고 불렀을 때 코 루틴은 처음에 매달린 코 루틴이 생성됩니다. generator<T>::begin() 메소드가 호출 될 때 코 루틴의 실행은 코 루틴 본체로 들어가고 첫 번째 co_yield 문에 도달하거나 코 루틴이 완료 될 때까지 계속됩니다.
반환 된 반복자가 end() 반복자와 같지 않으면 반복되는 반복기는 co_yield 문에 전달 된 값에 대한 참조를 반환합니다.
반복자에서 operator++() 호출하면 코 루틴의 실행이 재개되고 다음 co_yield 지점에 도달하거나 Coroutine이 완료 ()로 실행될 때까지 계속됩니다.
Coroutine에 의해 던져진 예외는 begin() 또는 operator++() 호출에서 발신자에게 전파됩니다.
API 요약 :
namespace cppcoro
{
template < typename T>
class generator
{
public:
using promise_type = <unspecified>;
class iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = std:: remove_reference_t <T>;
using reference = value_type&;
using pointer = value_type*;
using difference_type = std:: size_t ;
iterator ( const iterator& other) noexcept ;
iterator& operator =( const iterator& other) noexcept ;
// If the generator coroutine throws an unhandled exception before producing
// the next element then the exception will propagate out of this call.
iterator& operator ++();
reference operator *() const noexcept ;
pointer operator ->() const noexcept ;
bool operator ==( const iterator& other) const noexcept ;
bool operator !=( const iterator& other) const noexcept ;
};
// Constructs to the empty sequence.
generator () noexcept ;
generator (generator&& other) noexcept ;
generator& operator =(generator&& other) noexcept ;
generator ( const generator& other) = delete ;
generator& operator =( const generator&) = delete ;
~generator ();
// Starts executing the generator coroutine which runs until either a value is yielded
// or the coroutine runs to completion or an unhandled exception propagates out of the
// the coroutine.
iterator begin ();
iterator end () noexcept ;
// Swap the contents of two generators.
void swap (generator& other) noexcept ;
};
template < typename T>
void swap (generator<T>& a, generator<T>& b) noexcept ;
// Apply function, func, lazily to each element of the source generator
// and yield a sequence of the results of calls to func().
template < typename FUNC, typename T>
generator<std:: invoke_result_t <FUNC, T&>> fmap (FUNC func, generator<T> source);
}recursive_generator<T> recursive_generator 외부 시퀀스의 요소로서 중첩 시퀀스의 요소를보다 효율적으로지지하도록 설계되도록 설계된 것을 제외하고는 generator 와 유사합니다.
type T 의 값을 co_yield 할 수있을뿐만 아니라 유형의 값을 co_yield recursive_generator<T> 의 값을 co_yield 할 수도 있습니다.
recursive_generator<T> 값을 co_yield 할 때, 산출 된 생성기의 모든 요소는 현재 생성기의 요소로 생성됩니다. 현재 코 루틴은 소비자가 중첩 발전기의 모든 요소를 소비 할 때까지 중단되며, 그 후 현재 코 루틴의 지점 실행은 실행을 재개하여 다음 요소를 생성합니다.
재귀 적 데이터 구조를 통한 반복에 대한 recursive_generator<T> generator<T> 이점은 iterator::operator++() 가 각 요소에 대해 O (깊이) 코 루틴을 재개/삭감하지 않고 다음 요소를 생성하기 위해 잎이 가장 큰 코 루틴을 직접 재개 할 수 있다는 것입니다. 아래쪽은 추가 오버 헤드가 있다는 것입니다
예를 들어:
// Lists the immediate contents of a directory.
cppcoro::generator<dir_entry> list_directory (std::filesystem::path path);
cppcoro::recursive_generator<dir_entry> list_directory_recursive (std::filesystem::path path)
{
for ( auto & entry : list_directory (path))
{
co_yield entry;
if (entry. is_directory ())
{
co_yield list_directory_recursive (entry. path ());
}
}
} fmap() 연산자를 recursive_generator<T> 에 적용하면 recursive_generator<U> 가 아닌 generator<U> 유형을 생성합니다. 이는 fmap 의 사용이 일반적으로 재귀 컨텍스트에서 사용되지 않으며 recursive_generator 에서 발생하는 추가 오버 헤드를 피하려고 노력하기 때문입니다.
async_generator<T> async_generator 값이 게으름하게 생성되고 값이 비동기 적으로 생성 될 수있는 유형 T 의 값을 생성하는 코 루틴 유형을 나타냅니다.
코 루틴 본체는 co_await 및 co_yield 표현식을 모두 사용할 수 있습니다.
발전기의 소비자는 for co_await 범위 기반 for-loop를 사용하여 값을 소비 할 수 있습니다.
예
cppcoro::async_generator< int > ticker ( int count, threadpool& tp)
{
for ( int i = 0 ; i < count; ++i)
{
co_await tp. delay ( std::chrono::seconds ( 1 ));
co_yield i;
}
}
cppcoro::task<> consumer (threadpool& tp)
{
auto sequence = ticker ( 10 , tp);
for co_await (std:: uint32_t i : sequence)
{
std::cout << " Tick " << i << std::endl;
}
}API 요약
// <cppcoro/async_generator.hpp>
namespace cppcoro
{
template < typename T>
class async_generator
{
public:
class iterator
{
public:
using iterator_tag = std::forward_iterator_tag;
using difference_type = std:: size_t ;
using value_type = std:: remove_reference_t <T>;
using reference = value_type&;
using pointer = value_type*;
iterator ( const iterator& other) noexcept ;
iterator& operator =( const iterator& other) noexcept ;
// Resumes the generator coroutine if suspended
// Returns an operation object that must be awaited to wait
// for the increment operation to complete.
// If the coroutine runs to completion then the iterator
// will subsequently become equal to the end() iterator.
// If the coroutine completes with an unhandled exception then
// that exception will be rethrown from the co_await expression.
Awaitable<iterator&> operator ++() noexcept ;
// Dereference the iterator.
pointer operator ->() const noexcept ;
reference operator *() const noexcept ;
bool operator ==( const iterator& other) const noexcept ;
bool operator !=( const iterator& other) const noexcept ;
};
// Construct to the empty sequence.
async_generator () noexcept ;
async_generator ( const async_generator&) = delete ;
async_generator (async_generator&& other) noexcept ;
~async_generator ();
async_generator& operator =( const async_generator&) = delete ;
async_generator& operator =(async_generator&& other) noexcept ;
void swap (async_generator& other) noexcept ;
// Starts execution of the coroutine and returns an operation object
// that must be awaited to wait for the first value to become available.
// The result of co_await'ing the returned object is an iterator that
// can be used to advance to subsequent elements of the sequence.
//
// This method is not valid to be called once the coroutine has
// run to completion.
Awaitable<iterator> begin () noexcept ;
iterator end () noexcept ;
};
template < typename T>
void swap (async_generator<T>& a, async_generator<T>& b);
// Apply 'func' to each element of the source generator, yielding a sequence of
// the results of calling 'func' on the source elements.
template < typename FUNC, typename T>
async_generator<std:: invoke_result_t <FUNC, T&>> fmap (FUNC func, async_generator<T> source);
} async_generator 객체가 파괴되면 기본 코 루틴의 취소를 요청합니다. 코 루틴이 이미 완료되거나 현재 co_yield 표현식으로 중단 된 경우, 코 루틴은 즉시 파괴됩니다. 그렇지 않으면, 코 루틴은 완료되거나 다음 co_yield 표현식에 도달 할 때까지 계속 실행됩니다.
코 루틴 프레임이 파괴되면 그 시점에서 모든 변수의 파괴자가 발전기의 자원을 정리하기 위해 실행됩니다.
소비자 코 루틴이 다음 항목이 생성되기를 기다리는 co_await 표현식을 실행하는 동안 발신자는 async_generator 객체를 파괴하지 않아야합니다.
single_consumer_event이것은 한 번에 기다리는 단일 코 루틴 만 지원하는 간단한 수동 리셋 이벤트 유형입니다. 이것은 사용될 수 있습니다
API 요약 :
// <cppcoro/single_consumer_event.hpp>
namespace cppcoro
{
class single_consumer_event
{
public:
single_consumer_event ( bool initiallySet = false ) noexcept ;
bool is_set () const noexcept ;
void set ();
void reset () noexcept ;
Awaiter< void > operator co_await () const noexcept ;
};
}예:
# include < cppcoro/single_consumer_event.hpp >
cppcoro::single_consumer_event event;
std::string value;
cppcoro::task<> consumer ()
{
// Coroutine will suspend here until some thread calls event.set()
// eg. inside the producer() function below.
co_await event;
std::cout << value << std::endl;
}
void producer ()
{
value = " foo " ;
// This will resume the consumer() coroutine inside the call to set()
// if it is currently suspended.
event. set ();
}single_consumer_async_auto_reset_event 이 클래스는 단일 코 루틴이 set() 메소드에 대한 호출에 의해 이벤트가 신호를 보낼 때까지 기다릴 수있는 비동기 동기화 프리미티브를 제공합니다.
이벤트를 기다리는 코 루틴이 이전 또는 후속 호출 set() 로 릴리스되면 이벤트는 자동으로 'SET NOT SET'상태로 다시 재설정합니다.
이 클래스는 단일 코 루틴 만 한 번에 이벤트를 기다리는 경우에 사용할 수있는 async_auto_reset_event 의보다 효율적인 버전입니다. 이벤트에서 여러 동시 동시 동시 동시 코 루틴을 지원 해야하는 경우 대신 async_auto_reset_event 클래스를 사용하십시오.
API 요약 :
// <cppcoro/single_consumer_async_auto_reset_event.hpp>
namespace cppcoro
{
class single_consumer_async_auto_reset_event
{
public:
single_consumer_async_auto_reset_event (
bool initiallySet = false ) noexcept ;
// Change the event to the 'set' state. If a coroutine is awaiting the
// event then the event is immediately transitioned back to the 'not set'
// state and the coroutine is resumed.
void set () noexcept ;
// Returns an Awaitable type that can be awaited to wait until
// the event becomes 'set' via a call to the .set() method. If
// the event is already in the 'set' state then the coroutine
// continues without suspending.
// The event is automatically reset back to the 'not set' state
// before resuming the coroutine.
Awaiter< void > operator co_await () const noexcept ;
};
}예제 사용 :
std::atomic< int > value;
cppcoro::single_consumer_async_auto_reset_event valueDecreasedEvent;
cppcoro::task<> wait_until_value_is_below ( int limit)
{
while (value. load (std::memory_order_relaxed) >= limit)
{
// Wait until there has been some change that we're interested in.
co_await valueDecreasedEvent;
}
}
void change_value ( int delta)
{
value. fetch_add (delta, std::memory_order_relaxed);
// Notify the waiter if there has been some change.
if (delta < 0 ) valueDecreasedEvent. set ();
}async_mutex발신자가 뮤 테스 잠금이 획득 될 때까지 코 루틴 내에서 뮤 테스를 'CO_AWAIT'할 수있는 간단한 상호 배제 추상화를 제공합니다.
구현은 MUTEX를 기다리는 코 루틴이 스레드를 차단하지 않고 대신에 코 루틴을 중단하고 나중에 이전의 잠금 장치에 의해 unlock() 에 대한 호출 내부에서 재개한다는 점에서 잠금이 없습니다.
API 요약 :
// <cppcoro/async_mutex.hpp>
namespace cppcoro
{
class async_mutex_lock ;
class async_mutex_lock_operation ;
class async_mutex_scoped_lock_operation ;
class async_mutex
{
public:
async_mutex () noexcept ;
~async_mutex ();
async_mutex ( const async_mutex&) = delete ;
async_mutex& operator ( const async_mutex&) = delete;
bool try_lock () noexcept ;
async_mutex_lock_operation lock_async () noexcept ;
async_mutex_scoped_lock_operation scoped_lock_async () noexcept ;
void unlock ();
};
class async_mutex_lock_operation
{
public:
bool await_ready () const noexcept ;
bool await_suspend (std::experimental::coroutine_handle<> awaiter) noexcept ;
void await_resume () const noexcept ;
};
class async_mutex_scoped_lock_operation
{
public:
bool await_ready () const noexcept ;
bool await_suspend (std::experimental::coroutine_handle<> awaiter) noexcept ;
[[nodiscard]] async_mutex_lock await_resume () const noexcept ;
};
class async_mutex_lock
{
public:
// Takes ownership of the lock.
async_mutex_lock (async_mutex& mutex, std:: adopt_lock_t ) noexcept ;
// Transfer ownership of the lock.
async_mutex_lock (async_mutex_lock&& other) noexcept ;
async_mutex_lock ( const async_mutex_lock&) = delete ;
async_mutex_lock& operator =( const async_mutex_lock&) = delete ;
// Releases the lock by calling unlock() on the mutex.
~async_mutex_lock ();
};
}예제 사용 :
# include < cppcoro/async_mutex.hpp >
# include < cppcoro/task.hpp >
# include < set >
# include < string >
cppcoro::async_mutex mutex;
std::set<std::string> values;
cppcoro::task<> add_item (std::string value)
{
cppcoro::async_mutex_lock lock = co_await mutex. scoped_lock_async ();
values. insert ( std::move (value));
}async_manual_reset_event 수동 리셋 이벤트는 Coroutine/Thread-Synchronization Primitive로, 하나 이상의 스레드가 set() 호출하는 스레드에 의해 이벤트가 신호를받을 때까지 기다릴 수 있습니다.
이 행사는 두 주 중 하나에 있습니다. '설정' 및 '설정되지 않음' .
코 루틴이 이벤트를 기다릴 때 이벤트가 '세트' 상태에있는 경우, 코 루틴은 매달리지 않고 계속됩니다. 그러나 코 루틴이 '세트가 아닌' 상태에 있으면 일부 스레드가 이후에 set() 메소드를 호출 할 때까지 코 루틴이 매달린다.
이벤트가 '세트' 가되기를 기다리는 동안 일시 중단 된 모든 스레드는 일부 스레드에 의해 다음 호출 set() 내부에서 재개됩니다.
이벤트가 재개되지 않아 파괴 될 때 코 루틴이 '설정되지 않음' 이벤트를 기다리고 있는지 확인해야합니다.
예:
cppcoro::async_manual_reset_event event;
std::string value;
void producer ()
{
value = get_some_string_value ();
// Publish a value by setting the event.
event. set ();
}
// Can be called many times to create many tasks.
// All consumer tasks will wait until value has been published.
cppcoro::task<> consumer ()
{
// Wait until value has been published by awaiting event.
co_await event;
consume_value (value);
}API 요약 :
namespace cppcoro
{
class async_manual_reset_event_operation ;
class async_manual_reset_event
{
public:
async_manual_reset_event ( bool initiallySet = false ) noexcept ;
~async_manual_reset_event ();
async_manual_reset_event ( const async_manual_reset_event&) = delete ;
async_manual_reset_event (async_manual_reset_event&&) = delete ;
async_manual_reset_event& operator =( const async_manual_reset_event&) = delete ;
async_manual_reset_event& operator =(async_manual_reset_event&&) = delete ;
// Wait until the event becomes set.
async_manual_reset_event_operation operator co_await () const noexcept ;
bool is_set () const noexcept ;
void set () noexcept ;
void reset () noexcept ;
};
class async_manual_reset_event_operation
{
public:
async_manual_reset_event_operation (async_manual_reset_event& event) noexcept ;
bool await_ready () const noexcept ;
bool await_suspend (std::experimental::coroutine_handle<> awaiter) noexcept ;
void await_resume () const noexcept ;
};
}async_auto_reset_event 자동 리세트 이벤트는 Coroutine/Thread-Synchronization Primitive로, set() 호출하여 이벤트가 스레드에 의해 신호를받을 때까지 하나 이상의 스레드가 기다릴 수 있습니다.
이벤트를 기다리는 코 루틴이 이전 또는 후속 호출 set() 에 의해 릴리스되면 이벤트는 자동으로 'SET NOT SET'상태로 다시 재설정합니다.
API 요약 :
// <cppcoro/async_auto_reset_event.hpp>
namespace cppcoro
{
class async_auto_reset_event_operation ;
class async_auto_reset_event
{
public:
async_auto_reset_event ( bool initiallySet = false ) noexcept ;
~async_auto_reset_event ();
async_auto_reset_event ( const async_auto_reset_event&) = delete ;
async_auto_reset_event (async_auto_reset_event&&) = delete ;
async_auto_reset_event& operator =( const async_auto_reset_event&) = delete ;
async_auto_reset_event& operator =(async_auto_reset_event&&) = delete ;
// Wait for the event to enter the 'set' state.
//
// If the event is already 'set' then the event is set to the 'not set'
// state and the awaiting coroutine continues without suspending.
// Otherwise, the coroutine is suspended and later resumed when some
// thread calls 'set()'.
//
// Note that the coroutine may be resumed inside a call to 'set()'
// or inside another thread's call to 'operator co_await()'.
async_auto_reset_event_operation operator co_await () const noexcept ;
// Set the state of the event to 'set'.
//
// If there are pending coroutines awaiting the event then one
// pending coroutine is resumed and the state is immediately
// set back to the 'not set' state.
//
// This operation is a no-op if the event was already 'set'.
void set () noexcept ;
// Set the state of the event to 'not-set'.
//
// This is a no-op if the state was already 'not set'.
void reset () noexcept ;
};
class async_auto_reset_event_operation
{
public:
explicit async_auto_reset_event_operation (async_auto_reset_event& event) noexcept ;
async_auto_reset_event_operation ( const async_auto_reset_event_operation& other) noexcept ;
bool await_ready () const noexcept ;
bool await_suspend (std::experimental::coroutine_handle<> awaiter) noexcept ;
void await_resume () const noexcept ;
};
}async_latch비동기 래치는 코 루틴이 카운터가 0으로 감소 될 때까지 비동기 적으로 대기 할 수있는 동기화 프리미티브입니다.
래치는 일회용 객체입니다. 카운터가 0에 도달하면 래치가 '준비'되고 래치가 파괴 될 때까지 준비되어 있습니다.
API 요약 :
// <cppcoro/async_latch.hpp>
namespace cppcoro
{
class async_latch
{
public:
// Initialise the latch with the specified count.
async_latch (std:: ptrdiff_t initialCount) noexcept ;
// Query if the count has reached zero yet.
bool is_ready () const noexcept ;
// Decrement the count by n.
// This will resume any waiting coroutines if the count reaches zero
// as a result of this call.
// It is undefined behaviour to decrement the count below zero.
void count_down (std:: ptrdiff_t n = 1 ) noexcept ;
// Wait until the latch becomes ready.
// If the latch count is not yet zero then the awaiting coroutine will
// be suspended and later resumed by a call to count_down() that decrements
// the count to zero. If the latch count was already zero then the coroutine
// continues without suspending.
Awaiter< void > operator co_await () const noexcept ;
};
}sequence_barrier sequence_barrier 는 단일 프로듀서 및 다수의 소비자가 단조로 증가하는 시퀀스 수와 관련하여 조정할 수있는 동기화 프리미티브입니다.
단일 생산자는 단조로 증가하는 순서로 새로운 시퀀스 번호를 게시함으로써 시퀀스 번호를 발전시킵니다. 하나 이상의 소비자가 마지막으로 게시 된 시퀀스 번호를 쿼리 할 수 있으며 특정 시퀀스 번호가 게시 될 때까지 기다릴 수 있습니다.
시퀀스 장벽은 스레드 안전 생산자/소비자 링 버퍼로 커서를 나타내는 데 사용될 수 있습니다.
더 많은 배경은 LMAX Disruptor 패턴을 참조하십시오 : https://lmax-exchange.github.io/disruptor/files/disruptor-1.0.pdf
API 시놉시스 :
namespace cppcoro
{
template < typename SEQUENCE = std:: size_t ,
typename TRAITS = sequence_traits<SEQUENCE>>
class sequence_barrier
{
public:
sequence_barrier (SEQUENCE initialSequence = TRAITS::initial_sequence) noexcept ;
~sequence_barrier ();
SEQUENCE last_published () const noexcept ;
// Wait until the specified targetSequence number has been published.
//
// If the operation does not complete synchronously then the awaiting
// coroutine is resumed on the specified scheduler. Otherwise, the
// coroutine continues without suspending.
//
// The co_await expression resumes with the updated last_published()
// value, which is guaranteed to be at least 'targetSequence'.
template < typename SCHEDULER>
[[nodiscard]]
Awaitable<SEQUENCE> wait_until_published (SEQUENCE targetSequence,
SCHEDULER& scheduler) const noexcept ;
void publish (SEQUENCE sequence) noexcept ;
};
}single_producer_sequencer single_producer_sequencer 는 단일 생산자 및 하나 이상의 소비자를위한 링 버퍼에 대한 액세스를 조정하는 데 사용할 수있는 동기화 원시입니다.
프로듀서는 먼저 링 버퍼에서 하나 이상의 슬롯을 획득하고 해당 슬롯에 해당하는 링 버퍼 요소에 글을 쓰고 마침내 해당 슬롯에 기록 된 값을 게시합니다. 생산자는 소비자가 소비 한 위치에 미리 '버퍼 크기'요소 이상을 생산할 수 없습니다.
그런 다음 소비자는 특정 요소가 게시 될 때까지 기다렸다가 항목을 처리 한 다음 sequence_barrier 객체에서 소비 한 시퀀스 번호를 게시하여 항목을 완료했을 때 생산자에게 통지합니다.
API 시놉시스 :
// <cppcoro/single_producer_sequencer.hpp>
namespace cppcoro
{
template <
typename SEQUENCE = std:: size_t ,
typename TRAITS = sequence_traits<SEQUENCE>>
class single_producer_sequencer
{
public:
using size_type = typename sequence_range<SEQUENCE, TRAITS>::size_type;
single_producer_sequencer (
const sequence_barrier<SEQUENCE, TRAITS>& consumerBarrier,
std:: size_t bufferSize,
SEQUENCE initialSequence = TRAITS::initial_sequence) noexcept ;
// Publisher API:
template < typename SCHEDULER>
[[nodiscard]]
Awaitable<SEQUENCE> claim_one (SCHEDULER& scheduler) noexcept ;
template < typename SCHEDULER>
[[nodiscard]]
Awaitable<sequence_range<SEQUENCE>> claim_up_to (
std:: size_t count,
SCHEDULER& scheduler) noexcept ;
void publish (SEQUENCE sequence) noexcept ;
// Consumer API:
SEQUENCE last_published () const noexcept ;
template < typename SCHEDULER>
[[nodiscard]]
Awaitable<SEQUENCE> wait_until_published (
SEQUENCE targetSequence,
SCHEDULER& scheduler) const noexcept ;
};
}예제 사용 :
using namespace cppcoro ;
using namespace std ::chrono ;
struct message
{
int id;
steady_clock::time_point timestamp;
float data;
};
constexpr size_t bufferSize = 16384 ; // Must be power-of-two
constexpr size_t indexMask = bufferSize - 1 ;
message buffer[bufferSize];
task< void > producer (
io_service& ioSvc,
single_producer_sequencer< size_t >& sequencer)
{
auto start = steady_clock::now ();
for ( int i = 0 ; i < 1'000'000 ; ++i)
{
// Wait until a slot is free in the buffer.
size_t seq = co_await sequencer. claim_one (ioSvc);
// Populate the message.
auto & msg = buffer[seq & indexMask];
msg. id = i;
msg. timestamp = steady_clock::now ();
msg. data = 123 ;
// Publish the message.
sequencer. publish (seq);
}
// Publish a sentinel
auto seq = co_await sequencer. claim_one (ioSvc);
auto & msg = buffer[seq & indexMask];
msg. id = - 1 ;
sequencer. publish (seq);
}
task< void > consumer (
static_thread_pool& threadPool,
const single_producer_sequencer< size_t >& sequencer,
sequence_barrier< size_t >& consumerBarrier)
{
size_t nextToRead = 0 ;
while ( true )
{
// Wait until the next message is available
// There may be more than one available.
const size_t available = co_await sequencer. wait_until_published (nextToRead, threadPool);
do {
auto & msg = buffer[nextToRead & indexMask];
if (msg. id == - 1 )
{
consumerBarrier. publish (nextToRead);
co_return ;
}
processMessage (msg);
} while (nextToRead++ != available);
// Notify the producer that we've finished processing
// up to 'nextToRead - 1'.
consumerBarrier. publish (available);
}
}
task< void > example (io_service& ioSvc, static_thread_pool& threadPool)
{
sequence_barrier< size_t > barrier;
single_producer_sequencer< size_t > sequencer{barrier, bufferSize};
co_await when_all (
producer (tp, sequencer),
consumer (tp, sequencer, barrier));
}multi_producer_sequencer multi_producer_sequencer 클래스는 여러 생산자 및 하나 이상의 소비자를위한 링 버퍼에 대한 액세스를 조정하는 동기화 프리미티브입니다.
단일 프로듀서 변형의 경우 single_producer_sequencer 클래스를 참조하십시오.
링 버퍼의 크기는 두 가지 전력이어야합니다. 구현은 정수 디비전/모듈로 대신 비트 마스크를 사용하여 오프셋을 버퍼로 계산하기 때문입니다. 또한 시퀀스 번호는 32 비트/64 비트 값을 안전하게 감싸도록 허용합니다.
API 요약 :
// <cppcoro/multi_producer_sequencer.hpp>
namespace cppcoro
{
template < typename SEQUENCE = std:: size_t ,
typename TRAITS = sequence_traits<SEQUENCE>>
class multi_producer_sequencer
{
public:
multi_producer_sequencer (
const sequence_barrier<SEQUENCE, TRAITS>& consumerBarrier,
SEQUENCE initialSequence = TRAITS::initial_sequence);
std:: size_t buffer_size () const noexcept ;
// Consumer interface
//
// Each consumer keeps track of their own 'lastKnownPublished' value
// and must pass this to the methods that query for an updated last-known
// published sequence number.
SEQUENCE last_published_after (SEQUENCE lastKnownPublished) const noexcept ;
template < typename SCHEDULER>
Awaitable<SEQUENCE> wait_until_published (
SEQUENCE targetSequence,
SEQUENCE lastKnownPublished,
SCHEDULER& scheduler) const noexcept ;
// Producer interface
// Query whether any slots available for claiming (approx.)
bool any_available () const noexcept ;
template < typename SCHEDULER>
Awaitable<SEQUENCE> claim_one (SCHEDULER& scheduler) noexcept ;
template < typename SCHEDULER>
Awaitable<sequence_range<SEQUENCE, TRAITS>> claim_up_to (
std:: size_t count,
SCHEDULER& scheduler) noexcept ;
// Mark the specified sequence number as published.
void publish (SEQUENCE sequence) noexcept ;
// Mark all sequence numbers in the specified range as published.
void publish ( const sequence_range<SEQUENCE, TRAITS>& range) noexcept ;
};
} cancellation_token 발신자가 해당 함수에 대한 작업을 취소하라는 요청을 전달할 수있는 함수로 전달할 수있는 값입니다.
취소 할 수있는 cancellation_token 얻으려면 먼저 cancellation_source 객체를 작성해야합니다. cancellation_source::token() 메소드를 사용하여 해당 cancellation_source 객체에 연결된 새로운 cancellation_token 값을 제조 할 수 있습니다.
나중에 작업의 취소를 요청하려면 cancellation_token 통과 한 후 연관된 cancellation_source 객체에서 cancellation_source::request_cancellation() 에 전화 할 수 있습니다.
기능은 두 가지 방법 중 하나로 취소 요청에 응답 할 수 있습니다.
cancellation_token::is_cancellation_requested() 또는 cancellation_token::throw_if_cancellation_requested() 호출하여 정기적 인 간격으로 취소를위한 설문 조사.cancellation_registration 클래스를 사용하여 취소를 요청할 때 실행할 콜백을 등록하십시오.API 요약 :
namespace cppcoro
{
class cancellation_source
{
public:
// Construct a new, independently cancellable cancellation source.
cancellation_source ();
// Construct a new reference to the same cancellation state.
cancellation_source ( const cancellation_source& other) noexcept ;
cancellation_source (cancellation_source&& other) noexcept ;
~cancellation_source ();
cancellation_source& operator =( const cancellation_source& other) noexcept ;
cancellation_source& operator =(cancellation_source&& other) noexcept ;
bool is_cancellation_requested () const noexcept ;
bool can_be_cancelled () const noexcept ;
void request_cancellation ();
cancellation_token token () const noexcept ;
};
class cancellation_token
{
public:
// Construct a token that can't be cancelled.
cancellation_token () noexcept ;
cancellation_token ( const cancellation_token& other) noexcept ;
cancellation_token (cancellation_token&& other) noexcept ;
~cancellation_token ();
cancellation_token& operator =( const cancellation_token& other) noexcept ;
cancellation_token& operator =(cancellation_token&& other) noexcept ;
bool is_cancellation_requested () const noexcept ;
void throw_if_cancellation_requested () const ;
// Query if this token can ever have cancellation requested.
// Code can use this to take a more efficient code-path in cases
// that the operation does not need to handle cancellation.
bool can_be_cancelled () const noexcept ;
};
// RAII class for registering a callback to be executed if cancellation
// is requested on a particular cancellation token.
class cancellation_registration
{
public:
// Register a callback to be executed if cancellation is requested.
// Callback will be called with no arguments on the thread that calls
// request_cancellation() if cancellation is not yet requested, or
// called immediately if cancellation has already been requested.
// Callback must not throw an unhandled exception when called.
template < typename CALLBACK>
cancellation_registration (cancellation_token token, CALLBACK&& callback);
cancellation_registration ( const cancellation_registration& other) = delete ;
~cancellation_registration ();
};
class operation_cancelled : public std :: exception
{
public:
operation_cancelled ();
const char * what () const override ;
};
}예 : 폴링 접근법
cppcoro::task<> do_something_async (cppcoro::cancellation_token token)
{
// Explicitly define cancellation points within the function
// by calling throw_if_cancellation_requested().
token. throw_if_cancellation_requested ();
co_await do_step_1 ();
token. throw_if_cancellation_requested ();
do_step_2 ();
// Alternatively, you can query if cancellation has been
// requested to allow yourself to do some cleanup before
// returning.
if (token. is_cancellation_requested ())
{
display_message_to_user ( " Cancelling operation... " );
do_cleanup ();
throw cppcoro::operation_cancelled{};
}
do_final_step ();
}예 : 콜백 접근
// Say we already have a timer abstraction that supports being
// cancelled but it doesn't support cancellation_tokens natively.
// You can use a cancellation_registration to register a callback
// that calls the existing cancellation API. e.g.
cppcoro::task<> cancellable_timer_wait (cppcoro::cancellation_token token)
{
auto timer = create_timer (10s);
cppcoro::cancellation_registration registration (token, [&]
{
// Call existing timer cancellation API.
timer. cancel ();
});
co_await timer;
}static_thread_pool static_thread_pool 클래스는 고정 크기의 스레드 풀에서 작업을 예약 할 수있는 추상화를 제공합니다.
이 클래스는 스케줄러 개념을 구현합니다 (아래 참조).
co_await threadPool.schedule() 실행하여 스레드 풀에 흡수 할 수 있습니다. 이 작업은 현재 Coroutine을 중단하고 스레드 풀에서 실행을 위해 Enqueue를 사용하며 스레드 풀의 스레드가 다음에 Coroutine을 실행할 수있게되면 스레드 풀이 Coroutine을 재개합니다. 이 작업은 던지지 않도록 보장되며 공동 경우에는 메모리를 할당하지 않습니다 .
이 클래스는 여러 스레드에 걸쳐 작업을 강화하기 위해 워크 스테이킹 알고리즘을 사용합니다. 스레드-풀 스레드에서 스레드 풀에 quequeed 작업은 Lifo 대기열의 동일한 스레드에서 실행할 예정입니다. 원격 스레드에서 스레드 풀에 quequeed 작업은 글로벌 FIFO 대기열에 큐를 제공합니다. 작업자 스레드가 로컬 큐에서 작업이 부족하면 먼저 글로벌 대기열에서 작업을 탈취하려고합니다. 해당 대기열이 비어 있으면 다음에 다른 작업자 스레드의 대기열 뒷면에서 작업을 훔치려 고합니다.
API 요약 :
namespace cppcoro
{
class static_thread_pool
{
public:
// Initialise the thread-pool with a number of threads equal to
// std::thread::hardware_concurrency().
static_thread_pool ();
// Initialise the thread pool with the specified number of threads.
explicit static_thread_pool (std:: uint32_t threadCount);
std:: uint32_t thread_count () const noexcept ;
class schedule_operation
{
public:
schedule_operation (static_thread_pool* tp) noexcept ;
bool await_ready () noexcept ;
bool await_suspend (std::experimental::coroutine_handle<> h) noexcept ;
bool await_resume () noexcept ;
private:
// unspecified
};
// Return an operation that can be awaited by a coroutine.
//
//
[[nodiscard]]
schedule_operation schedule () noexcept ;
private:
// Unspecified
};
}예제 사용 : 간단합니다
cppcoro::task<std::string> do_something_on_threadpool (cppcoro::static_thread_pool& tp)
{
// First schedule the coroutine onto the threadpool.
co_await tp. schedule ();
// When it resumes, this coroutine is now running on the threadpool.
do_something ();
} 예제 사용 : 병렬로 작업 - schedule_on() 연산자를 사용하여 static_thread_pool 사용합니다.
cppcoro::task< double > dot_product (static_thread_pool& tp, double a[], double b[], size_t count)
{
if (count > 1000 )
{
// Subdivide the work recursively into two equal tasks
// The first half is scheduled to the thread pool so it can run concurrently
// with the second half which continues on this thread.
size_t halfCount = count / 2 ;
auto [first, second] = co_await when_all (
schedule_on (tp, dot_product (tp, a, b, halfCount),
dot_product (tp, a + halfCount, b + halfCount, count - halfCount));
co_return first + second;
}
else
{
double sum = 0.0 ;
for ( size_t i = 0 ; i < count; ++i)
{
sum += a[i] * b[i];
}
co_return sum;
}
}io_service 및 io_work_scope io_service 클래스는 비동기 I/O 작업에서 I/O 완료 이벤트를 처리하기위한 추상화를 제공합니다.
비동기 I/O 작동이 완료되면, 이벤트 처리 방법 중 하나의 호출 내부의 I/O 스레드에서 작업을 기다리는 코 루틴 : process_events() , process_pending_events() , process_one_event() 또는 process_one_pending_event() .
io_service 클래스는 I/O 스레드를 관리하지 않습니다. 일부 스레드는 I/O 완료 이벤트를 기다리고있는 코 루틴의 이벤트 처리 방법 중 하나를 호출해야합니다. 이것은 process_events() 호출하거나 process_pending_events() 또는 process_one_pending_event() 에 대한 호출을 통해 새 이벤트를 주기적으로 폴링하여 다른 이벤트 루프 (예 : UI 이벤트 루프)와 혼합 된 전용 스레드 일 수 있습니다.
이를 통해 io_service 이벤트 루프를 사용자 인터페이스 이벤트 루프와 같은 다른 이벤트 루프와 통합 할 수 있습니다.
여러 스레드가 process_events() 호출하여 여러 스레드에서 이벤트를 다중화 할 수 있습니다. 옵션 io_service 생성자 매개 변수를 통해 적극적으로 처리하는 이벤트가있는 최대 스레드 수에 대한 힌트를 지정할 수 있습니다.
Windows에서 구현은 Windows I/O 완료 포트 시설을 사용하여 이벤트를 확장 가능한 방식으로 I/O 스레드로 발송합니다.
API 요약 :
namespace cppcoro
{
class io_service
{
public:
class schedule_operation ;
class timed_schedule_operation ;
io_service ();
io_service (std:: uint32_t concurrencyHint);
io_service (io_service&&) = delete ;
io_service ( const io_service&) = delete ;
io_service& operator =(io_service&&) = delete ;
io_service& operator =( const io_service&) = delete ;
~io_service ();
// Scheduler methods
[[nodiscard]]
schedule_operation schedule () noexcept ;
template < typename REP, typename RATIO>
[[nodiscard]]
timed_schedule_operation schedule_after (
std::chrono::duration<REP, RATIO> delay,
cppcoro::cancellation_token cancellationToken = {}) noexcept ;
// Event-loop methods
//
// I/O threads must call these to process I/O events and execute
// scheduled coroutines.
std:: uint64_t process_events ();
std:: uint64_t process_pending_events ();
std:: uint64_t process_one_event ();
std:: uint64_t process_one_pending_event ();
// Request that all threads processing events exit their event loops.
void stop () noexcept ;
// Query if some thread has called stop()
bool is_stop_requested () const noexcept ;
// Reset the event-loop after a call to stop() so that threads can
// start processing events again.
void reset ();
// Reference-counting methods for tracking outstanding references
// to the io_service.
//
// The io_service::stop() method will be called when the last work
// reference is decremented.
//
// Use the io_work_scope RAII class to manage calling these methods on
// entry-to and exit-from a scope.
void notify_work_started () noexcept ;
void notify_work_finished () noexcept ;
};
class io_service ::schedule_operation
{
public:
schedule_operation ( const schedule_operation&) noexcept ;
schedule_operation& operator =( const schedule_operation&) noexcept ;
bool await_ready () const noexcept ;
void await_suspend (std::experimental::coroutine_handle<> awaiter) noexcept ;
void await_resume () noexcept ;
};
class io_service ::timed_schedule_operation
{
public:
timed_schedule_operation (timed_schedule_operation&&) noexcept ;
timed_schedule_operation ( const timed_schedule_operation&) = delete ;
timed_schedule_operation& operator =( const timed_schedule_operation&) = delete ;
timed_schedule_operation& operator =(timed_schedule_operation&&) = delete ;
bool await_ready () const noexcept ;
void await_suspend (std::experimental::coroutine_handle<> awaiter);
void await_resume ();
};
class io_work_scope
{
public:
io_work_scope (io_service& ioService) noexcept ;
io_work_scope ( const io_work_scope& other) noexcept ;
io_work_scope (io_work_scope&& other) noexcept ;
~io_work_scope ();
io_work_scope& operator =( const io_work_scope& other) noexcept ;
io_work_scope& operator =(io_work_scope&& other) noexcept ;
io_service& service () const noexcept ;
};
}예:
# include < cppcoro/task.hpp >
# include < cppcoro/task.hpp >
# include < cppcoro/io_service.hpp >
# include < cppcoro/read_only_file.hpp >
# include < experimental/filesystem >
# include < memory >
# include < algorithm >
# include < iostream >
namespace fs = std::experimental::filesystem;
cppcoro::task<std:: uint64_t > count_lines (cppcoro::io_service& ioService, fs::path path)
{
auto file = cppcoro::read_only_file::open (ioService, path);
constexpr size_t bufferSize = 4096 ;
auto buffer = std::make_unique<std:: uint8_t []>(bufferSize);
std:: uint64_t newlineCount = 0 ;
for (std:: uint64_t offset = 0 , fileSize = file. size (); offset < fileSize;)
{
const auto bytesToRead = static_cast < size_t >(
std::min<std:: uint64_t >(bufferSize, fileSize - offset));
const auto bytesRead = co_await file. read (offset, buffer. get (), bytesToRead);
newlineCount += std::count (buffer. get (), buffer. get () + bytesRead, ' n ' );
offset += bytesRead;
}
co_return newlineCount;
}
cppcoro::task<> run (cppcoro::io_service& ioService)
{
cppcoro::io_work_scope ioScope (ioService);
auto lineCount = co_await count_lines (ioService, fs::path{ " foo.txt " });
std::cout << " foo.txt has " << lineCount << " lines. " << std::endl;;
}
cppcoro::task<> process_events (cppcoro::io_service& ioService)
{
// Process events until the io_service is stopped.
// ie. when the last io_work_scope goes out of scope.
ioService. process_events ();
co_return ;
}
int main ()
{
cppcoro::io_service ioService;
cppcoro::sync_wait ( cppcoro::when_all_ready (
run (ioService),
process_events (ioService)));
return 0 ;
}io_service io_service 클래스는 Scheduler 및 DelayedScheduler 개념의 인터페이스를 구현합니다.
이를 통해 코 루틴은 현재 스레드에서 실행을 일시 중지하고 특정 io_service 객체와 관련된 I/O 스레드에서 재개를 예약 할 수 있습니다.
예:
cppcoro::task<> do_something (cppcoro::io_service& ioService)
{
// Coroutine starts execution on the thread of the task awaiter.
// A coroutine can transfer execution to an I/O thread by awaiting the
// result of io_service::schedule().
co_await ioService. schedule ();
// At this point, the coroutine is now executing on an I/O thread
// inside a call to one of the io_service event processing methods.
// A coroutine can also perform a delayed-schedule that will suspend
// the coroutine for a specified duration of time before scheduling
// it for resumption on an I/O thread.
co_await ioService. schedule_after (100ms);
// At this point, the coroutine is executing on a potentially different I/O thread.
}file , readable_file , writable_file이러한 유형은 콘크리트 파일 I/O를 수행하기위한 추상적 인 기본 클래스입니다.
API 요약 :
namespace cppcoro
{
class file_read_operation ;
class file_write_operation ;
class file
{
public:
virtual ~file ();
std:: uint64_t size () const ;
protected:
file (file&& other) noexcept ;
};
class readable_file : public virtual file
{
public:
[[nodiscard]]
file_read_operation read (
std:: uint64_t offset,
void * buffer,
std:: size_t byteCount,
cancellation_token ct = {}) const noexcept ;
};
class writable_file : public virtual file
{
public:
void set_size (std:: uint64_t fileSize);
[[nodiscard]]
file_write_operation write (
std:: uint64_t offset,
const void * buffer,
std:: size_t byteCount,
cancellation_token ct = {}) noexcept ;
};
class file_read_operation
{
public:
file_read_operation (file_read_operation&& other) noexcept ;
bool await_ready () const noexcept ;
bool await_suspend (std::experimental::coroutine_handle<> awaiter);
std:: size_t await_resume ();
};
class file_write_operation
{
public:
file_write_operation (file_write_operation&& other) noexcept ;
bool await_ready () const noexcept ;
bool await_suspend (std::experimental::coroutine_handle<> awaiter);
std:: size_t await_resume ();
};
}read_only_file , write_only_file , read_write_file이러한 유형은 구체적인 파일 I/O 클래스를 나타냅니다.
API 요약 :
namespace cppcoro
{
class read_only_file : public readable_file
{
public:
[[nodiscard]]
static read_only_file open (
io_service& ioService,
const std::experimental::filesystem::path& path,
file_share_mode shareMode = file_share_mode::read,
file_buffering_mode bufferingMode = file_buffering_mode::default_);
};
class write_only_file : public writable_file
{
public:
[[nodiscard]]
static write_only_file open (
io_service& ioService,
const std::experimental::filesystem::path& path,
file_open_mode openMode = file_open_mode::create_or_open,
file_share_mode shareMode = file_share_mode::none,
file_buffering_mode bufferingMode = file_buffering_mode::default_);
};
class read_write_file : public readable_file , public writable_file
{
public:
[[nodiscard]]
static read_write_file open (
io_service& ioService,
const std::experimental::filesystem::path& path,
file_open_mode openMode = file_open_mode::create_or_open,
file_share_mode shareMode = file_share_mode::none,
file_buffering_mode bufferingMode = file_buffering_mode::default_);
};
} 모든 open() 함수는 std::system_error 실패시합니다.
참고 : 네트워킹 추상화는 현재 Windows 플랫폼에서만 지원됩니다. Linux 지원이 곧 출시 될 예정입니다.
socket소켓 클래스는 네트워크를 통해 데이터를 비동기로 보내거나받는 데 사용될 수 있습니다.
현재 IPv4 및 IPv6을 통한 TCP/IP, UDP/IP 만 지원합니다.
API 요약 :
// <cppcoro/net/socket.hpp>
namespace cppcoro ::net
{
class socket
{
public:
static socket create_tcpv4 (ip_service& ioSvc);
static socket create_tcpv6 (ip_service& ioSvc);
static socket create_updv4 (ip_service& ioSvc);
static socket create_udpv6 (ip_service& ioSvc);
socket (socket&& other) noexcept ;
~socket ();
socket& operator =(socket&& other) noexcept ;
// Return the native socket handle for the socket
<platform-specific> native_handle () noexcept ;
const ip_endpoint& local_endpoint () const noexcept ;
const ip_endpoint& remote_endpoint () const noexcept ;
void bind ( const ip_endpoint& localEndPoint);
void listen ();
[[nodiscard]]
Awaitable< void > connect ( const ip_endpoint& remoteEndPoint) noexcept ;
[[nodiscard]]
Awaitable< void > connect ( const ip_endpoint& remoteEndPoint,
cancellation_token ct) noexcept ;
[[nodiscard]]
Awaitable< void > accept (socket& acceptingSocket) noexcept ;
[[nodiscard]]
Awaitable< void > accept (socket& acceptingSocket,
cancellation_token ct) noexcept ;
[[nodiscard]]
Awaitable< void > disconnect () noexcept ;
[[nodiscard]]
Awaitable< void > disconnect (cancellation_token ct) noexcept ;
[[nodiscard]]
Awaitable<std:: size_t > send ( const void * buffer, std:: size_t size) noexcept ;
[[nodiscard]]
Awaitable<std:: size_t > send ( const void * buffer,
std:: size_t size,
cancellation_token ct) noexcept ;
[[nodiscard]]
Awaitable<std:: size_t > recv ( void * buffer, std:: size_t size) noexcept ;
[[nodiscard]]
Awaitable<std:: size_t > recv ( void * buffer,
std:: size_t size,
cancellation_token ct) noexcept ;
[[nodiscard]]
socket_recv_from_operation recv_from (
void * buffer,
std:: size_t size) noexcept ;
[[nodiscard]]
socket_recv_from_operation_cancellable recv_from (
void * buffer,
std:: size_t size,
cancellation_token ct) noexcept ;
[[nodiscard]]
socket_send_to_operation send_to (
const ip_endpoint& destination,
const void * buffer,
std:: size_t size) noexcept ;
[[nodiscard]]
socket_send_to_operation_cancellable send_to (
const ip_endpoint& destination,
const void * buffer,
std:: size_t size,
cancellation_token ct) noexcept ;
void close_send ();
void close_recv ();
};
}예 : 에코 서버
# include < cppcoro/net/socket.hpp >
# include < cppcoro/io_service.hpp >
# include < cppcoro/cancellation_source.hpp >
# include < cppcoro/async_scope.hpp >
# include < cppcoro/on_scope_exit.hpp >
# include < memory >
# include < iostream >
cppcoro::task< void > handle_connection (socket s)
{
try
{
const size_t bufferSize = 16384 ;
auto buffer = std::make_unique< unsigned char []>(bufferSize);
size_t bytesRead;
do {
// Read some bytes
bytesRead = co_await s. recv (buffer. get (), bufferSize);
// Write some bytes
size_t bytesWritten = 0 ;
while (bytesWritten < bytesRead) {
bytesWritten += co_await s. send (
buffer. get () + bytesWritten,
bytesRead - bytesWritten);
}
} while (bytesRead != 0 );
s. close_send ();
co_await s. disconnect ();
}
catch (...)
{
std::cout << " connection failed " << std::
}
}
cppcoro::task< void > echo_server (
cppcoro::net::ipv4_endpoint endpoint,
cppcoro::io_service& ioSvc,
cancellation_token ct)
{
cppcoro::async_scope scope;
std::exception_ptr ex;
try
{
auto listeningSocket = cppcoro::net::socket::create_tcpv4 (ioSvc);
listeningSocket. bind (endpoint);
listeningSocket. listen ();
while ( true ) {
auto connection = cppcoro::net::socket::create_tcpv4 (ioSvc);
co_await listeningSocket. accept (connection, ct);
scope. spawn ( handle_connection ( std::move (connection)));
}
}
catch (cppcoro::operation_cancelled)
{
}
catch (...)
{
ex = std::current_exception ();
}
// Wait until all handle_connection tasks have finished.
co_await scope. join ();
if (ex) std::rethrow_exception (ex);
}
int main ( int argc, const char * argv[])
{
cppcoro::io_service ioSvc;
if (argc != 2 ) return - 1 ;
auto endpoint = cppcoro::ipv4_endpoint::from_string (argv[ 1 ]);
if (!endpoint) return - 1 ;
( void ) cppcoro::sync_wait ( cppcoro::when_all (
[&]() -> task<>
{
// Shutdown the event loop once finished.
auto stopOnExit = cppcoro::on_scope_exit ([&] { ioSvc. stop (); });
cppcoro::cancellation_source canceller;
co_await cppcoro::when_all (
[&]() -> task<>
{
// Run for 30s then stop accepting new connections.
co_await ioSvc. schedule_after ( std::chrono::seconds ( 30 ));
canceller. request_cancellation ();
}(),
echo_server (*endpoint, ioSvc, canceller. token ()));
}(),
[&]() -> task<>
{
ioSvc. process_events ();
}()));
return 0 ;
}ip_address , ipv4_address , ipv6_addressIP 주소를 나타내는 도우미 클래스.
API 시놉시스 :
namespace cppcoro ::net
{
class ipv4_address
{
using bytes_t = std:: uint8_t [ 4 ];
public:
constexpr ipv4_address ();
explicit constexpr ipv4_address (std:: uint32_t integer);
explicit constexpr ipv4_address ( const std::uint8_t (&bytes)[4]);
explicit constexpr ipv4_address (std:: uint8_t b0,
std:: uint8_t b1,
std:: uint8_t b2,
std:: uint8_t b3);
constexpr const bytes_t & bytes () const ;
constexpr std:: uint32_t to_integer () const ;
static constexpr ipv4_address loopback ();
constexpr bool is_loopback () const ;
constexpr bool is_private_network () const ;
constexpr bool operator ==(ipv4_address other) const ;
constexpr bool operator !=(ipv4_address other) const ;
constexpr bool operator <(ipv4_address other) const ;
constexpr bool operator >(ipv4_address other) const ;
constexpr bool operator <=(ipv4_address other) const ;
constexpr bool operator >=(ipv4_address other) const ;
std::string to_string ();
static std::optional<ipv4_address> from_string (std::string_view string) noexcept ;
};
class ipv6_address
{
using bytes_t = std:: uint8_t [ 16 ];
public:
constexpr ipv6_address ();
explicit constexpr ipv6_address (
std:: uint64_t subnetPrefix,
std:: uint64_t interfaceIdentifier);
constexpr ipv6_address (
std:: uint16_t part0,
std:: uint16_t part1,
std:: uint16_t part2,
std:: uint16_t part3,
std:: uint16_t part4,
std:: uint16_t part5,
std:: uint16_t part6,
std:: uint16_t part7);
explicit constexpr ipv6_address (
const std::uint16_t (&parts)[8]);
explicit constexpr ipv6_address (
const std::uint8_t (bytes)[16]);
constexpr const bytes_t & bytes () const ;
constexpr std:: uint64_t subnet_prefix () const ;
constexpr std:: uint64_t interface_identifier () const ;
static constexpr ipv6_address unspecified ();
static constexpr ipv6_address loopback ();
static std::optional<ipv6_address> from_string (std::string_view string) noexcept ;
std::string to_string () const ;
constexpr bool operator ==( const ipv6_address& other) const ;
constexpr bool operator !=( const ipv6_address& other) const ;
constexpr bool operator <( const ipv6_address& other) const ;
constexpr bool operator >( const ipv6_address& other) const ;
constexpr bool operator <=( const ipv6_address& other) const ;
constexpr bool operator >=( const ipv6_address& other) const ;
};
class ip_address
{
public:
// Constructs to IPv4 address 0.0.0.0
ip_address () noexcept ;
ip_address (ipv4_address address) noexcept ;
ip_address (ipv6_address address) noexcept ;
bool is_ipv4 () const noexcept ;
bool is_ipv6 () const noexcept ;
const ipv4_address& to_ipv4 () const ;
const ipv6_address& to_ipv6 () const ;
const std:: uint8_t * bytes () const noexcept ;
std::string to_string () const ;
static std::optional<ip_address> from_string (std::string_view string) noexcept ;
bool operator ==( const ip_address& rhs) const noexcept ;
bool operator !=( const ip_address& rhs) const noexcept ;
// ipv4_address sorts less than ipv6_address
bool operator <( const ip_address& rhs) const noexcept ;
bool operator >( const ip_address& rhs) const noexcept ;
bool operator <=( const ip_address& rhs) const noexcept ;
bool operator >=( const ip_address& rhs) const noexcept ;
};
}ip_endpoint , ipv4_endpoint ipv6_endpointIP 주소와 포트 번호를 나타내는 도우미 클래스.
API 시놉시스 :
namespace cppcoro ::net
{
class ipv4_endpoint
{
public:
ipv4_endpoint () noexcept ;
explicit ipv4_endpoint (ipv4_address address, std:: uint16_t port = 0 ) noexcept ;
const ipv4_address& address () const noexcept ;
std:: uint16_t port () const noexcept ;
std::string to_string () const ;
static std::optional<ipv4_endpoint> from_string (std::string_view string) noexcept ;
};
bool operator ==( const ipv4_endpoint& a, const ipv4_endpoint& b);
bool operator !=( const ipv4_endpoint& a, const ipv4_endpoint& b);
bool operator <( const ipv4_endpoint& a, const ipv4_endpoint& b);
bool operator >( const ipv4_endpoint& a, const ipv4_endpoint& b);
bool operator <=( const ipv4_endpoint& a, const ipv4_endpoint& b);
bool operator >=( const ipv4_endpoint& a, const ipv4_endpoint& b);
class ipv6_endpoint
{
public:
ipv6_endpoint () noexcept ;
explicit ipv6_endpoint (ipv6_address address, std:: uint16_t port = 0 ) noexcept ;
const ipv6_address& address () const noexcept ;
std:: uint16_t port () const noexcept ;
std::string to_string () const ;
static std::optional<ipv6_endpoint> from_string (std::string_view string) noexcept ;
};
bool operator ==( const ipv6_endpoint& a, const ipv6_endpoint& b);
bool operator !=( const ipv6_endpoint& a, const ipv6_endpoint& b);
bool operator <( const ipv6_endpoint& a, const ipv6_endpoint& b);
bool operator >( const ipv6_endpoint& a, const ipv6_endpoint& b);
bool operator <=( const ipv6_endpoint& a, const ipv6_endpoint& b);
bool operator >=( const ipv6_endpoint& a, const ipv6_endpoint& b);
class ip_endpoint
{
public:
// Constructs to IPv4 end-point 0.0.0.0:0
ip_endpoint () noexcept ;
ip_endpoint (ipv4_endpoint endpoint) noexcept ;
ip_endpoint (ipv6_endpoint endpoint) noexcept ;
bool is_ipv4 () const noexcept ;
bool is_ipv6 () const noexcept ;
const ipv4_endpoint& to_ipv4 () const ;
const ipv6_endpoint& to_ipv6 () const ;
ip_address address () const noexcept ;
std:: uint16_t port () const noexcept ;
std::string to_string () const ;
static std::optional<ip_endpoint> from_string (std::string_view string) noexcept ;
bool operator ==( const ip_endpoint& rhs) const noexcept ;
bool operator !=( const ip_endpoint& rhs) const noexcept ;
// ipv4_endpoint sorts less than ipv6_endpoint
bool operator <( const ip_endpoint& rhs) const noexcept ;
bool operator >( const ip_endpoint& rhs) const noexcept ;
bool operator <=( const ip_endpoint& rhs) const noexcept ;
bool operator >=( const ip_endpoint& rhs) const noexcept ;
};
}sync_wait() sync_wait() 함수는 지정된 awaitable 완성 될 때까지 동기 대기하는 데 사용될 수 있습니다.
지정된 기다릴 수있는 것은 새로 생성 된 코 루틴 내의 현재 스레드에서 co_await 됩니다.
sync_wait() 호출은 작업이 완료 될 때까지 차단되며 co_await 표현식의 결과를 반환하거나 co_await 표현식이 처리되지 않은 예외로 완료되면 예외를 재검토합니다.
sync_wait() 함수는 주로 main() 내에서 최상위 작업을 시작하고 작업이 완료 될 때까지 대기하는 데 주로 유용합니다. 실제로 첫 번째/최상위 레벨 task 시작하는 유일한 방법입니다.
API 요약 :
// <cppcoro/sync_wait.hpp>
namespace cppcoro
{
template < typename AWAITABLE>
auto sync_wait (AWAITABLE&& awaitable)
-> typename awaitable_traits<AWAITABLE&&>::await_result_t;
}예 :
void example_task ()
{
auto makeTask = []() -> task<std::string>
{
co_return " foo " ;
};
auto task = makeTask ();
// start the lazy task and wait until it completes
sync_wait (task); // -> "foo"
sync_wait ( makeTask ()); // -> "foo"
}
void example_shared_task ()
{
auto makeTask = []() -> shared_task<std::string>
{
co_return " foo " ;
};
auto task = makeTask ();
// start the shared task and wait until it completes
sync_wait (task) == " foo " ;
sync_wait ( makeTask ()) == " foo " ;
}when_all_ready() when_all_ready() 함수는 모든 입력이 차단 가능한 모든 입력이 완료 될 때 완료되는 새로운 차단 가능을 생성하는 데 사용될 수 있습니다.
입력 작업은 모든 유형의 기다릴 수 있습니다.
반환 된 대기 가능이 co_await ed 일 때, co_await 는 각각의 입력 차단 가능성이 차단 된 스레드를 순서대로 켜질 when_all_ready() 함수로 전달됩니다. 이러한 작업이 동시에 완료되지 않으면 동시에 실행됩니다.
입력 AWAITABLE에 대한 co_await 표현식이 모두 완료되면 반환 된 AWAITABLE가 완료되고 대기중인 Coroutine을 재개합니다. 기다리고있는 코 루틴은 마지막으로 완료된 입력의 실에서 재개됩니다.
반환 된 반품은 co_await 에드에 따라 예외를 던지지 않도록 보장됩니다.
그러나 when_all_ready() 호출 자체는 각 입력 차단 제품을 기다리는 데 필요한 코 루틴 프레임에 메모리를 할당 할 수없는 경우 std::bad_alloc 던질 수 있습니다. 입력이 차단 가능한 객체가 사본/이동 생성자에서 던지는 경우 예외가 발생할 수 있습니다.
co_await 의 반환 가능한 결과는 when_all_task<RESULT> 객체의 std::tuple 또는 std::vector 입니다. 이 객체를 사용하면 해당 출력 작업의 when_all_task<RESULT>::result() 메소드를 호출하여 별도로 볼 수있는 각 입력의 결과 (또는 예외)를 얻을 수 있습니다. 이를 통해 발신자는 동시에 여러 개의 차선을 기다릴 수 있으며 완료시 동기화 할 수 있습니다. 그 후에도 co_await 작업의 결과를 성공/실패에 대한 결과를 검사 할 수 있습니다.
이는 개별 co_await 작동의 실패로 인해 전체 작업이 예외로 실패하는 when_all() 와 다릅니다. 즉, co_await 작업이 실패한 구성 요소를 결정할 수 없으며 다른 co_await 작업의 결과를 얻지 못하게합니다.
API 요약 :
// <cppcoro/when_all_ready.hpp>
namespace cppcoro
{
// Concurrently await multiple awaitables.
//
// Returns an awaitable object that, when co_await'ed, will co_await each of the input
// awaitable objects and will resume the awaiting coroutine only when all of the
// component co_await operations complete.
//
// Result of co_await'ing the returned awaitable is a std::tuple of detail::when_all_task<T>,
// one for each input awaitable and where T is the result-type of the co_await expression
// on the corresponding awaitable.
//
// AWAITABLES must be awaitable types and must be movable (if passed as rvalue) or copyable
// (if passed as lvalue). The co_await expression will be executed on an rvalue of the
// copied awaitable.
template < typename ... AWAITABLES>
auto when_all_ready (AWAITABLES&&... awaitables)
-> Awaitable<std::tuple<detail::when_all_task<typename awaitable_traits<AWAITABLES>::await_result_t>...>>;
// Concurrently await each awaitable in a vector of input awaitables.
template <
typename AWAITABLE,
typename RESULT = typename awaitable_traits<AWAITABLE>:: await_result_t >
auto when_all_ready (std::vector<AWAITABLE> awaitables)
-> Awaitable<std::vector<detail::when_all_task<RESULT>>>;
}예제 사용 :
task<std::string> get_record ( int id);
task<> example1 ()
{
// Run 3 get_record() operations concurrently and wait until they're all ready.
// Returns a std::tuple of tasks that can be unpacked using structured bindings.
auto [task1, task2, task3] = co_await when_all_ready (
get_record ( 123 ),
get_record ( 456 ),
get_record ( 789 ));
// Unpack the result of each task
std::string& record1 = task1. result ();
std::string& record2 = task2. result ();
std::string& record3 = task3. result ();
// Use records....
}
task<> example2 ()
{
// Create the input tasks. They don't start executing yet.
std::vector<task<std::string>> tasks;
for ( int i = 0 ; i < 1000 ; ++i)
{
tasks. emplace_back ( get_record (i));
}
// Execute all tasks concurrently.
std::vector<detail::when_all_task<std::string>> resultTasks =
co_await when_all_ready ( std::move (tasks));
// Unpack and handle each result individually once they're all complete.
for ( int i = 0 ; i < 1000 ; ++i)
{
try
{
std::string& record = tasks[i]. result ();
std::cout << i << " = " << record << std::endl;
}
catch ( const std:: exception & ex)
{
std::cout << i << " : " << ex. what () << std::endl;
}
}
}when_all() when_all() 함수는 co_await ed가 각각의 입력 가능성을 동시에 co_await 동시에 반환하고 개별 결과의 집계를 반환하는 새로운 차단 가능성을 생성하는 데 사용될 수 있습니다.
반환 된 대기 가능이 기다리고 있으면 현재 스레드의 각 입력 차단 제품이 co_await 됩니다. 첫 번째로 기다릴 수있는 정지가 끝나면 두 번째 작업이 시작됩니다. 운영은 모두 완료 될 때까지 동시에 실행됩니다.
모든 구성 요소 co_await 작업이 완료되면 각 개별 결과에서 결과의 집계가 구성됩니다. 입력 작업에 의해 예외가 발생하거나 집계 결과의 구성이 예외를 던지면 예외는 반환 된 대기 가능한 co_await 에서 전파됩니다.
여러 co_await 작업이 예외로 실패하면 예외 중 하나는 co_await when_all() 에서 전파됩니다. 어떤 작업의 예외가 선택 될지 지정되지 않았습니다.
어떤 구성 요소 co_await 작동이 실패했는지 또는 다른 작업 결과를 얻을 수있는 능력을 유지하는 것이 중요하다면, 일부는 실패하더라도 when_all_ready() 작업의 결과를 얻을 수 있습니다.
API 요약 :
// <cppcoro/when_all.hpp>
namespace cppcoro
{
// Variadic version.
//
// Note that if the result of `co_await awaitable` yields a void-type
// for some awaitables then the corresponding component for that awaitable
// in the tuple will be an empty struct of type detail::void_value.
template < typename ... AWAITABLES>
auto when_all (AWAITABLES&&... awaitables)
-> Awaitable<std::tuple<typename awaitable_traits<AWAITABLES>::await_result_t...>>;
// Overload for vector<Awaitable<void>>.
template <
typename AWAITABLE,
typename RESULT = typename awaitable_traits<AWAITABLE>:: await_result_t ,
std:: enable_if_t <std::is_void_v<RESULT>, int > = 0 >
auto when_all (std::vector<AWAITABLE> awaitables)
-> Awaitable<void>;
// Overload for vector<Awaitable<NonVoid>> that yield a value when awaited.
template <
typename AWAITABLE,
typename RESULT = typename awaitable_traits<AWAITABLE>:: await_result_t ,
std:: enable_if_t <!std::is_void_v<RESULT>, int > = 0 >
auto when_all (std::vector<AWAITABLE> awaitables)
-> Awaitable<std::vector<std::conditional_t<
std::is_lvalue_reference_v<RESULT>,
std::reference_wrapper<std::remove_reference_t<RESULT>>,
std::remove_reference_t<RESULT>>>>;
}예 :
task<A> get_a ();
task<B> get_b ();
task<> example1 ()
{
// Run get_a() and get_b() concurrently.
// Task yields a std::tuple<A, B> which can be unpacked using structured bindings.
auto [a, b] = co_await when_all ( get_a (), get_b ());
// use a, b
}
task<std::string> get_record ( int id);
task<> example2 ()
{
std::vector<task<std::string>> tasks;
for ( int i = 0 ; i < 1000 ; ++i)
{
tasks. emplace_back ( get_record (i));
}
// Concurrently execute all get_record() tasks.
// If any of them fail with an exception then the exception will propagate
// out of the co_await expression once they have all completed.
std::vector<std::string> records = co_await when_all ( std::move (tasks));
// Process results
for ( int i = 0 ; i < 1000 ; ++i)
{
std::cout << i << " = " << records[i] << std::endl;
}
}fmap() fmap() 함수는 컨테이너 유형 내에 포함 된 값에 호출 기능을 적용하는 데 사용될 수 있으며, 함유 된 값을 적용한 결과의 새로운 컨테이너 유형을 반환합니다.
fmap() 함수는 유형 generator<T> , recursive_generator<T> 및 async_generator<T> 의 값과 차단 Awaitable 개념 (예 : task<T> )의 값에 함수를 적용 할 수 있습니다.
이러한 각 유형은 fmap() 에 대한 오버로드를 제공하여 두 인수를 취합니다. 적용 할 함수 및 컨테이너 값. 지원되는 fmap() 과부하의 각 유형에 대한 문서를 참조하십시오.
예를 들어, fmap() 함수는 task<T> 의 최종 결과에 함수를 적용하는 데 사용될 수 있으며, 함수의 반환 값으로 완료되는 새로운 task<U> 생성 할 수 있습니다.
// Given a function you want to apply that converts
// a value of type A to value of type B.
B a_to_b (A value);
// And a task that yields a value of type A
cppcoro::task<A> get_an_a ();
// We can apply the function to the result of the task using fmap()
// and obtain a new task yielding the result.
cppcoro::task<B> bTask = fmap(a_to_b, get_an_a());
// An alternative syntax is to use the pipe notation.
cppcoro::task<B> bTask = get_an_a() | cppcoro::fmap(a_to_b);API 요약 :
// <cppcoro/fmap.hpp>
namespace cppcoro
{
template < typename FUNC>
struct fmap_transform
{
fmap_transform (FUNC&& func) noexcept (std::is_nothrow_move_constructible_v<FUNC>);
FUNC func;
};
// Type-deducing constructor for fmap_transform object that can be used
// in conjunction with operator|.
template < typename FUNC>
fmap_transform<FUNC> fmap (FUNC&& func);
// operator| overloads for providing pipe-based syntactic sugar for fmap()
// such that the expression:
// <value-expr> | cppcoro::fmap(<func-expr>)
// is equivalent to:
// fmap(<func-expr>, <value-expr>)
template < typename T, typename FUNC>
decltype ( auto ) operator |(T&& value, fmap_transform<FUNC>&& transform);
template < typename T, typename FUNC>
decltype ( auto ) operator |(T&& value, fmap_transform<FUNC>& transform);
template < typename T, typename FUNC>
decltype ( auto ) operator |(T&& value, const fmap_transform<FUNC>& transform);
// Generic overload for all awaitable types.
//
// Returns an awaitable that when co_awaited, co_awaits the specified awaitable
// and applies the specified func to the result of the 'co_await awaitable'
// expression as if by 'std::invoke(func, co_await awaitable)'.
//
// If the type of 'co_await awaitable' expression is 'void' then co_awaiting the
// returned awaitable is equivalent to 'co_await awaitable, func()'.
template <
typename FUNC,
typename AWAITABLE,
std:: enable_if_t <is_awaitable_v<AWAITABLE>, int > = 0 >
auto fmap (FUNC&& func, AWAITABLE&& awaitable)
-> Awaitable<std::invoke_result_t<FUNC, typename awaitable_traits<AWAITABLE>::await_result_t>>;
} fmap() 함수는 ADL (Argument-Dependent Lookup)에 의해 올바른 오버로드를 조회하도록 설계되었으므로 일반적으로 cppcoro:: prefix없이 호출해야합니다.
resume_on() resume_on() 함수는 기다릴 때 차단 된 코 루틴을 재개 할 수있는 실행 컨텍스트를 제어하는 데 사용될 수 있습니다. async_generator 에 적용되면 co_await g.begin() 및 co_await ++it 작업이 대기중인 코 루틴을 재개합니다.
일반적으로 차단 가능한 (예 : task ) 또는 async_generator 의 대기중인 코 루틴은 작업이 완료된 스레드에서 실행을 재개합니다. 경우에 따라 이것은 계속 실행하려는 스레드가 아닐 수도 있습니다. 이 경우 resume_on() 함수를 사용하여 지정된 스케줄러와 관련된 스레드에서 실행을 재개하는 새로운 차단 가능 또는 생성기를 생성 할 수 있습니다.
resume_on() 함수는 새로운 차단 가능/생성기를 반환하는 일반적인 함수로 사용할 수 있습니다. 또는 파이프 라인 syntax에서 사용할 수 있습니다.
예:
task<record> load_record ( int id);
ui_thread_scheduler uiThreadScheduler;
task<> example ()
{
// This will start load_record() on the current thread.
// Then when load_record() completes (probably on an I/O thread)
// it will reschedule execution onto thread pool and call to_json
// Once to_json completes it will transfer execution onto the
// ui thread before resuming this coroutine and returning the json text.
task<std::string> jsonTask =
load_record ( 123 )
| cppcoro::resume_on ( threadpool::default ())
| cppcoro::fmap (to_json)
| cppcoro::resume_on (uiThreadScheduler);
// At this point, all we've done is create a pipeline of tasks.
// The tasks haven't started executing yet.
// Await the result. Starts the pipeline of tasks.
std::string jsonText = co_await jsonTask;
// Guaranteed to be executing on ui thread here.
someUiControl. set_text (jsonText);
}API 요약 :
// <cppcoro/resume_on.hpp>
namespace cppcoro
{
template < typename SCHEDULER, typename AWAITABLE>
auto resume_on (SCHEDULER& scheduler, AWAITABLE awaitable)
-> Awaitable<typename awaitable_traits<AWAITABLE>::await_traits_t>;
template < typename SCHEDULER, typename T>
async_generator<T> resume_on (SCHEDULER& scheduler, async_generator<T> source);
template < typename SCHEDULER>
struct resume_on_transform
{
explicit resume_on_transform (SCHEDULER& scheduler) noexcept ;
SCHEDULER& scheduler;
};
// Construct a transform/operation that can be applied to a source object
// using "pipe" notation (ie. operator|).
template < typename SCHEDULER>
resume_on_transform<SCHEDULER> resume_on (SCHEDULER& scheduler) noexcept ;
// Equivalent to 'resume_on(transform.scheduler, std::forward<T>(value))'
template < typename T, typename SCHEDULER>
decltype ( auto ) operator |(T&& value, resume_on_transform<SCHEDULER> transform)
{
return resume_on (transform. scheduler , std::forward<T>(value));
}
}schedule_on() schedule_on() 함수는 주어진 주어진 AWAITABLE 또는 async_generator 실행되기 시작하는 실행 컨텍스트를 변경하는 데 사용될 수 있습니다.
async_generator 에 적용되면 co_yield 문 후에 재개되는 실행 컨텍스트에도 영향을 미칩니다.
schedule_on Transform은 차단 가능 또는 async_generator 차단 가능 또는 발전기의 구현에 달려있는 스레드를 지정하지 않습니다.
작업이 완료되는 스레드를 제어하는 변환은 resume_on() 연산자를 참조하십시오.
예를 들어:
task< int > get_value ();
io_service ioSvc;
task<> example ()
{
// Starts executing get_value() on the current thread.
int a = co_await get_value ();
// Starts executing get_value() on a thread associated with ioSvc.
int b = co_await schedule_on (ioSvc, get_value ());
}API 요약 :
// <cppcoro/schedule_on.hpp>
namespace cppcoro
{
// Return a task that yields the same result as 't' but that
// ensures that 't' is co_await'ed on a thread associated with
// the specified scheduler. Resulting task will complete on
// whatever thread 't' would normally complete on.
template < typename SCHEDULER, typename AWAITABLE>
auto schedule_on (SCHEDULER& scheduler, AWAITABLE awaitable)
-> Awaitable<typename awaitable_traits<AWAITABLE>::await_result_t>;
// Return a generator that yields the same sequence of results as
// 'source' but that ensures that execution of the coroutine starts
// execution on a thread associated with 'scheduler' and resumes
// after a 'co_yield' on a thread associated with 'scheduler'.
template < typename SCHEDULER, typename T>
async_generator<T> schedule_on (SCHEDULER& scheduler, async_generator<T> source);
template < typename SCHEDULER>
struct schedule_on_transform
{
explicit schedule_on_transform (SCHEDULER& scheduler) noexcept ;
SCHEDULER& scheduler;
};
template < typename SCHEDULER>
schedule_on_transform<SCHEDULER> schedule_on (SCHEDULER& scheduler) noexcept ;
template < typename T, typename SCHEDULER>
decltype ( auto ) operator |(T&& value, schedule_on_transform<SCHEDULER> transform);
}awaitable_traits<T> 이 템플릿 metafunction은 유형 T 의 표현에 적용될 경우 co_await 표현식의 결과 유형을 결정하는 데 사용될 수 있습니다.
이것은 Coroutine의 약속 객체에 의해 적용되는 await_transform 의 영향을받지 않는 컨텍스트에서 T 형의 값이 기다리고 있다고 가정합니다. 그러한 맥락에서 유형 T 값이 기다리고있는 경우 결과가 다를 수 있습니다.
awaitable_traits<T> TEMPLATE MetAfunction은 유형 ( T )이 기다릴 수없는 경우 awaiter_t 또는 await_result_t 중첩 된 typedef를 정의하지 않습니다. 이를 통해 T 기다릴 수없는 경우 과부하를 비활성화하는 Sfinae 컨텍스트에서 사용할 수 있습니다.
API 요약 :
// <cppcoro/awaitable_traits.hpp>
namespace cppcoro
{
template < typename T>
struct awaitable_traits
{
// The type that results from applying `operator co_await()` to a value
// of type T, if T supports an `operator co_await()`, otherwise is type `T&&`.
typename awaiter_t = <unspecified>;
// The type of the result of co_await'ing a value of type T.
typename await_result_t = <unspecified>;
};
}is_awaitable<T> is_awaitable<T> 템플릿 metafunction을 사용하면 주어진 유형이 코 루틴 내에서 co_await ed 될 수 있는지 여부를 쿼리 할 수 있습니다.
API 요약 :
// <cppcoro/is_awaitable.hpp>
namespace cppcoro
{
template < typename T>
struct is_awaitable : std::bool_constant<...>
{};
template < typename T>
constexpr bool is_awaitable_v = is_awaitable<T>::value;
}Awaitable<T> 개념 Awaitable<T> await_transform 오버로드가없고 co_await 표현식의 결과에 type type, T 가지고 있음을 알리는 코 루틴 컨텍스트에서 유형이 co_await 될 수 있음을 나타내는 개념입니다.
예를 들어, 유형 task<T> 개념을 Awaitable<T&&> 개념을 구현하는 반면, 타입 task<T>& 개념을 Awaitable<T&> 수 있습니다.
Awaiter<T> 개념 Awaiter<T> 유형에 await_ready , await_suspend 및 await_resume 메소드가 포함되어 있음을 나타내는 개념입니다.
Awaiter<T> 만족시키는 유형은 유형의 경우 awaiter 사용해야합니다.
awaiter.await_ready() -> boolawaiter.await_suspend(std::experimental::coroutine_handle<void>{}) -> void 또는 bool 또는 std::experimental::coroutine_handle<P> P .awaiter.await_resume() -> T Awaiter<T> 개념을 구현하는 모든 유형은 또한 Awaitable<T> 개념을 구현합니다.
Scheduler 개념 Scheduler 는 일부 실행 컨텍스트 내에서 코 루틴을 실행할 수있는 개념입니다.
concept Scheduler
{
Awaitable< void > schedule ();
} 유형 S 주어지면 Scheduler 개념과 S 의 s 를 구현합니다.
s.schedule() 메소드는 co_await s.schedule() 현재 코 루틴을 무조건적으로 중단하고 s 와 관련된 실행 컨텍스트에서 재개를 예약 할 수 있도록 차단 가능한 유형을 반환합니다.co_await s.schedule() expression의 결과는 유형 void 가지고 있습니다. cppcoro::task<> f (Scheduler& scheduler)
{
// Execution of the coroutine is initially on the caller's execution context.
// Suspends execution of the coroutine and schedules it for resumption on
// the scheduler's execution context.
co_await scheduler. schedule ();
// At this point the coroutine is now executing on the scheduler's
// execution context.
}DelayedScheduler 자 개념 DelayedScheduler 지정된 시간 기간이 경과 한 후 코 루틴이 스케줄러의 실행 컨텍스트에서 실행을 위해 스스로 일정을 잡을 수있는 개념입니다.
concept DelayedScheduler : Scheduler
{
template < typename REP, typename RATIO>
Awaitable< void > schedule_after (std::chrono::duration<REP, RATIO> delay);
template < typename REP, typename RATIO>
Awaitable< void > schedule_after (
std::chrono::duration<REP, RATIO> delay,
cppcoro::cancellation_token cancellationToken);
} 유형 S 주어지면 DelayedScheduler 기와 인스턴스, s of type S 를 구현합니다.
s.schedule_after(delay) 메소드는 co_await s.schedule_after(delay) s 과 관련된 실행 컨텍스트에서 재개를 위해 코 루틴을 예약하기 전에 delay 시간 동안 현재 코 루틴을 일시 할 수 있도록 기다릴 수있는 객체를 반환합니다.co_await s.schedule_after(delay) 표현식에는 유형 void 가 있습니다.CPPCoro 라이브러리는 Visual Studio 2017과 Linux가있는 Windows의 건물을 지원합니다.
이 라이브러리는 케이크 빌드 시스템을 사용합니다 (아니요, C# 1이 아닙니다).
케이크 빌드 시스템은 GIT 하위 모듈로 자동으로 체크 아웃하므로 별도로 다운로드하거나 설치할 필요가 없습니다.
이 라이브러리에는 현재 Visual Studio 2017 이상이 필요하며 Windows 10 SDK가 필요합니다.
Clang (#3) 및 Linux (#15)에 대한 지원이 계획되어 있습니다.
Cake Build-System은 Python으로 구현되며 Python 2.7을 설치해야합니다.
Python 2.7 통역사가 귀하의 경로에 있으며 'Python'으로 제공되는지 확인하십시오.
Visual Studio 2017 업데이트 3 이상이 설치되었는지 확인하십시오. 업데이트 2 이하의 코 루틴에는 업데이트 3에서 수정 된 몇 가지 알려진 문제가 있습니다.
https://vcppdogfooding.azurewebsites.net/에서 nuget 패키지를 다운로드하여 .nuget 파일을 디렉토리로 풀어서 Visual Studio Compiler의 실험 버전을 사용할 수도 있습니다. 다음 줄을 수정하고 무책임하여 압축되지 않은 위치를 가리 키도록 config.cake 파일을 업데이트하십시오.
nugetPath = None # r'C:PathToVisualCppTools.14.0.25224-Pre'Windows 10 SDK가 설치되어 있는지 확인하십시오. 기본적으로 최신 Windows 10 SDK 및 범용 C 런타임 버전을 사용합니다.
CPPCoro 저장소는 GIT 서브 모듈을 사용하여 케이크 빌드 시스템의 소스를 끌어냅니다.
즉, --recursive 플래그를 git clone 명령에 전달해야합니다. 예를 들어.
c:Code> git clone --recursive https://github.com/lewissbaker/cppcoro.git
CPPCoro를 이미 복제 한 경우 변경 사항을 가져 오면 서브 모듈을 업데이트해야합니다.
c:Codecppcoro> git submodule update --init --recursive
명령 줄에서 빌드하려면 작업 공간 루트에서 'Cake.bat'을 실행하십시오.
예를 들어.
C:cppcoro> cake.bat
Building with C:cppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')
Building with C:cppcoroconfig.cake - Variant(release='optimised', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
Building with C:cppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
Building with C:cppcoroconfig.cake - Variant(release='optimised', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')
Compiling testmain.cpp
Compiling testmain.cpp
Compiling testmain.cpp
Compiling testmain.cpp
...
Linking buildwindows_x86_msvc14.10_debugtestrun.exe
Linking buildwindows_x64_msvc14.10_optimisedtestrun.exe
Linking buildwindows_x86_msvc14.10_optimisedtestrun.exe
Linking buildwindows_x64_msvc14.10_debugtestrun.exe
Generating code
Finished generating code
Generating code
Finished generating code
Build succeeded.
Build took 0:00:02.419.
기본적으로, 인수가없는 cake 실행하면 모든 빌드 변형이있는 모든 프로젝트를 구축하고 단위 테스트를 실행합니다. 추가 명령 줄 인수를 전달하여 구축 된 것을 좁힐 수 있습니다. 예를 들어.
c:cppcoro> cake.bat release=debug architecture=x64 lib/build.cake
Building with C:UsersLewisCodecppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
Archiving buildwindows_x64_msvc14.10_debuglibcppcoro.lib
Build succeeded.
Build took 0:00:00.321.
cake --help 실행하여 사용 가능한 명령 줄 옵션을 나열 할 수 있습니다.
Visual Studio 내에서 개발하려면 cake.bat -p 실행하여 .vcproj/.sln 파일을 구축 할 수 있습니다.
예를 들어.
c:cppcoro> cake.bat -p
Building with C:cppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')
Building with C:cppcoroconfig.cake - Variant(release='optimised', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
Building with C:cppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
Building with C:cppcoroconfig.cake - Variant(release='optimised', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')
Generating Solution build/project/cppcoro.sln
Generating Project build/project/cppcoro_tests.vcxproj
Generating Filters build/project/cppcoro_tests.vcxproj.filters
Generating Project build/project/cppcoro.vcxproj
Generating Filters build/project/cppcoro.vcxproj.filters
Build succeeded.
Build took 0:00:00.247.
Visual Studio 내에서 이러한 프로젝트를 구축하면 컴파일을 수행하기 위해 Cake에 호출됩니다.
CPPCoro 프로젝트는 Linux에서 Clang+ Libc ++ 5.0 이상을 사용하여 구축 할 수 있습니다.
CPPCoro 구축은 Ubuntu 17.04에 따라 테스트되었습니다.
다음 패키지가 설치되어 있는지 확인하십시오.
이것은 Clang과 Libc ++가 구축 및 설치되었다고 가정합니다.
Clang이 아직 구성되지 않은 경우 CPPCoro를 사용하여 Clang 설정에 대한 자세한 내용은 다음 섹션을 참조하십시오.
체크 아웃 CPPCORO 및 그 하위 모듈 :
git clone --recursive https://github.com/lewissbaker/cppcoro.git cppcoro
init.sh 실행하려면 cake 배쉬 기능을 설정하십시오.
cd cppcoro
source init.sh
그런 다음 작업 공간 루트에서 cake 실행하여 CPPCoro를 구축하고 테스트를 실행할 수 있습니다.
$ cake
추가 명령 줄 인수를 지정하여 빌드를 사용자 정의 할 수 있습니다.
--help 명령 줄 인수에 대한 도움말을 인쇄합니다--debug=run 빌드 명령 선이 실행되는 것을 보여줍니다release=debug 또는 release=optimised 빌드 변형을 디버그 또는 최적화로 제한합니다 (기본적으로 두 가지 모두 빌드됩니다).lib/build.cake 테스트가 아닌 CPPCoro 라이브러리를 구축합니다.test/build.cake@task_tests.cpp 특정 소스 파일 만 컴파일됩니다test/build.cake@testresult 테스트를 구축하고 실행합니다예를 들어:
$ cake --debug=run release=debug lib/build.cake
Clang 컴파일러가 /usr/bin/clang 에 있지 않은 경우 cake 에 대한 다음 명령 줄 옵션 중 하나 이상을 사용하여 대체 위치를 지정할 수 있습니다.
--clang-executable=<name> clang 대신 사용할 Clang 실행 가능 이름을 지정하십시오. 예를 들어. Clang 8.0 Pass의 사용을 강제하기 위해 --clang-executable=clang-8--clang-executable=<abspath> -Clang 실행 가능에 대한 전체 경로를 지정하십시오. 빌드 시스템은 또한 동일한 디렉토리에서 다른 실행 파일을 찾습니다. 이 경로에 <prefix>/bin/<name> 양식이 있으면 기본 Clang-Install-Prefix를 <prefix> 로 설정합니다.--clang-install-prefix=<path> -Clang이 설치된 경로를 지정하십시오. 이로 인해 빌드 시스템이 --clang-executable 에 의해 재정의되지 않는 한 <path>/bin 아래에서 Clang을 찾게됩니다.--libcxx-install-prefix=<path> -libc ++가 설치된 경로를 지정하십시오. 기본적으로 빌드 시스템은 Clang과 동일한 위치에서 libc ++를 찾습니다. 다른 위치에 설치된 경우이 명령 줄 옵션을 사용하십시오.예 : 기본 위치에 설치된 Clang의 특정 버전 사용
$ cake --clang-executable=clang-8
예 : 사용자 정의 위치에서 Clang의 기본 버전 사용
$ cake --clang-install-prefix=/path/to/clang-install
예 : 다른 위치에서 libc ++와 함께 사용자 정의 위치에있는 특정 버전의 Clang 사용
$ cake --clang-executable=/path/to/clang-install/bin/clang-8 --libcxx-install-prefix=/path/to/libcxx-install
If your Linux distribution does not have a version of Clang 5.0 or later available, you can install a snapshot build from the LLVM project.
Follow instructions at http://apt.llvm.org/ to setup your package manager to support pulling from the LLVM package manager.
For example, for Ubuntu 17.04 Zesty:
Edit /etc/apt/sources.list and add the following lines:
deb http://apt.llvm.org/zesty/ llvm-toolchain-zesty main
deb-src http://apt.llvm.org/zesty/ llvm-toolchain-zesty main
Install the PGP key for those packages:
$ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
Install Clang and LLD:
$ sudo apt-get install clang-6.0 lld-6.0
The LLVM snapshot builds do not include libc++ versions so you'll need to build that yourself. 아래를 참조하십시오.
You can also use the bleeding-edge Clang version by building Clang from source yourself.
See instructions here:
To do this you will need to install the following pre-requisites:
$ sudo apt-get install git cmake ninja-build clang lld
Note that we are using your distribution's version of clang to build clang from source. GCC could also be used here instead.
Checkout LLVM + Clang + LLD + libc++ repositories:
mkdir llvm
cd llvm
git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm
git clone --depth=1 https://github.com/llvm-mirror/clang.git llvm/tools/clang
git clone --depth=1 https://github.com/llvm-mirror/lld.git llvm/tools/lld
git clone --depth=1 https://github.com/llvm-mirror/libcxx.git llvm/projects/libcxx
ln -s llvm/tools/clang clang
ln -s llvm/tools/lld lld
ln -s llvm/projects/libcxx libcxx
Configure and build Clang:
mkdir clang-build
cd clang-build
cmake -GNinja
-DCMAKE_CXX_COMPILER=/usr/bin/clang++
-DCMAKE_C_COMPILER=/usr/bin/clang
-DCMAKE_BUILD_TYPE=MinSizeRel
-DCMAKE_INSTALL_PREFIX="/path/to/clang/install"
-DCMAKE_BUILD_WITH_INSTALL_RPATH="yes"
-DLLVM_TARGETS_TO_BUILD=X86
-DLLVM_ENABLE_PROJECTS="lld;clang"
../llvm
ninja install-clang
install-clang-headers
install-llvm-ar
install-lld
The cppcoro project requires libc++ as it contains the <experimental/coroutine> header required to use C++ coroutines under Clang.
Checkout libc++ + llvm :
mkdir llvm
cd llvm
git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm
git clone --depth=1 https://github.com/llvm-mirror/libcxx.git llvm/projects/libcxx
ln -s llvm/projects/libcxx libcxx
Build libc++ :
mkdir libcxx-build
cd libcxx-build
cmake -GNinja
-DCMAKE_CXX_COMPILER="/path/to/clang/install/bin/clang++"
-DCMAKE_C_COMPILER="/path/to/clang/install/bin/clang"
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_INSTALL_PREFIX="/path/to/clang/install"
-DLLVM_PATH="../llvm"
-DLIBCXX_CXX_ABI=libstdc++
-DLIBCXX_CXX_ABI_INCLUDE_PATHS="/usr/include/c++/6.3.0/;/usr/include/x86_64-linux-gnu/c++/6.3.0/"
../libcxx
ninja cxx
ninja install
This will build and install libc++ into the same install directory where you have clang installed.
The cppcoro port in vcpkg is kept up to date by Microsoft team members and community contributors. The url of vcpkg is: https://github.com/Microsoft/vcpkg . You can download and install cppcoro using the vcpkg dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh # ./bootstrap-vcpkg.bat for Windows
./vcpkg integrate install
./vcpkg install cppcoro버전이 오래된 경우 VCPKG 저장소에서 문제를 만들거나 요청을 가져 오십시오.
GitHub issues are the primary mechanism for support, bug reports and feature requests.
Contributions are welcome and pull-requests will be happily reviewed. I only ask that you agree to license any contributions that you make under the MIT license.
If you have general questions about C++ coroutines, you can generally find someone to help in the #coroutines channel on Cpplang Slack group.