C ++でのXMLのDSL

何がありますか


そもそも、私が働いているプロジェクトとそこにすべてが書かれている方法について少しお話しします。 たぶん私たちの一部ではないので...

このプロジェクトは、1つの事業セグメントの顧客向けに特別に開発されたCRMシステムです。 プロジェクトは約6年で、開発チームは10人で構成されています。 言語:C ++およびPL / SQL。

私たちのシステムは、Plain Old XMLを使用しています。 また、使用されているXMLのスキーマはほとんどありません。 ここで言うことができるのは、ユニットテストが2年目だけここでワクチン接種され、マネージャーがまだそれらを書くのに費やした時間を非難している場合です。 さあ...

道に沿って、すべての改善は、現状が迷惑で耐えられないときに現れます。 同じことが今起こった。

多くの人がそうであるように、私たちは多くのことを最適ではなく、最善の方法ではないと思います。 私たちは主なことをします。 XMLの例も例外ではありません。


標準的な実践


以下は、関数でXMLドキュメントを構築するためのコードの例です。

::std::string request = clearCreateNode("book", createNode("author", "", "name='Freddy' surname='Smith'") + createNode("author", "", "name='Bill' surname='Joe'") + clearCreateNode("quote", "This is the best unknown book I've ever quoted!" + createNode("author", "", "name='Mr. Bob'")), "isbn='123456789' name='Some book' year='2011'"); 


標準プラクティス。 streamないのはなぜですか? -要素のネストがあるため。 streamが最初に頭に浮かぶものであり、命令型のアプローチでは、「右のオブジェクトを左に送る」以外の何かを得ることはほとんど不可能です。

さて、私たちはそれに慣れています。 コンマは許容できますが、タグごとに関数名clearNodeまたはclearCreateNode繰り返しを許容することはできませんでした。 そして、たとえ"T"だけが狂気であったとしても、名前がどれほど短くてもかまいません。 それぞれの発言の前に誰かと対話を繰り返すのに似ています:「私が言う」、および各文の前に「文:」、そして単語の前に「単語:」など。 それは狂気ですか?

問題は何ですか


誰かがXML自体は冗長であると言うでしょう-少なくともコードがタグを閉じる必要がないのは良いことです...

はい、それはそうですが、どちらも振る舞う理由ではありません。 私は馬鹿よりも人と話すのが好きです。 対話のコンテキストとセマンティクスを両方とも理解するとき。 スラングを使用してお互いを理解できるのはいつですか。 または、デザインのルールを一度しか説明できず、それらを考慮してコミュニケーションを続けられる場合。

たとえば、Object Pascalには次のような構造があります。

 with (Object) begin a := 1; b := "foobar"; end; 


ただし、次のように言うことができてうれしいです。「今、オブジェクトを操作しているので、「a」プロパティを1に、「b」プロパティを「foobar」に設定してください」

「脂肪一滴ではありません!」


XMLに戻ると、DSL、たとえばLispでは、XMLドキュメントは次のようになります。

 ((book :isbn "123456789" :name "Some book" :year "2011") ((author :name "Freddy" :surname "Smith")) ((author :name "Bill" :surname "Joe")) (quote "This is the best unknown book I've ever quoted!" (author :name "Mr. Bob"))) 


括弧に注意を払わない場合、「脂肪の低下ではありません!」はありません。XMLの<、>、/、=文字に注意を払わず、終了タグの冗長性を省略すると、同じメッセージが表示されます。

 <book isbn='123456789' name='Some book' year='2011'><author name='Freddy' surname='Smith'/><author name='Bill' surname='Joe'/><quote>This is the best unknown book I&apos;ve ever quoted!<author name='Mr. Bob'/></quote></book> 


またはきれいに印刷して:

 <book isbn='123456789' name='Some book' year='2011'> <author name='Freddy' surname='Smith'/> <author name='Bill' surname='Joe'/> <quote>This is the best unknown book I&apos;ve ever quoted! <author name='Mr. Bob'/> </quote> </book> 


C ++で意味が似ているものを取得することは可能ですか?

結果


一般に、最終的には次のようになります。

  const xml::Tag book ("book"); const xml::Tag author ("author"); const xml::Tag quote ("quote"); xml::Element request = xml::Element() (book ("isbn", "123456789") ("name", "Some book") ("year", "2011"), xml::Element() (author ("name", "Freddy") ("surname", "Smith")) (author ("name", "Bill") ("surname", "Joe")) (quote, xml::Element() ("This is the best unknown book I've ever quoted!") (author ("name", "Mr. Bob")))); 


または、プロジェクトで再利用するタグのリストを選択すると:

 namespace library { namespace books { const xml::Tag book ("book"); const xml::Tag author ("author"); const xml::Tag quote ("quote"); } } 


それから:

  namespace bks = ::library::books; xml::Element request = xml::Element() (bks::book ("isbn", "123456789") ("name", "Some book") ("year", "2011"), xml::Element() (bks::author ("name", "Freddy") ("surname", "Smith")) (bks::author ("name", "Bill") ("surname", "Joe")) (bks::quote, xml::Element() ("This is the best unknown book I've ever quoted!") (bks::author ("name", "Mr. Bob")))); 


冗長性を完全に取り除くことはできませんでした(結局、アタッチメントポイントでxml::Element()を指定する必要があります)。 しかし、進歩があります。TEGであるタグごとに繰り返す必要はありません。構築中のドキュメント全体の分析を収集し、きれいな印刷などを行うことができます。オブジェクト、より正確には、共通ルート。

結論


このようなDSLのインターフェイスは、次のもので構成できます。

  template <class T> void addContent(const T & content); void addEmptyTag(const Tag & tag); template <class T> void addTag(const Tag & tag, const T & content); void addTagElement(const Tag & tag, const Element & element); 


  template <class T> inline Element & operator () (const T & content) { addContent(content); return *this; } inline Element & operator () (const Tag & tag) { addEmptyTag(tag); return *this; } template <class T> inline Element & operator () (const Tag & tag, const T & content) { addTag(tag, content); return *this; } template <> inline Element & operator () (const Tag & tag, const Element & element) { addTagElement(tag, element); return *this; } 



そして今では、DSLで書かれたコードをXMLでコンパイルするためのさまざまな実装を発明するだけです。 ラボオプションの例を次に示します。

  template <class T> void addContent(const T & content) { ::std::ostringstream oss; oss << content; buf += http_encode(oss.str()); } void addEmptyTag(const Tag & tag) { buf += createNode(tag.name, "", tag.args); } template <class T> void addTag(const Tag & tag, const T & content) { ::std::ostringstream oss; oss << content; buf += createNode(tag.name, oss.str(), tag.args); } void addTagElement(const Tag & tag, const Element & element) { buf += clearCreateNode(tag.name, element.str(), tag.args); } 


完全を期すために、クラスコードにTagます。

 class Tag { public: // DSL : template <class K> Tag & operator () (const K & attr_name) { ::std::ostringstream oss; oss << " " << attr_name; args += oss.str(); return *this; } template <class K, class T> Tag & operator () (const K & attr_name, const T & value) { ::std::ostringstream oss; oss << " " << attr_name << "='" << value << "'"; args += oss.str(); return *this; } //   DSL : template <class K> Tag operator () (const K & attr_name) const { Tag copy(*this); return copy (attr_name); } template <class K, class T> Tag operator () (const K & attr_name, const T & value) const { Tag copy(*this); return copy (attr_name, value); } //   : template <class T> Tag(const T & name) : name(name) {} Tag(const Tag & ref) : name(ref.name), args(ref.args) {} ::std::string name; ::std::string args; }; 


...そして、シンプルなXMLドキュメントをこのDSLのコードに変換するXSLTの例:

 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions"> <xsl:output method="text" version="1.0" encoding="windows-1251" indent="no"/> <xsl:template match="/"> <xsl:apply-templates/> ; </xsl:template> <xsl:template match="text()">"<xsl:value-of select="."/>"</xsl:template> <xsl:template match="attribute::*"> ("<xsl:value-of select="name()"/>", "<xsl:value-of select="."/>")</xsl:template> <xsl:template match="*"><xsl:if test="child::node()">xml::Element()</xsl:if> (<xsl:value-of select="name()"/><xsl:apply-templates select="attribute::*"/><xsl:if test="child::node()">, <xsl:apply-templates/></xsl:if>)</xsl:template> </xsl:stylesheet> 


その結果、C ++向けのDSLをほとんど血を流さずに作成したことがわかりました。 次の項目は、C ++にSQLコードを含めることです...

過ごした時間をありがとう、私はあなたがトピックが好きであることを願っています。

Source: https://habr.com/ru/post/J128933/


All Articles