Untuk siapa
Artikel ini ditujukan bagi mereka yang belum menemukan idiom Curiously recurring template pattern (CRTP), tetapi memiliki gambaran tentang template apa yang ada di C ++. Anda tidak memerlukan pengetahuan khusus atau pengetahuan yang kuat tentang pemrograman dalam templat untuk memahami artikel.
Mari kita hadapi masalah ini :
Sebuah file berasal dari jaringan dalam salah satu format: json atau xml dan kami ingin mengurai dan mendapatkan beberapa informasi. Solusinya menyarankan dirinya sendiri - untuk menggunakan pola jembatan untuk memisahkan antarmuka parser dan dua implementasinya, satu per format file. Jadi, setelah menentukan format file, kita dapat meneruskan implementasi yang kita butuhkan dalam bentuk pointer ke fungsi parsing.
Contoh skema
// , Parser
// ,
ParsedDataType parseData(Parser* parser, FileType file);
int main() {
FileType file = readFile();
Parser* impl = nullptr;
if (file.type() == JsonFile)
impl = new ParserJsonImpl();
else
impl = new ParserXmlImpl();
ParsedDataType parsedData = parserData(impl, file);
}
Pendekatan klasik ini memiliki beberapa kelemahan :
Antarmuka Parser harus memiliki fungsi virtual, dan seperti yang kita ketahui, mahal untuk membuka tabel metode virtual.
Antarmuka fungsi tidak sejelas yang kami inginkan jika dibandingkan, misalnya, dengan bahasa fungsional dengan sistem tipe kaya.
( , ).
C++
CRTP - , , , .
- -, , , , .
template <typename Implementation>
struct ParserInterface {
ParsedData getData() {
return impl()->getDataImpl();
}
ParsedID getID() {
return impl()->getIDImpl();
}
private:
Implementation* impl() {
return static_cast<Implementation*>(this);
}
};
, , Implementation* impl()
.
-, . , -, .
struct ParserJsonImpl : public ParserInterface<ParserJsonImpl> {
friend class ParserInterface;
private:
ParsedData getDataImpl() {
std::cout << "ParserJsonImpl::getData()\n";
return ParsedData();
}
ParsedID getIDImpl() {
std::cout << "ParserJsonImpl::getID()\n";
return ParsedID;
}
};
struct ParserXmlImpl : public ParserInterface<ParserXmlImpl> {
friend class ParserInterface;
private:
ParsedData getDataImpl() {
std::cout << "ParserXmlImpl::getData()\n";
return ParsedData();
}
ParsedID getIDImpl() {
std::cout << "ParserXmlImpl::getID()\n";
return ParsedID();
}
};
, . , , ParserInterface<A>
ParserInterface<B>
. . , , - , static_cast<>()
Implementation* impl()
. , . .
:
, - .
, - , private.
, -, friend.
, , .
template <typename Impl>
std::pair<ParsedData, parsedID> parseFile(ParserInterface<Impl> parser) {
return std::make_pair(parser.getData(), parser.getID());
}
, . ParserInterface parser
. , static_cast
, , .
:
int main() {
ParserJsonImpl jsonParser;
parseFile(jsonParser);
ParserXmlImpl xmlParser;
parseFile(xmlParser);
return 0;
}
.
ParserJsonImpl::getData() ParserJsonImpl::getID() ParserXmlImpl::getData() ParserXmlImpl::getID()
, , , . , static_cast
. . , :
. , , .
: , , , .
Pendekatan ini juga digunakan untuk idiom MixIn kelas, yang "mencampur" perilaku mereka dengan kelas yang diwariskan. Salah satu kelas ini - std::enable_shared_from_this
- bercampur dalam fungsionalitas untuk mendapatkan pointer shared_ptr
ke dirinya sendiri.
Artikel ini memberikan contoh paling sederhana untuk membiasakan diri Anda dengan topik, lebih lanjut - lebih lanjut.
Daftar lengkap kode kerja
#include <iostream>
template <typename Implementation>
struct ParserInterface {
int getData() {
return impl()->getDataImpl();
}
int getID() {
return impl()->getIDImpl();
}
private:
Implementation* impl() {
return static_cast<Implementation*>(this);
}
};
struct ParserJsonImpl : public ParserInterface<ParserJsonImpl> {
friend class ParserInterface<ParserJsonImpl>;
private:
int getDataImpl() {
std::cout << "ParserJsonImpl::getData()\n";
return 0;
}
int getIDImpl() {
std::cout << "ParserJsonImpl::getID()\n";
return 0;
}
};
struct ParserXmlImpl : public ParserInterface<ParserXmlImpl> {
int getDataImpl() {
std::cout << "ParserXmlImpl::getData()\n";
return 0;
}
int getIDImpl() {
std::cout << "ParserXmlImpl::getID()\n";
return 0;
}
};
template <typename Impl>
std::pair<int, int> parseFile(ParserInterface<Impl> parser) {
auto result = std::make_pair(parser.getData(), parser.getID());
return result;
}
int main() {
ParserJsonImpl jsonParser;
parseFile(jsonParser);
ParserXmlImpl xmlParser;
parseFile(xmlParser);
return 0;
}