Saya mencoba untuk menyimpan dalam std::tuple
berbagai jumlah nilai, yang nantinya akan digunakan sebagai argumen untuk panggilan ke pointer fungsi yang cocok dengan tipe yang disimpan.
Saya telah membuat contoh sederhana yang menunjukkan masalah yang saya sedang berjuang untuk pecahkan:
#include <iostream>
#include <tuple>
void f(int a, double b, void* c) {
std::cout << a << ":" << b << ":" << c << std::endl;
}
template <typename ...Args>
struct save_it_for_later {
std::tuple<Args...> params;
void (*func)(Args...);
void delayed_dispatch() {
// How can I "unpack" params to call func?
func(std::get<0>(params), std::get<1>(params), std::get<2>(params));
// But I *really* don't want to write 20 versions of dispatch so I'd rather
// write something like:
func(params...); // Not legal
}
};
int main() {
int a=666;
double b = -1.234;
void *c = NULL;
save_it_for_later<int,double,void*> saved = {
std::tuple<int,double,void*>(a,b,c), f};
saved.delayed_dispatch();
}
Biasanya untuk masalah yang melibatkan std::tuple
atau templat variadik, saya akan menulis templat lain yang ingin template <typename Head, typename ...Tail>
secara rekursif mengevaluasi semua jenis satu per satu, tetapi saya tidak dapat melihat cara melakukan hal itu untuk mengirim panggilan fungsi.
Motivasi nyata untuk ini agak lebih kompleks dan itu sebagian besar hanya latihan belajar saja. Anda dapat mengasumsikan bahwa saya menyerahkan tuple dengan kontrak dari antarmuka lain, jadi tidak dapat diubah tetapi keinginan untuk membukanya menjadi panggilan fungsi adalah milik saya. Ini std::bind
mengesampingkan penggunaan sebagai cara murah untuk menghindari masalah mendasar.
Apa cara bersih pengiriman panggilan menggunakan std::tuple
, atau cara alternatif yang lebih baik untuk mencapai hasil bersih yang sama dari menyimpan / meneruskan beberapa nilai dan penunjuk fungsi sampai titik masa depan yang sewenang-wenang?
auto saved = std::bind(f, a, b, c);
... lalu meneleponsaved()
?Jawaban:
Anda perlu membuat paket parameter angka dan membukanya
sumber
struct gens
definisi generik (definisi yang diturunkan dari derivasi yang diperluas dari kata yang sama). Saya melihatnya pada akhirnya mencapai spesialisasi dengan 0. Jika suasana hati cocok untuk Anda dan Anda memiliki siklus cadangan, jika Anda dapat mengembangkannya, dan bagaimana ia digunakan untuk ini, saya akan berterima kasih selamanya. Dan saya berharap saya bisa memilih ini seratus kali. Saya lebih senang bermain dengan garis singgung dari kode ini. Terima kasih.seq<0, 1, .., N-1>
. Cara kerjanya:gens<5>: gens<4, 4>: gens<3, 3, 4>: gens<2, 2, 3, 4> : gens<1, 1, 2, 3, 4> : gens<0, 0, 1, 2, 3, 4>
. Jenis terakhir adalah khusus, menciptakanseq<0, 1, 2, 3, 4>
. Trik yang cukup pintar.gens
oleh:template <int N, int... S> struct gens { typedef typename gens<N-1, N-1, S...>::type type; };
std::integer_sequence<T, N>
dan spesialisasi untukstd::size_t
,std::index_sequence<N>
- ditambah fungsi pembantu terkaitstd::make_in(teger|dex)_sequence<>()
danstd::index_sequence_for<Ts...>()
. Dan di C ++ 17 ada banyak hal baik lainnya yang diintegrasikan ke dalam perpustakaan - khususnya termasukstd::apply
danstd::make_from_tuple
, yang akan menangani bit pembongkaran dan pemanggilanSolusi C ++ 17 hanya dengan menggunakan
std::apply
:Hanya merasa itu harus dinyatakan sekali dalam jawaban di utas ini (setelah sudah muncul di salah satu komentar).
Solusi dasar C ++ 14 masih hilang di utas ini. EDIT: Tidak, itu sebenarnya ada di jawaban Walter.
Fungsi ini diberikan:
Sebutkan dengan cuplikan berikut:
Contoh:
DEMO
sumber
http://coliru.stacked-crooked.com/a/8ea8bcc878efc3cb
std::make_unique
langsung? Apakah perlu fungsi konkret contoh? 2. Mengapastd::move(ts)...
jika kita bisa mengubah[](auto... ts)
ke[](auto&&... ts)
?std::make_unique
mengharapkan tuple, dan tuple dapat dibuat dari tuple yang tidak dibongkar hanya melalui panggilan lain kestd::make_tuple
. Ini adalah apa yang telah saya lakukan di lambda (meskipun sangat redundan, karena Anda juga dapat dengan mudah menyalin tuple ke pointer unik tanpa menggunakancall
).Ini adalah versi kompilasi yang lengkap dari solusi Johannes untuk pertanyaan awoodland, dengan harapan semoga bermanfaat bagi seseorang. Ini diuji dengan snapshot g ++ 4.7 pada pemerasan Debian.
Satu dapat menggunakan file SConstruct berikut
Di mesin saya, ini memberi
sumber
Berikut ini adalah solusi C ++ 14.
Ini masih membutuhkan satu fungsi pembantu (
call_func
). Karena ini adalah idiom umum, mungkin standar harus mendukungnya secara langsungstd::call
dengan kemungkinan implementasiKemudian pengiriman tertunda kami menjadi
sumber
std::call
. Kebun binatang C ++ 14 yang kacauinteger_sequence
danindex_sequence
jenis-jenis pembantu dijelaskan di sini: en.cppreference.com/w/cpp/utility/integer_afterence Perhatikan ketiadaan yang mencolokstd::make_index_sequence(Args...)
, itulah sebabnya Walter dipaksa ke dalam sintaksis clunkierstd::index_sequence_for<Args...>{}
.Ini agak rumit untuk dicapai (meskipun mungkin). Saya menyarankan Anda untuk menggunakan perpustakaan di mana ini sudah diterapkan, yaitu Boost.Fusion ( fungsi panggil ). Sebagai bonus, Boost Fusion juga bekerja dengan kompiler C ++ 03.
sumber
c ++ 14larutan. Pertama, beberapa utilitas boilerplate:
Ini memungkinkan Anda memanggil lambda dengan serangkaian bilangan bulat waktu kompilasi.
dan kita selesai.
index_upto
danindex_over
membiarkan Anda bekerja dengan paket parameter tanpa harus menghasilkan kelebihan eksternal baru.Tentu saja di c ++ 17 Anda hanya
Sekarang, jika kita suka itu, masuk c ++ 14 kita dapat menulis:
relatif mudah dan bersihkan c ++ 17 sintaks siap dikirim.
ganti saja
notstd
denganstd
ketika kompiler Anda ditingkatkan dan bob adalah paman Anda.sumber
std::apply
<- musik di telingakuindex_upto
dan kurang fleksibel. ;) Coba panggilfunc
dengan argumen mundur denganindex_upto
danstd::apply
masing - masing. Diakui, siapa sih yang ingin memanggil fungsi dari tuple mundur.std::tuple_size_v
adalah C ++ 17, jadi untuk solusi C ++ 14 yang harus diganti olehtypename std::tuple_size<foo>::value
value
bukan tipe. Tapi tetap diperbaiki.sizeof...(Types)
. Saya suka solusi Anda tanpatypename
.Memikirkan masalah lebih didasarkan pada jawaban yang diberikan, saya telah menemukan cara lain untuk memecahkan masalah yang sama:
Yang membutuhkan perubahan implementasi
delayed_dispatch()
menjadi:Ini berfungsi dengan secara rekursif mengubah
std::tuple
paket parameter menjadi haknya sendiri.call_or_recurse
diperlukan sebagai spesialisasi untuk mengakhiri rekursi dengan panggilan nyata, yang hanya membongkar paket parameter yang sudah selesai.Saya tidak yakin ini merupakan solusi yang "lebih baik", tetapi ini cara lain untuk memikirkan dan menyelesaikannya.
Sebagai solusi alternatif lain yang dapat Anda gunakan
enable_if
, untuk membentuk sesuatu yang bisa dibilang lebih sederhana daripada solusi saya sebelumnya:Kelebihan pertama hanya membutuhkan satu argumen lagi dari tuple dan memasukkannya ke dalam paket parameter. Kelebihan kedua mengambil paket parameter yang cocok dan kemudian membuat panggilan nyata, dengan kelebihan pertama dinonaktifkan di satu-satunya kasus di mana yang kedua akan layak.
sumber
Variasi saya dari solusi dari Johannes menggunakan C ++ 14 std :: index_afterence (dan tipe pengembalian fungsi sebagai parameter templat RetT):
sumber