Template mekanisme dalam C++ hanya sengaja menjadi berguna untuk template metaprogramming. Di sisi lain, D's ini dirancang khusus untuk memfasilitasi hal ini. Dan rupanya itu's bahkan lebih mudah untuk memahami (atau jadi saya'telah mendengar).
I've tidak ada pengalaman dengan D, tapi saya'm penasaran, apa yang dapat anda lakukan di D dan anda tidak dalam C++, ketika datang ke template metaprogramming?
Dua hal terbesar yang membantu template metaprogramming in D adalah template kendala dan statis jika
- baik yang C++ secara teoritis bisa menambahkan dan yang akan mendapatkan manfaat yang sangat.
Template kendala memungkinkan anda untuk menempatkan kondisi pada template yang harus benar untuk template untuk dapat instantiated. Misalnya, ini adalah tanda tangan dari salah satu std.algoritma.menemukan
's overload:
R find(alias pred = "a == b", R, E)(R haystack, E needle)
if (isInputRange!R &&
is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
Dalam rangka untuk kerangka ini fungsi untuk dapat menjadi contoh, jenis R
harus menjadi berbagai masukan seperti yang didefinisikan oleh std.berbagai.isInputRange
(jadi isInputRange!R
harus benar
), dan diberikan predikat kebutuhan untuk menjadi biner fungsi yang sesuai dengan argumen yang diberikan dan mengembalikan jenis yang secara implisit convertible untuk bool
. Jika hasil dari kondisi di template kendala adalah palsu
, maka template won't kompilasi. Hal ini tidak hanya melindungi anda dari yang jahat template kesalahan yang anda dapatkan dalam C++ ketika template won't kompilasi mereka dengan argumen yang diberikan, tapi itu membuat sehingga anda dapat membebani template berdasarkan template kendala. Misalnya, ada's kelebihan lain dari menemukan
yang
R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
if (isForwardRange!R1 && isForwardRange!R2
&& is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)
&& !isRandomAccessRange!R1)
Dibutuhkan persis sama argumen, namun kendala yang berbeda. Jadi, berbagai jenis pekerjaan yang berbeda dengan overload yang sama kerangka fungsi, dan implementasi terbaik dari menemukan
dapat digunakan untuk masing-masing jenis. Ada's tidak ada cara untuk melakukan hal semacam itu bersih dalam C++. Dengan sedikit keakraban dengan fungsi dan template yang digunakan dalam khas template kendala, template kendala dalam D yang cukup mudah untuk membaca, sedangkan anda perlu beberapa sangat rumit template metaprogramming in C++ untuk bahkan mencoba sesuatu seperti ini, yang rata-rata programmer tidak akan mampu untuk memahami, apalagi benar-benar melakukan mereka sendiri. Boost adalah contoh utama dari ini. Itu beberapa hal yang menakjubkan, tapi itu's sangat rumit.
statis jika
meningkatkan situasi bahkan lebih. Hanya suka dengan template kendala, setiap kondisi yang dapat dievaluasi pada saat compile time dapat digunakan dengan itu. misalnya
static if(isIntegral!T)
{
//...
}
else static if(isFloatingPoint!T)
{
//...
}
else static if(isSomeString!T)
{
//...
}
else static if(isDynamicArray!T)
{
//...
}
else
{
//...
}
Cabang yang disusun dalam tergantung pada kondisi pertama bernilai benar
. Jadi, dalam sebuah template, anda dapat mengkhususkan diri potongan pelaksanaannya berdasarkan pada jenis yang template itu diturunkan dengan atau berdasarkan apa pun yang dapat dievaluasi pada waktu kompilasi. Misalnya, core.waktu
menggunakan
static if(is(typeof(clock_gettime)))
untuk mengkompilasi kode berbeda berdasarkan apakah sistem menyediakan clock_gettime
atau tidak (jika clock_gettime
ada, menggunakan ini, jika tidak maka menggunakan gettimeofday
).
Mungkin yang paling mencolok contoh yang saya've melihat di mana D meningkatkan pada template ini dengan masalah yang tim saya bekerja berlari ke dalam C++. Kita perlu instantiate sebuah template yang berbeda berdasarkan apakah jenis itu diberikan berasal dari kelas dasar atau tidak. Kami akhirnya menggunakan larutan berdasarkan ini stack overflow pertanyaan. Ia bekerja, tetapi itu's cukup rumit untuk menguji apakah salah satu jenis yang berasal dari yang lain.
Di D, namun, semua yang harus anda lakukan adalah menggunakan :
operator. misalnya
auto func(T : U)(T val) {...}
Jika T
secara implisit convertible untuk U
(seperti itu akan jika T
yang berasal dari U
), kemudian func
akan mengkompilasi, sedangkan jika T
isn't secara implisit convertible untuk U
, maka tidak't. That sederhana perbaikan bahkan membuat template dasar spesialisasi jauh lebih kuat (bahkan tanpa template kendala atau statis jika
).
Secara pribadi, saya jarang menggunakan template dalam C++ selain dengan wadah dan sesekali fungsi di <algoritma>
, karena mereka're begitu banyak rasa sakit untuk menggunakan. Mereka menghasilkan jelek kesalahan dan sangat sulit untuk melakukan sesuatu yang mewah dengan. Untuk melakukan apa pun bahkan sedikit rumit, anda harus sangat terampil dengan template dan template metaprogramming. Dengan template yang di D meskipun, itu's begitu mudah bahwa saya menggunakan mereka sepanjang waktu. Kesalahan adalah jauh lebih mudah untuk memahami dan berurusan dengan (meskipun mereka're masih lebih buruk daripada kesalahan yang biasanya adalah dengan non-kerangka fungsi), dan saya don't harus mencari cara untuk memaksa bahasa ke melakukan apa yang saya inginkan dengan mewah metaprogramming.
Ada's tidak ada alasan bahwa C++ tidak't mendapatkan jauh dari kemampuan ini bahwa D (C++ konsep akan membantu jika mereka pernah mendapatkan orang-beres), tapi sampai mereka menambahkan dasar kompilasi bersyarat dengan konstruksi mirip dengan template kendala dan statis jika
untuk C++, C++ template baru saja memenangkan't dapat membandingkan dengan D template dalam hal kemudahan penggunaan dan kekuasaan.
Saya percaya tidak ada yang lebih baik yang memenuhi syarat untuk menunjukkan kekuatan luar biasa (TM) D sistem template dari ini renderer saya menemukan tahun yang lalu:
Ya! Ini sebenarnya adalah apa yang dihasilkan oleh compiler ... itu adalah "program", dan cukup berwarna-warni, memang.
Sumber tampaknya akan kembali online.
Contoh terbaik dari D metaprogramming adalah D perpustakaan standar modul yang membuat penggunaan berat dari itu vs. C++ Meningkatkan dan STL modul. Check out D's std.berbagai, std.algoritma, std.fungsional dan std.paralelisme. Tak satu pun dari ini akan dengan mudah diimplementasikan dalam C++, setidaknya dengan jenis yang bersih, ekspresif API yang D modul memiliki.
Cara terbaik untuk belajar D metaprogramming, IMHO, adalah jenis-jenis contoh. Saya belajar sebagian besar dengan membaca kode std.algoritma dan std.range, yang ditulis oleh Andrei Alexandrescu (C++ template metaprogramming guru yang telah menjadi sangat terlibat dengan D). Saya kemudian menggunakan apa yang saya pelajari dan memberikan kontribusi std.paralelisme modul.
Juga perhatikan bahwa D memiliki waktu kompilasi fungsi evaluasi (CTFE) yang mirip dengan C++1x's constexpr
tapi jauh lebih umum dalam yang besar dan berkembang subset dari fungsi yang dapat dievaluasi pada saat runtime dapat dievaluasi dimodifikasi pada waktu kompilasi. Hal ini berguna untuk waktu kompilasi generasi kode, dan kode yang dihasilkan dapat disusun dengan menggunakan string mixin.
Nah di D anda dapat dengan mudah memaksakan statis kendala pada template parameter dan menulis kode tergantung pada yang sebenarnya template argumen dengan statis jika. It's mungkin untuk mensimulasikan bahwa untuk kasus sederhana dengan C++ dengan menggunakan template spesialisasi dan trik lainnya (lihat boost) tapi itu's PITA dan sangat terbatas menyebabkan compiler doesn't mengekspos banyak rincian tentang jenis.
Satu hal C++ benar-benar hanya dapat't melakukan lebih canggih mengkompilasi kode waktu generasi.
Berikut ini's sepotong D kode yang melakukan custom-made peta()
yang mengembalikan hasil dengan referensi.
Itu menciptakan dua array dengan panjang 4, maps masing-masing sesuai pasangan dari elemen ke elemen dengan nilai minimum, dan mengalikan dengan 50, dan toko hasilnya kembali ke asli array.
Beberapa fitur penting yang perlu diperhatikan adalah sebagai berikut:
Template variadic: peta()
bisa mengambil sejumlah argumen.
Kode yang (relatif) short! The Mapper
struktur, yang merupakan inti logika, hanya 15 baris-namun hal ini dapat melakukan begitu banyak untuk begitu sedikit. Maksud saya isn't bahwa ini adalah mustahil di C++, tapi yang pasti bukan't sebagai kompak dan bersih.
import std.metastrings, std.typetuple, std.range, std.stdio;
void main() {
auto arr1 = [1, 10, 5, 6], arr2 = [3, 9, 80, 4];
foreach (ref m; map!min(arr1, arr2)[1 .. 3])
m *= 50;
writeln(arr1, arr2); // Voila! You get: [1, 10, 250, 6][3, 450, 80, 4]
}
auto ref min(T...)(ref T values) {
auto p = &values[0];
foreach (i, v; values)
if (v < *p)
p = &values[i];
return *p;
}
Mapper!(F, T) map(alias F, T...)(T args) { return Mapper!(F, T)(args); }
struct Mapper(alias F, T...) {
T src; // It's a tuple!
@property bool empty() { return src[0].empty; }
@property auto ref front() {
immutable sources = FormatIota!(q{src[%s].front}, T.length);
return mixin(Format!(q{F(%s)}, sources));
}
void popFront() { foreach (i, x; src) { src[i].popFront(); } }
auto opSlice(size_t a, size_t b) {
immutable sliced = FormatIota!(q{src[%s][a .. b]}, T.length);
return mixin(Format!(q{map!F(%s)}, sliced));
}
}
// All this does is go through the numbers [0, len),
// and return string 'f' formatted with each integer, all joined with commas
template FormatIota(string f, int len, int i = 0) {
static if (i + 1 < len)
enum FormatIota = Format!(f, i) ~ ", " ~ FormatIota!(f, len, i + 1);
else
enum FormatIota = Format!(f, i);
}
Saya menulis pengalaman saya dengan D's template, string mixin, dan template mixin: http://david.rothlis.net/d/templates/
Ini harus memberikan anda sebuah rasa dari apa yang mungkin di D-I don't berpikir bahwa dalam C++ anda dapat mengakses sebuah identifier string, mengubah string yang pada waktu kompilasi, dan menghasilkan kode dari memanipulasi string.
Kesimpulan saya: Sangat fleksibel, sangat kuat, dan dapat digunakan oleh manusia belaka, tetapi referensi penyusun masih agak buggy ketika datang untuk lebih maju waktu kompilasi metaprogramming barang-barang.
Manipulasi String, bahkan parsing string.
Ini adalah MP perpustakaan yang menghasilkan rekursif layak parsers berdasarkan tata bahasa yang didefinisikan pada string menggunakan (lebih atau kurang) BNF. Aku ingin't menyentuhnya di tahun tetapi digunakan untuk bekerja.
di D anda dapat memeriksa ukuran jenis dan metode yang tersedia pada hal itu dan memutuskan mana pelaksanaan yang ingin anda gunakan
ini digunakan untuk contoh di inti.atom
module
bool cas(T,V1,V2)( shared(T)* here, const V1 ifThis, const V2 writeThis ){
static if(T.sizeof == byte.sizeof){
//do 1 byte CaS
}else static if(T.sizeof == short.sizeof){
//do 2 byte CaS
}else static if( T.sizeof == int.sizeof ){
//do 4 byte CaS
}else static if( T.sizeof == long.sizeof ){
//do 8 byte CaS
}else static assert(false);
}
Hanya untuk counter D ray tracing post, berikut adalah C++ waktu kompilasi ray tracer (metatrace):
(by the way, sebagian besar menggunakan C++2003 metaprogramming; itu akan menjadi lebih mudah dibaca dengan `constexpr ini)
Ada tenang beberapa hal yang dapat anda lakukan di template metaprogramming in D yang tidak dapat anda lakukan pada C++. Yang paling penting adalah bahwa anda dapat melakukan template metaprogramming TANPA BEGITU BANYAK rasa SAKIT!