Für wen
Dieser Artikel richtet sich an Personen, die nicht auf die CRTP-Sprache (Curiously Recurring Template Pattern) gestoßen sind, aber eine Vorstellung davon haben, welche Vorlagen in C ++ enthalten sind. Sie benötigen keine spezifischen Kenntnisse oder festen Kenntnisse der Programmierung in Vorlagen, um den Artikel zu verstehen.
Lassen Sie uns dieses Problem haben:
Eine Datei kommt aus dem Netzwerk in einem der folgenden Formate: json oder xml. Wir möchten sie analysieren und einige Informationen abrufen. Die Lösung bietet sich an - das Bridge- Muster zu verwenden, um die Parser-Schnittstelle und ihre zwei Implementierungen zu trennen, eine pro Dateiformat. Nachdem wir das Dateiformat festgelegt haben, können wir die benötigte Implementierung in Form eines Zeigers auf die Analysefunktion übergeben.
Schematisches Beispiel
// , 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);
}
Dieser klassische Ansatz hat mehrere Nachteile :
Die Parser-Schnittstelle muss über virtuelle Funktionen verfügen, und wie wir wissen, ist es teuer, zur virtuellen Methodentabelle zu wechseln.
Die Funktionsschnittstelle ist nicht so beschreibend, wie wir es uns wünschen, wenn wir sie beispielsweise mit funktionalen Sprachen mit Rich-Type-Systemen vergleichen.
( , ).
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
. . , :
. , , .
: , , , .
Dieser Ansatz wird auch für die MixIn-Sprache von Klassen verwendet, die ihr Verhalten mit geerbten Klassen "mischen". Eine dieser Klassen - std::enable_shared_from_this
- mischt die Funktionalität ein, um einen Zeiger shared_ptr
auf sich selbst zu erhalten.
Dieser Artikel bietet das einfachste Beispiel, um sich mit dem Thema vertraut zu machen.
Vollständige Auflistung des Arbeitscodes
#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;
}