Perpustakaan 'CPPCORO' menyediakan serangkaian besar primitif tujuan umum untuk memanfaatkan proposal Coroutines TS yang dijelaskan dalam N4680.
Ini termasuk:
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 dan 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>SchedulerDelayedSchedulerPerpustakaan ini adalah perpustakaan eksperimental yang sedang mengeksplorasi ruang abstraksi pemrograman asinkron berkinerja tinggi dan dapat diskalakan yang dapat dibangun di atas proposal C ++ coroutine.
Telah bersumber terbuka dengan harapan bahwa orang lain akan menganggapnya berguna dan bahwa komunitas C ++ dapat memberikan umpan balik tentang hal itu dan cara-cara untuk memperbaikinya.
Itu membutuhkan kompiler yang mendukung TS Coroutines:
Versi Linux fungsional kecuali untuk kelas terkait io_context dan file I/O yang belum diimplementasikan untuk Linux (lihat Edisi #15 untuk info lebih lanjut).
task<T>Tugas merupakan perhitungan asinkron yang dieksekusi dengan malas karena pelaksanaan coroutine tidak dimulai sampai tugas ditunggu.
Contoh:
# 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;
}Tinjauan 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);
} Anda dapat membuat objek task<T> dengan memanggil fungsi Coroutine yang mengembalikan task<T> .
Coroutine harus berisi penggunaan co_await atau co_return . Perhatikan bahwa task<T> Coroutine mungkin tidak menggunakan kata kunci co_yield .
Ketika coroutine yang mengembalikan task<T> dipanggil, bingkai coroutine dialokasikan jika perlu dan parameter ditangkap dalam bingkai coroutine. Coroutine ditangguhkan pada awal tubuh Coroutine dan eksekusi dikembalikan ke penelepon dan nilai task<T> yang mewakili perhitungan asinkron dikembalikan dari panggilan fungsi.
Badan Coroutine akan mulai mengeksekusi ketika nilai task<T> co_await ED. Ini akan menangguhkan coroutine yang menunggu dan memulai eksekusi coroutine yang terkait dengan nilai task<T> yang ditunggu.
Coroutine yang menunggu nantinya akan dilanjutkan di utas yang menyelesaikan eksekusi Coroutine task<T> yang ditunggu -tunggu. yaitu. Utas yang mengeksekusi co_return atau yang melempar pengecualian yang tidak ditangani yang mengakhiri eksekusi coroutine.
Jika tugas telah berjalan hingga selesai maka menunggu lagi akan mendapatkan hasil yang sudah komputasi tanpa menangguhkan coroutine yang menunggu.
Jika objek task dihancurkan sebelum ditunggu maka coroutine tidak pernah mengeksekusi dan destruktor hanya menghancurkan parameter yang ditangkap dan membebaskan memori apa pun yang digunakan oleh bingkai coroutine.
shared_task<T> Kelas shared_task<T> adalah tipe coroutine yang menghasilkan nilai tunggal secara tidak sinkron.
Ini 'malas' dalam eksekusi tugas itu tidak dimulai sampai ditunggu oleh beberapa coroutine.
Ini 'dibagikan' karena nilai tugas dapat disalin, memungkinkan banyak referensi untuk hasil tugas yang akan dibuat. Ini juga memungkinkan beberapa coroutine untuk secara bersamaan menunggu hasilnya.
Tugas akan mulai mengeksekusi di utas yang pertama co_await tugas. Awaiters berikutnya akan ditangguhkan dan diantar untuk dimulainya kembali ketika tugas selesai atau akan terus secara serempak jika tugas telah berjalan hingga selesai.
Jika Awaiter ditangguhkan saat menunggu tugas menyelesaikan maka akan dilanjutkan di utas yang menyelesaikan eksekusi tugas. yaitu. Utas yang mengeksekusi co_return atau yang melempar pengecualian yang tidak ditangani yang mengakhiri eksekusi coroutine.
Ringkasan 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);
} Semua metode const di shared_task<T> aman untuk dihubungi secara bersamaan dengan metodasi const lain pada contoh yang sama dari beberapa utas. Tidak aman untuk memanggil metode non-konstan shared_task<T> secara bersamaan dengan metode lain pada contoh yang sama dari shared_task<T> .
task<T> Kelas shared_task<T> mirip dengan task<T> karena tugas tidak segera memulai eksekusi setelah fungsi coroutine dipanggil. Tugas hanya mulai mengeksekusi ketika pertama kali ditunggu.
Ini berbeda dari task<T> karena objek tugas yang dihasilkan dapat disalin, memungkinkan beberapa objek tugas untuk merujuk hasil asinkron yang sama. Ini juga mendukung beberapa coroutine secara bersamaan menunggu hasil tugas.
Pertukarannya adalah bahwa hasilnya selalu merupakan referensi nilai-L untuk hasilnya, tidak pernah referensi nilai-R (karena hasilnya dapat dibagikan) yang dapat membatasi kemampuan untuk menggerakkan hasilnya menjadi variabel lokal. Ini juga memiliki biaya run-time yang sedikit lebih tinggi karena kebutuhan untuk mempertahankan jumlah referensi dan mendukung banyak A pencerahan.
generator<T> generator mewakili tipe coroutine yang menghasilkan urutan nilai tipe, T , di mana nilai dihasilkan dengan malas dan sinkron.
Tubuh Coroutine mampu menghasilkan nilai tipe T menggunakan kata kunci co_yield . Namun, perhatikan bahwa tubuh coroutine tidak dapat menggunakan kata kunci co_await ; Nilai harus diproduksi secara sinkron.
Misalnya:
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;
}
} Ketika fungsi coroutine yang mengembalikan generator<T> disebut coroutine dibuat awalnya ditangguhkan. Eksekusi coroutine memasuki tubuh coroutine ketika generator<T>::begin() dipanggil dan berlanjut sampai salah satu pernyataan co_yield pertama tercapai atau coroutine berjalan hingga selesai.
Jika iterator yang dikembalikan tidak sama dengan Iterator end() maka dereferencing iterator akan mengembalikan referensi ke nilai yang diteruskan ke pernyataan co_yield .
operator++() pada iterator akan melanjutkan eksekusi coroutine dan melanjutkan sampai titik co_yield berikutnya tercapai atau coroutine berjalan ke penyelesaian ().
Pengecualian yang tidak ditangani oleh coroutine akan menyebar keluar dari panggilan begin() atau operator++() ke penelepon.
Ringkasan 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 mirip dengan generator kecuali bahwa itu dirancang untuk lebih efisien mendukung menghasilkan unsur -unsur dari urutan bersarang sebagai elemen dari urutan luar.
Selain dapat co_yield nilai tipe T Anda juga dapat co_yield nilai tipe recursive_generator<T> .
Saat Anda co_yield a recursive_generator<T> Nilai semua elemen generator yang dihasilkan dihasilkan sebagai elemen dari generator saat ini. Coroutine saat ini ditangguhkan sampai konsumen selesai mengonsumsi semua elemen dari generator bersarang, setelah itu eksekusi titik dari coroutine saat ini akan melanjutkan eksekusi untuk menghasilkan elemen berikutnya.
Manfaat recursive_generator<T> daripada generator<T> untuk iterasi lebih dari struktur data rekursif adalah bahwa iterator::operator++() dapat secara langsung melanjutkan coroutine paling banyak daun untuk menghasilkan elemen berikutnya, daripada harus melanjutkan/menangguhkan O (kedalaman) coroutine untuk setiap elemen. Sisi bawah adalah bahwa ada tambahan overhead
Misalnya:
// 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 ());
}
}
} Perhatikan bahwa menerapkan operator fmap() ke recursive_generator<T> akan menghasilkan jenis generator<U> daripada jenis recursive_generator<U> . Ini karena penggunaan fmap umumnya tidak digunakan dalam konteks rekursif dan kami mencoba untuk menghindari overhead tambahan yang dikeluarkan oleh recursive_generator .
async_generator<T> async_generator mewakili tipe coroutine yang menghasilkan urutan nilai tipe, T , di mana nilai dihasilkan dengan malas dan nilai dapat diproduksi secara tidak sinkron.
Tubuh Coroutine dapat menggunakan ekspresi co_await dan co_yield .
Konsumen generator dapat menggunakan for co_await untuk mengkonsumsi nilai-nilainya.
Contoh
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;
}
}Ringkasan 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);
} Ketika objek async_generator dirusak, ia meminta pembatalan coroutine yang mendasarinya. Jika coroutine telah berjalan sampai selesai atau saat ini ditangguhkan dalam ekspresi co_yield maka coroutine dihancurkan segera. Kalau tidak, coroutine akan melanjutkan eksekusi sampai berjalan sampai selesai atau mencapai ekspresi co_yield berikutnya.
Ketika kerangka coroutine dihancurkan, destruktor dari semua variabel dalam ruang lingkup pada saat itu akan dieksekusi untuk memastikan sumber daya generator dibersihkan.
Perhatikan bahwa penelepon harus memastikan bahwa objek async_generator tidak boleh dihancurkan saat konsumen coroutine menjalankan ekspresi co_await menunggu item berikutnya diproduksi.
single_consumer_eventIni adalah jenis acara reset manual sederhana yang hanya mendukung satu coroutine yang menantinya sekaligus. Ini bisa digunakan untuk
Ringkasan 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 ;
};
}Contoh:
# 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 Kelas ini memberikan primitif sinkronisasi async yang memungkinkan coroutine tunggal untuk menunggu sampai acara ditandai dengan panggilan ke metode set() .
Setelah coroutine yang menunggu acara dirilis oleh panggilan sebelumnya atau selanjutnya untuk set() acara secara otomatis diatur ulang kembali ke keadaan 'tidak diatur'.
Kelas ini adalah versi yang lebih efisien dari async_auto_reset_event yang dapat digunakan dalam kasus di mana hanya satu coroutine yang akan menunggu acara tersebut pada satu waktu. Jika Anda perlu mendukung beberapa coroutine bersamaan menunggu di acara tersebut maka gunakan kelas async_auto_reset_event sebagai gantinya.
Ringkasan 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 ;
};
}Contoh Penggunaan:
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_mutexMemberikan abstraksi eksklusi timbal balik sederhana yang memungkinkan penelepon untuk 'co_await' mutex dari dalam coroutine untuk menangguhkan coroutine sampai kunci mutex diperoleh.
Implementasinya bebas kunci karena coroutine yang menunggu mutex tidak akan memblokir utas tetapi sebaliknya akan menangguhkan coroutine dan kemudian melanjutkannya di dalam panggilan untuk unlock() oleh pemegang kunci sebelumnya.
Ringkasan 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 ();
};
}Contoh Penggunaan:
# 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 Peristiwa reset manual adalah primitif coroutine/utas-sinkronisasi yang memungkinkan satu atau lebih utas untuk menunggu sampai acara ditandai oleh utas yang memanggil set() .
Acara ini ada di salah satu dari dua negara bagian; 'Set' dan 'Not Set' .
Jika acara tersebut dalam keadaan 'set' ketika sebuah coroutine menunggu acara maka coroutine berlanjut tanpa menangguhkan. Namun jika coroutine dalam keadaan 'tidak diatur' maka coroutine ditangguhkan sampai beberapa utas selanjutnya memanggil metode set() .
Setiap utas yang ditangguhkan saat menunggu acara menjadi 'set' akan dilanjutkan di dalam panggilan berikutnya untuk set() oleh beberapa utas.
Perhatikan bahwa Anda harus memastikan bahwa tidak ada coroutine yang menunggu acara 'tidak diatur' ketika acara tersebut dirusak karena tidak akan dilanjutkan.
Contoh:
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);
}Ringkasan 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 Acara reset otomatis adalah primitif coroutine/utas-sinkronisasi yang memungkinkan satu atau lebih utas menunggu sampai acara ditandai oleh utas dengan menelepon set() .
Setelah coroutine yang menunggu acara dirilis oleh panggilan sebelumnya atau selanjutnya untuk set() acara secara otomatis diatur ulang kembali ke keadaan 'tidak diatur'.
Ringkasan 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_latchKunci async adalah primitif sinkronisasi yang memungkinkan coroutine untuk secara asinkron menunggu sampai konter telah dikurangi menjadi nol.
Latch adalah objek sekali pakai. Setelah konter mencapai nol, kait menjadi 'siap' dan akan tetap siap sampai kait dihancurkan.
Ringkasan 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 adalah primitif sinkronisasi yang memungkinkan produser tunggal dan banyak konsumen untuk berkoordinasi sehubungan dengan angka urutan yang meningkat secara monoton.
Produser tunggal memajukan nomor urutan dengan menerbitkan nomor urutan baru dalam urutan yang meningkat secara monoton. Satu atau lebih konsumen dapat menanyakan nomor urutan terakhir yang diterbitkan dan dapat menunggu sampai nomor urutan tertentu telah diterbitkan.
Penghalang urutan dapat digunakan untuk mewakili kursor menjadi produsen yang aman-utas/buffer cincin konsumen
Lihat pola Disruptor LMAX untuk lebih banyak latar belakang: https://lmax-exchange.github.io/disruptor/files/disruptor-1.0.pdf
Sinopsis 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 adalah primitif sinkronisasi yang dapat digunakan untuk mengoordinasikan akses ke buffer cincin untuk produsen tunggal dan satu atau lebih konsumen.
Seorang produser pertama kali memperoleh satu atau lebih slot dalam ring-buffer, menulis ke elemen buffer cincin yang sesuai dengan slot tersebut, dan akhirnya menerbitkan nilai-nilai yang ditulis untuk slot tersebut. Seorang produsen tidak pernah dapat menghasilkan lebih dari elemen 'bufferze' sebelum di mana konsumen telah dikonsumsi.
Seorang konsumen kemudian menunggu elemen -elemen tertentu untuk diterbitkan, memproses item dan kemudian memberi tahu produser ketika telah selesai memproses item dengan menerbitkan nomor urutan yang telah selesai dikonsumsi di objek sequence_barrier .
Sinopsis 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 ;
};
}Contoh Penggunaan:
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 Kelas multi_producer_sequencer adalah primitif sinkronisasi yang mengoordinasikan akses ke buffer cincin untuk beberapa produsen dan satu atau lebih konsumen.
Untuk varian produser tunggal, lihat kelas single_producer_sequencer .
Perhatikan bahwa buffer cincin harus memiliki ukuran yang merupakan kekuatan dua. Ini karena implementasi menggunakan bitmask sebagai ganti divisi/modulo integer untuk menghitung offset ke dalam buffer. Juga, ini memungkinkan nomor urutan untuk membungkus dengan aman di sekitar nilai 32-bit/64-bit.
Ringkasan 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 adalah nilai yang dapat diteruskan ke fungsi yang memungkinkan penelepon untuk mengkomunikasikan permintaan untuk membatalkan operasi ke fungsi itu.
Untuk mendapatkan cancellation_token yang dapat dibatalkan, Anda harus terlebih dahulu membuat objek cancellation_source . Metode cancellation_source::token() dapat digunakan untuk memproduksi nilai -nilai cancellation_token baru yang terkait dengan objek cancellation_source .
Ketika Anda ingin nanti meminta pembatalan operasi, Anda telah melewati cancellation_token kepada Anda dapat menghubungi cancellation_source::request_cancellation() pada objek cancellation_source yang terkait.
Fungsi dapat menanggapi permintaan pembatalan dalam satu dari dua cara:
cancellation_token::is_cancellation_requested() atau cancellation_token::throw_if_cancellation_requested() .cancellation_registration .Ringkasan 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 ;
};
}Contoh: Pendekatan Polling
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 ();
}Contoh: Pendekatan Callback
// 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 Kelas static_thread_pool menyediakan abstraksi yang memungkinkan Anda menjadwalkan pekerjaan pada kumpulan utas ukuran tetap.
Kelas ini mengimplementasikan konsep penjadwal (lihat di bawah).
Anda dapat bekerja untuk membuat-pool dengan mengeksekusi co_await threadPool.schedule() . Operasi ini akan menangguhkan coroutine saat ini, enqueue untuk dieksekusi pada pool thread dan kumpulan utas kemudian akan melanjutkan coroutine ketika utas di kolam utas berikutnya bebas untuk menjalankan coroutine. Operasi ini dijamin tidak akan melempar dan, dalam kasus umum, tidak akan mengalokasikan memori apa pun .
Kelas ini memanfaatkan algoritma pencurian kerja untuk memuat kerja keseimbangan di beberapa utas. Pekerjaan yang disetel ke kolam utas dari benang-benang akan dijadwalkan untuk dieksekusi pada utas yang sama dalam antrian LIFO. Pekerjaan yang disetel ke kolam benang dari benang jarak jauh akan diaktifkan ke antrian FIFO global. Ketika utas pekerja kehabisan pekerjaan dari antrian lokalnya, ia pertama -tama mencoba untuk dequeue bekerja dari antrian global. Jika antrian itu kosong maka berikutnya mencoba mencuri pekerjaan dari belakang antrian utas pekerja lainnya.
Ringkasan 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
};
}Contoh Penggunaan: Sederhana
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 ();
} Contoh Penggunaan: Melakukan hal -hal secara paralel - menggunakan operator schedule_on() dengan 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 dan io_work_scope Kelas io_service menyediakan abstraksi untuk memproses acara penyelesaian I/O dari operasi I/O asinkron.
Ketika operasi I/O asinkron selesai, coroutine yang menunggu operasi itu akan dilanjutkan pada benang I/O di dalam panggilan ke salah satu metode pemrosesan peristiwa: process_events() , process_pending_events() , process_one_event() atau process_one_pending_event() .
Kelas io_service tidak mengelola utas I/O apa pun. Anda harus memastikan bahwa beberapa utas memanggil salah satu metode pemrosesan acara untuk coroutine yang menunggu acara penyelesaian I/O untuk dikirim. Ini bisa berupa utas khusus yang memanggil process_events() atau dicampur dengan beberapa loop peristiwa lain (misalnya loop acara UI) dengan polling secara berkala untuk acara baru melalui panggilan ke process_pending_events() atau process_one_pending_event() .
Ini memungkinkan integrasi loop acara io_service dengan loop acara lainnya, seperti loop acara antarmuka pengguna.
Anda dapat multiplex pemrosesan peristiwa di beberapa utas dengan memiliki beberapa utas panggilan process_events() . Anda dapat menentukan petunjuk tentang jumlah maksimum utas untuk secara aktif memproses acara melalui parameter konstruktor io_service opsional.
Di Windows, implementasi memanfaatkan fasilitas port penyelesaian Windows I/O untuk mengirim acara ke utas I/O dengan cara yang dapat diskalakan.
Ringkasan 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 ;
};
}Contoh:
# 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 sebagai penjadwal Kelas io_service mengimplementasikan antarmuka untuk konsep Scheduler dan DelayedScheduler .
Ini memungkinkan coroutine untuk menangguhkan eksekusi pada utas saat ini dan menjadwalkan dirinya untuk dimulainya kembali pada utas I/O yang terkait dengan objek io_service tertentu.
Contoh:
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_fileJenis-jenis ini adalah kelas dasar abstrak untuk melakukan file konkret I/O.
Ringkasan 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_fileJenis -jenis ini mewakili kelas I/O file konkret.
Ringkasan 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_);
};
} Semua fungsi open() melempar std::system_error pada kegagalan.
Catatan: Abstraksi jaringan saat ini hanya didukung di platform Windows. Dukungan Linux akan segera hadir.
socketKelas soket dapat digunakan untuk mengirim/menerima data melalui jaringan secara tidak sinkron.
Saat ini hanya mendukung TCP/IP, UDP/IP over IPv4 dan IPv6.
Ringkasan 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 ();
};
}Contoh: Server Echo
# 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_addressKelas penolong untuk mewakili alamat IP.
Sinopsis 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_endpointKelas penolong untuk mewakili alamat IP dan jumlah port.
Sinopsis 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() Fungsi sync_wait() dapat digunakan untuk secara sinkron menunggu sampai yang ditentukan awaitable .
Yang ditentukan yang ditentukan akan co_await ed pada utas saat ini di dalam coroutine yang baru dibuat.
Panggilan sync_wait() akan diblokir sampai operasi selesai dan akan mengembalikan hasil dari ekspresi co_await atau rethrow pengecualian jika ekspresi co_await diselesaikan dengan pengecualian yang tidak ditangani.
Fungsi sync_wait() sebagian besar berguna untuk memulai tugas tingkat atas dari dalam main() dan menunggu sampai tugas selesai, dalam praktiknya itu adalah satu-satunya cara untuk memulai task tingkat pertama/atas.
Ringkasan API:
// <cppcoro/sync_wait.hpp>
namespace cppcoro
{
template < typename AWAITABLE>
auto sync_wait (AWAITABLE&& awaitable)
-> typename awaitable_traits<AWAITABLE&&>::await_result_t;
}Contoh:
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() Fungsi when_all_ready() dapat digunakan untuk membuat yang baru menunggu yang selesai ketika semua input menunggu selesai.
Tugas input dapat berupa semua jenis yang menunggu.
Ketika yang dikembalikan menunggu co_await ed itu akan co_await masing -masing input menunggu secara bergantian pada utas yang menunggu dalam urutan mereka diteruskan ke fungsi when_all_ready() . Jika tugas -tugas ini tidak selesai secara sinkron maka mereka akan dieksekusi secara bersamaan.
Setelah semua ekspresi co_await pada input menunggu telah berjalan untuk menyelesaikan, Awaitable yang dikembalikan akan diselesaikan dan melanjutkan coroutine yang menunggu. Coroutine yang menunggu akan dilanjutkan di utas input yang menunggu yang terakhir diselesaikan.
Yang dikembalikan menunggu dijamin tidak akan melempar pengecualian ketika co_await ED, bahkan jika beberapa input menunggu gagal dengan pengecualian yang tidak ditangani.
Namun, perhatikan bahwa panggilan when_all_ready() itu sendiri dapat melempar std::bad_alloc jika tidak dapat mengalokasikan memori untuk bingkai coroutine yang diperlukan untuk menunggu masing -masing input menunggu. Ini juga dapat melempar pengecualian jika salah satu dari input yang ditunggu -tunggu dari objek dari konstruktor salinan/bergerak.
Hasil dari co_await ing yang dikembalikan menunggu adalah std::tuple atau std::vector when_all_task<RESULT> objek. Objek -objek ini memungkinkan Anda untuk mendapatkan hasil (atau pengecualian) dari setiap input yang dapat ditunggu -tunggu secara terpisah dengan memanggil metode when_all_task<RESULT>::result() dari tugas output yang sesuai. Hal ini memungkinkan penelepon untuk secara bersamaan menunggu beberapa menunggu dan menyinkronkan pada penyelesaian mereka sambil tetap mempertahankan kemampuan untuk selanjutnya memeriksa hasil dari masing -masing operasi co_await untuk keberhasilan/kegagalan.
Ini berbeda dari when_all() di mana kegagalan operasi co_await individu menyebabkan operasi keseluruhan gagal dengan pengecualian. Ini berarti Anda tidak dapat menentukan operasi co_await komponen mana yang gagal dan juga mencegah Anda mendapatkan hasil operasi co_await lainnya.
Ringkasan 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>>>;
}Contoh Penggunaan:
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() Fungsi when_all() dapat digunakan untuk membuat yang baru menunggu bahwa ketika co_await ed akan co_await masing -masing input menunggu secara bersamaan dan mengembalikan agregat hasil masing -masing.
Ketika yang dikembalikan menunggu ditunggu, itu akan co_await masing -masing input menunggu pada utas saat ini. Setelah ditangguhkan pertama yang ditunda, tugas kedua akan dimulai, dan seterusnya. Operasi dieksekusi secara bersamaan sampai semuanya berjalan sampai selesai.
Setelah semua operasi co_await komponen telah selesai, agregat hasil dibangun dari masing -masing hasil individu. Jika pengecualian dilemparkan oleh salah satu tugas input atau jika konstruksi hasil agregat melempar pengecualian maka pengecualian akan menyebar keluar dari co_await dari yang dikembalikan.
Jika beberapa operasi co_await gagal dengan pengecualian maka salah satu pengecualian akan menyebar keluar dari co_await when_all() ekspresi pengecualian lainnya akan diabaikan secara diam -diam. Tidak ditentukan pengecualian operasi mana yang akan dipilih.
Jika penting untuk mengetahui operasi co_await komponen mana yang gagal atau mempertahankan kemampuan untuk mendapatkan hasil operasi lain bahkan jika beberapa di antaranya gagal maka Anda harus menggunakan when_all_ready() sebagai gantinya.
Ringkasan 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>>>>;
}Contoh:
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() Fungsi fmap() dapat digunakan untuk menerapkan fungsi yang dapat dipanggil ke nilai yang terkandung dalam tipe kontainer, mengembalikan tipe wadah baru dari hasil penerapan fungsi yang terkandung nilai (s).
Fungsi fmap() dapat menerapkan fungsi untuk nilai -nilai generator<T> , recursive_generator<T> dan async_generator<T> serta nilai apa pun yang mendukung konsep Awaitable (mis. task<T> ).
Masing -masing jenis ini memberikan kelebihan beban untuk fmap() yang mengambil dua argumen; fungsi untuk diterapkan dan nilai wadah. Lihat Dokumentasi untuk setiap jenis untuk kelebihan fmap() yang didukung.
Misalnya, fungsi fmap() dapat digunakan untuk menerapkan fungsi pada hasil akhirnya dari task<T> , menghasilkan task<U> yang akan selesai dengan nilai pengembalian fungsi.
// 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);Ringkasan 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>>;
} Fungsi fmap() dirancang untuk mencari kelebihan yang benar dengan pencarian yang bergantung pada argumen (ADL) sehingga umumnya harus dipanggil tanpa cppcoro:: awalan.
resume_on() Fungsi resume_on() dapat digunakan untuk mengontrol konteks eksekusi bahwa yang menunggu akan melanjutkan coroutine yang menunggu saat ditunggu. Ketika diterapkan pada async_generator itu mengontrol konteks eksekusi yang co_await g.begin() dan co_await ++it melanjutkan coroutine yang menunggu.
Biasanya, coroutine yang menunggu dari yang menunggu (mis. task ) atau async_generator akan melanjutkan eksekusi pada utas apa pun operasi selesai. Dalam beberapa kasus ini mungkin bukan utas yang ingin Anda terus dieksekusi. Dalam kasus ini, Anda dapat menggunakan fungsi resume_on() untuk membuat generator baru yang dapat ditunggu -tunggu yang akan melanjutkan eksekusi pada utas yang terkait dengan penjadwal yang ditentukan.
Fungsi resume_on() dapat digunakan baik sebagai fungsi normal yang mengembalikan generator baru yang menunggu/generator. Atau dapat digunakan dalam pipa-sintaksis.
Contoh:
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);
}Ringkasan 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() Fungsi schedule_on() dapat digunakan untuk mengubah konteks eksekusi yang diberikan oleh yang diberikan atau async_generator mulai dieksekusi.
Ketika diterapkan pada async_generator itu juga mempengaruhi konteks eksekusi mana yang dilanjutkan pada setelah pernyataan co_yield .
Perhatikan bahwa transformasi schedule_on tidak menentukan utas yang akan diselesaikan atau hasil async_generator yang menunggu atau menghasilkan hasil, yang tergantung pada implementasi generator atau generator yang menunggu.
Lihat operator resume_on() untuk transformasi yang mengontrol utas operasi selesai.
Misalnya:
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 ());
}Ringkasan 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> Metafungsi template ini dapat digunakan untuk menentukan jenis ekspresi co_await yang dihasilkan jika diterapkan pada ekspresi tipe T
Perhatikan bahwa ini mengasumsikan nilai Tipe T sedang ditunggu dalam konteks di mana ia tidak terpengaruh oleh await_transform yang diterapkan oleh objek janji Coroutine. Hasilnya mungkin berbeda jika nilai tipe T ditunggu dalam konteks seperti itu.
Metafungsi template awaitable_traits<T> tidak mendefinisikan awaiter_t atau await_result_t bersarang typedefs jika type, T , tidak menunggu. Ini memungkinkan penggunaannya dalam konteks SFinae yang menonaktifkan kelebihan beban ketika T tidak menunggu.
Ringkasan 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> Metafungsi is_awaitable<T> Template memungkinkan Anda untuk menanyakan apakah jenis yang diberikan dapat co_await atau tidak atau tidak dari dalam coroutine.
Ringkasan 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> adalah konsep yang menunjukkan bahwa jenis dapat co_await dalam konteks coroutine yang tidak memiliki kelebihan await_transform dan bahwa hasil dari ekspresi co_await memiliki tipe, T .
Misalnya, Type task<T> mengimplementasikan konsep Awaitable<T&&> sedangkan Type task<T>& mengimplementasikan konsep Awaitable<T&> .
Awaiter<T> Awaiter<T> adalah konsep yang menunjukkan jenis berisi metode await_ready , await_suspend dan await_resume yang diperlukan untuk mengimplementasikan protokol untuk menangguhkan/melanjutkan coroutine yang menunggu.
Jenis yang memuaskan Awaiter<T> harus dimiliki, untuk instance dari jenisnya, awaiter :
awaiter.await_ready() -> boolawaiter.await_suspend(std::experimental::coroutine_handle<void>{}) -> void atau bool atau std::experimental::coroutine_handle<P> Untuk beberapa P .awaiter.await_resume() -> T Jenis apa pun yang mengimplementasikan konsep Awaiter<T> juga mengimplementasikan konsep Awaitable<T> .
Scheduler Scheduler adalah konsep yang memungkinkan penjadwalan eksekusi coroutine dalam beberapa konteks eksekusi.
concept Scheduler
{
Awaitable< void > schedule ();
} Diberikan tipe, S , yang mengimplementasikan konsep Scheduler , dan instance, s , tipe S :
s.schedule() mengembalikan tipe yang menunggu sehingga co_await s.schedule() akan menangguhkan coroutine saat ini dan menjadwalkannya untuk dimulainya kembali pada konteks eksekusi yang terkait dengan penjadwal, s .co_await s.schedule() memiliki tipe 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 adalah konsep yang memungkinkan coroutine untuk menjadwalkan dirinya untuk dieksekusi pada konteks eksekusi penjadwal setelah durasi waktu yang ditentukan telah berlalu.
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);
} Diberikan tipe, S , yang mengimplementasikan DelayedScheduler dan instance, s tipe S :
s.schedule_after(delay) mengembalikan objek yang dapat ditunggu sehingga co_await s.schedule_after(delay) menangguhkan coroutine saat ini selama durasi delay sebelum menjadwalkan coroutine untuk dimulainya kembali pada konteks eksekusi yang terkait dengan penjadwal, s .co_await s.schedule_after(delay) memiliki tipe void .Perpustakaan CPPCORO mendukung bangunan di bawah Windows dengan Visual Studio 2017 dan Linux dengan Clang 5.0+.
Perpustakaan ini memanfaatkan sistem pembuatan kue (tidak, bukan C# satu).
Sistem pembuatan kue diperiksa secara otomatis sebagai submodule git sehingga Anda tidak perlu mengunduh atau menginstalnya secara terpisah.
Perpustakaan ini saat ini membutuhkan Visual Studio 2017 atau lebih baru dan Windows 10 SDK.
Dukungan untuk Clang (#3) dan Linux (#15) direncanakan.
Sistem Build Cake diimplementasikan dalam Python dan membutuhkan Python 2.7 untuk dipasang.
Pastikan interpreter Python 2.7 ada di jalur Anda dan tersedia sebagai 'Python'.
Pastikan pembaruan Visual Studio 2017 3 atau lebih baru diinstal. Perhatikan bahwa ada beberapa masalah yang diketahui dengan coroutine di Update 2 atau sebelumnya yang telah diperbaiki dalam pembaruan 3.
Anda juga dapat menggunakan versi eksperimental dari Visual Studio Compiler dengan mengunduh paket Nuget dari https://vcppdogfooding.azurewebsites.net/ dan membuka ritsleting file .nuget ke direktori. Cukup perbarui file config.cake untuk menunjuk di lokasi yang tidak di -zip dengan memodifikasi dan membuka komentar baris berikut:
nugetPath = None # r'C:PathToVisualCppTools.14.0.25224-Pre'Pastikan Anda menginstal Windows 10 SDK. Ini akan menggunakan versi Windows 10 SDK dan Universal C Runtime terbaru secara default.
Repositori CPPCORO memanfaatkan submodul GIT untuk menarik sumber untuk sistem pembuatan kue.
Ini berarti Anda perlu meneruskan bendera --recursive ke perintah git clone . misalnya.
c:Code> git clone --recursive https://github.com/lewissbaker/cppcoro.git
Jika Anda sudah mengkloning CPPCORO, maka Anda harus memperbarui submodul setelah menarik perubahan.
c:Codecppcoro> git submodule update --init --recursive
Untuk membangun dari baris perintah cukup jalankan 'kue.bat' di root ruang kerja.
misalnya.
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.
Secara default, menjalankan cake tanpa argumen akan membangun semua proyek dengan semua varian membangun dan melaksanakan tes unit. Anda dapat mempersempit apa yang dibangun dengan meloloskan argumen baris perintah tambahan. misalnya.
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.
Anda dapat menjalankan cake --help ke daftar opsi baris perintah yang tersedia.
Untuk mengembangkan dari dalam Visual Studio Anda dapat membangun file .vcproj/.sln dengan menjalankan cake.bat -p .
misalnya.
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.
Saat Anda membangun proyek -proyek ini dari dalam Visual Studio, ia akan memanggil kue untuk melakukan kompilasi.
Proyek CPPCORO juga dapat dibangun di bawah Linux menggunakan CLANG+ LIBC ++ 5.0 atau lebih baru.
Membangun CPPCORO telah diuji di bawah Ubuntu 17.04.
Pastikan Anda memiliki paket berikut yang diinstal:
Ini dengan asumsi Anda memiliki dentang dan libc ++ dibangun dan diinstal.
Jika Anda belum mengkonfigurasi dentang, lihat bagian berikut untuk detail tentang pengaturan dentang untuk membangun dengan CPPCORO.
Checkout CPPCORO dan submodulesnya:
git clone --recursive https://github.com/lewissbaker/cppcoro.git cppcoro
Jalankan init.sh untuk mengatur fungsi bash cake :
cd cppcoro
source init.sh
Kemudian Anda dapat menjalankan cake dari root ruang kerja untuk membangun CPPCORO dan menjalankan tes:
$ cake
Anda dapat menentukan argumen baris perintah tambahan untuk menyesuaikan build:
--help akan mencetak bantuan untuk argumen baris perintah--debug=run akan menunjukkan garis perintah build sedang dijalankanrelease=debug atau release=optimised akan membatasi varian build untuk debug atau dioptimalkan (secara default akan membangun keduanya).lib/build.cake hanya akan membangun perpustakaan CPPCORO dan bukan tes.test/build.cake@task_tests.cpp hanya akan menyusun file sumber tertentutest/build.cake@testresult akan membangun dan menjalankan tesMisalnya:
$ cake --debug=run release=debug lib/build.cake
Jika kompiler dentang Anda tidak terletak di /usr/bin/clang maka Anda dapat menentukan lokasi alternatif menggunakan satu atau lebih opsi baris perintah berikut untuk cake :
--clang-executable=<name> -Tentukan nama clang yang dapat dieksekusi untuk digunakan alih-alih clang . misalnya. Untuk memaksa penggunaan Clang 8.0 Pass --clang-executable=clang-8--clang-executable=<abspath> -Tentukan jalur lengkap ke CLang yang dapat dieksekusi. Sistem build juga akan mencari eksekutif lainnya di direktori yang sama. Jika jalur ini memiliki formulir <prefix>/bin/<name> maka ini juga akan mengatur default clang-install-prefix ke <prefix> .--clang-install-prefix=<path> -Tentukan jalur di mana dentang telah diinstal. Ini akan menyebabkan sistem build untuk mencari dentang di bawah <path>/bin (kecuali ditimpa oleh --clang-executable ).--libcxx-install-prefix=<path> -Tentukan jalur di mana libc ++ telah diinstal. Secara default sistem build akan mencari libc ++ di lokasi yang sama dengan dentang. Gunakan opsi baris perintah ini jika diinstal di lokasi yang berbeda.Contoh: Gunakan versi spesifik dentang yang diinstal di lokasi default
$ cake --clang-executable=clang-8
Contoh: Gunakan versi default dentang dari lokasi khusus
$ cake --clang-install-prefix=/path/to/clang-install
Contoh: Gunakan versi klang tertentu, di lokasi khusus, dengan libc ++ dari lokasi yang berbeda
$ 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. Lihat di bawah.
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 cppcoroJika versi sudah ketinggalan zaman, silakan buat masalah atau tarik permintaan pada repositori 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.