1

私のプロジェクトの1つでは、社内の単純化されたXMLファイルを書くためのより一般的なアプローチを実現しようとしています。このため、私は首尾よくboost-fusionを使用しました。引数を文字列リテラルに変換してボイラープレートコードを生成する

新しいXMLファイル形式ごとに、クライアントは次のように記述する必要があります。 XMLファイルにはタグPersonとタグCompanyが含まれていると仮定します。ユーザーはまだ自動的に生成されなければならないxmlTagsを定義する必要があるとして

#include <boost/fusion/include/define_struct.hpp> 
#include <boost/variant.hpp> 
#include <map> 
#include <vector> 

BOOST_FUSION_DEFINE_STRUCT(
(), 
Person, 
(std::string, name) // name is mandatory for all tags 
(int, age)) 

BOOST_FUSION_DEFINE_STRUCT(
(), 
Company, 
(std::string, name) // name is mandatory for all tags 
(int, noEmployees) 
(std::string, location) 
) 

typedef boost::variant<Person, Company> Types; 

std::vector<std::pair<Types, std::vector<std::string>>> xmlTags = 
{ 
    {Person(), {"name", "age"}}, 
    {Company(), {"name", "noEmployees", "location"}}, 
}; 

int main(int argc, char**args) { 
} 

私は、上記の溶液でまだかなり満足していません。また、Typesも生成する必要があります。クライアントは、マップを修正して誤ったXMLファイルを作成したり、XMLリーダー/ライターをクラッシュさせたりすることを忘れる可能性があります。私にとってはすべて、この定型的なコードを生成する

DEFINE_XML_TAGS(
    XML_TAG(
     Person, 
     (int, age) 
    ) 
    XML_TAG(
     Company, 
     (int, noEmployees) 
     (std::string, location) 
    ) 
) 

良い解決策は、次のようになります。私はBoost-Preprocessorがこの良い解決策の一部であると思います。

しかし、私は望む結果をどのように達成するか考えていません。今までこのライブラリを使用していない。幸いにも、私たちのコンパイラは可変的なテンプレート引数をサポートしています。

希望の結果を達成する方法を知っている人はいますか?

答えて

1

Boost.Preprocessorライブラリの使用に興味がある場合は、2つの基本的な "データタイプ"であるsequencetupleに慣れる必要があります。ライブラリーが使用するマクロの全リストは、参照セクションthe documentationで見つけることができます。私は以下で使用するものについて説明します。

インターフェイスには、XML_TAGDEFINE_XML_TAGSという2つのマクロがあります。
XML_TAGは本当にシンプルですが、引数を2組のカッコ内に置くだけです。これは、あなたが使用する多くのXML_TAGがタプル(struct_name,sequence_of_type_and_name_pairs)であるシーケンスに変換されることを引き起こします。
DEFINE_XML_TAGSはすべての作業を行うマクロです。それは3つのヘルパーマクロGENERATE_STRUCT_DEFS,GENERATE_VARIANT_OF_TYPESおよびGENERATE_XMLTAGSを使用します。

GENERATE_VARIANT_OF_TYPES
はタイプのカンマ区切りのリストを取得するためにENUMERATE_TYPES(TAG_SEQ)を呼び出します。 今すぐTAG_SEQ((Person,(int,age)))((Company,(int,noEmployees)(std::string,location)))です。Person,Companyが必要です。 BOOST_PP_ENUM(SEQ)はシーケンスを受け取り、その要素をコンマで区切って返します。だから、BOOST_PP_ENUM((Person)(Company))が必要です。 BOOST_PP_SEQ_FOR_EACH(MACRO,DATA,SEQ)は、SEQ内の各要素と、渡すDATAのいずれかを使用してMACROを呼び出します。したがってBOOST_PP_SEQ_FOR_EACH(GET_TYPE_SEQUENCE,_,TAG_SEQ)GET_TYPE_SEQUENCE(Person,(int,age))(Company,(int,noEmployees)(sd:string,location))と呼び出します。 GET_TYPE_SEQUENCEは、各タプルの最初の要素を取り、それをBOOST_PP_TUPLE_ELEMを使用して括弧のセットの中に置きます。

GENERATE_XML_TAGS
それは順番にGENERATE_ONE_PAIRを使用してSEQ_FOR_EACHを呼び出すGENERATE_PAIRSを呼び出します。 前のセクションで説明したように、GENERATE_ONE_PAIRは、タプル(struct_name、sequence_of_type_name_pairs)のそれぞれを取得します。名前の後に括弧を追加し、sequence_of_type_name_pairsでGENERATE_VECTOR_OF_MEMBER_NAMESを呼び出します。 GENERATE_VECTOR_OF_MEMBER_NAMESは最初に必須の "name"メンバを追加して、BOOST_PP_ENUMで上記のマクロと非常に似ていますが、タプルの現在のシーケンスには2組のかっこがないため、少しトリックする必要があるという違いがあります第3の手法ではhere)。 GENERATE_MEMBER_NAME_SEQUENCEは、メンバーの名前を単純に取り、それを文字列に変換し、そのまわりに括弧を入れます。

GENERATE_STRUCT_DEFS
BOOST_PP_REPEAT(N,MACRO,DATA) MACRO N回、渡すデータと現在の繰り返しのインデックスを呼び出します。 GENERATE_ONE_STRUCT_DEFはシーケンスのインデックス番目の要素をとり、最初に構造体の名前と最後に型名ペアのシーケンスをとり、これらの値を持つDO_GENERATE_ONE_STRUCT_DEFを呼び出します。最後にDO_GENERATE_ONE_STRUCT_DEFは、BOOST_FUSION_DEFINE_STRUCTマクロ呼び出しを構築します。

バグがBOOST_FUSION_ADAPT_STRUCT_NAMESPACE_DEFINITION_ENDであることを、私は思いますが、私は必ずするのに十分精通していませんよ。それはちょうど BOOST_PP_REPEATを使用するべきであると思うとき BOOST_PP_REPEAT_1を直接使用する。私は定義されていないと BOOST_PP_REPEATを使用してマクロを再定義し、すべてが動作するようだが、おそらく はそれを盲目的に信用すべきではありません。

Test Running on WandBox

define_xml_tags.hpp

#include <boost/fusion/include/define_struct.hpp> 
#include <boost/variant.hpp> 
#include <vector> 
#include <utility> 
#include <boost/preprocessor/cat.hpp> 
#include <boost/preprocessor/repetition/repeat.hpp> 
#include <boost/preprocessor/seq/enum.hpp> 
#include <boost/preprocessor/seq/for_each.hpp> 
#include <boost/preprocessor/seq/for_each_i.hpp> 
#include <boost/preprocessor/seq/size.hpp> 
#include <boost/preprocessor/stringize.hpp> 
#include <boost/preprocessor/tuple/elem.hpp> 

//I think there is a bug in the original macro, it uses BOOST_PP_REPEAT_1 where I think it should use BOOST_PP_REPEAT, but I don't know enough to know for sure 
#undef BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_DEFINITION_END 

#define BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_DEFINITION_END(NAMESPACE_SEQ)  \ 
    BOOST_PP_REPEAT(               \ 
     BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(NAMESPACE_SEQ)),       \ 
     BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_END_I,        \ 
     _) 

//helps form a SEQUENCE of TUPLES 
#define XML_TAG(NAME,MEMBER_SEQ) ((NAME,MEMBER_SEQ)) 

//helpers for GENERATE_STRUCT_DEFS, read from the bottom to the top 
#define DO_GENERATE_ONE_STRUCT_DEF(NAME,MEMBER_SEQ) \ 
BOOST_FUSION_DEFINE_STRUCT((), NAME, (std::string, name) MEMBER_SEQ) 

#define GENERATE_ONE_STRUCT_DEF(Z,INDEX,TAG_SEQ) \ 
DO_GENERATE_ONE_STRUCT_DEF(BOOST_PP_TUPLE_ELEM(2,0,BOOST_PP_SEQ_ELEM(INDEX,TAG_SEQ)), BOOST_PP_TUPLE_ELEM(2,1,BOOST_PP_SEQ_ELEM(INDEX,TAG_SEQ))) 

#define GENERATE_STRUCT_DEFS(TAG_SEQ) \ 
BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(TAG_SEQ),GENERATE_ONE_STRUCT_DEF,TAG_SEQ) 


//helpers for GENERATE_VARIANT_OF_TYPES, bottom to top 
#define GET_TYPE_SEQUENCE(R,DATA,NAME_MEMBERSEQ_TUPLE) \ 
(BOOST_PP_TUPLE_ELEM(2,0,NAME_MEMBERSEQ_TUPLE)) 

#define ENUMERATE_TYPES(TAG_SEQ) \ 
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(GET_TYPE_SEQUENCE,_,TAG_SEQ)) 

#define GENERATE_VARIANT_OF_TYPES(TAG_SEQ) \ 
typedef boost::variant<ENUMERATE_TYPES(TAG_SEQ)> Types; 


//helpers for GENERATE_XMLTAGS, go from bottom to top in order to understand 

//Heavily "inspired" from BOOST_FUSION_ADAPT_STRUCT 
#define GENERATE_NAME_SEQUENCE_FILLER_0(X, Y) \ 
    ((X, Y)) GENERATE_NAME_SEQUENCE_FILLER_1 
#define GENERATE_NAME_SEQUENCE_FILLER_1(X, Y) \ 
    ((X, Y)) GENERATE_NAME_SEQUENCE_FILLER_0 
#define GENERATE_NAME_SEQUENCE_FILLER_0_END 
#define GENERATE_NAME_SEQUENCE_FILLER_1_END 

#define GENERATE_MEMBER_NAME_SEQUENCE(R,DATA,INDEX,TYPE_NAME_TUPLE) (BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(2,1,TYPE_NAME_TUPLE))) 

#define GENERATE_VECTOR_OF_MEMBER_NAMES(MEMBER_SEQ) \ 
{ "name", BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(GENERATE_MEMBER_NAME_SEQUENCE,_,BOOST_PP_CAT(GENERATE_NAME_SEQUENCE_FILLER_0 MEMBER_SEQ,_END))) } 

#define GENERATE_ONE_PAIR(R,DATA,NAME_MEMBERSEQ_TUPLE) \ 
{ BOOST_PP_TUPLE_ELEM(2,0,NAME_MEMBERSEQ_TUPLE)(), GENERATE_VECTOR_OF_MEMBER_NAMES(BOOST_PP_TUPLE_ELEM(2,1,NAME_MEMBERSEQ_TUPLE)) }, 

#define GENERATE_PAIRS(TAG_SEQ) \ 
BOOST_PP_SEQ_FOR_EACH(GENERATE_ONE_PAIR,_,TAG_SEQ) 

#define GENERATE_XMLTAGS(TAG_SEQ) \ 
const std::vector<std::pair<Types,std::vector<std::string>>> xmlTags = { GENERATE_PAIRS(TAG_SEQ) }; 


//This is the actual macro, it simply invokes three different macros that do a different task each 
#define DEFINE_XML_TAGS(TAG_SEQ) \ 
GENERATE_STRUCT_DEFS(TAG_SEQ) \ 
GENERATE_VARIANT_OF_TYPES(TAG_SEQ) \ 
GENERATE_XMLTAGS(TAG_SEQ) 

main.cppに

#include <iostream> 
#include <boost/fusion/include/io.hpp> 
#include <boost/fusion/include/as_vector.hpp> 
#include <boost/variant/static_visitor.hpp> 

#include "define_xml_tags.hpp" 



DEFINE_XML_TAGS(
    XML_TAG(
     Person, 
     (int, age) 
    ) 
    XML_TAG(
     Company, 
     (int, noEmployees) 
     (std::string, location) 
    ) 
) 

struct printer : boost::static_visitor<void> { 
    void operator()(const Person& p) const 
    { 
     std::cout << "This is a person:" << boost::fusion::as_vector(p) << '\n'; 
    } 

    void operator()(const Company& c) const 
    { 
     std::cout << "This is a company:" << boost::fusion::as_vector(c) << '\n'; 
    } 
}; 

void identify(Types v) 
{ 
    boost::apply_visitor(printer(),v); 
} 


int main() 
{ 
    Person p; 
    p.name="John"; 
    p.age = 18; 

    identify(p); 

    Company c; 
    c.name="Mpany Co"; 
    c.noEmployees=123; 
    c.location="Fake St"; 
    identify(c); 


    std::cout << "\nChecking xmlTags:\n"; 
    for(const auto& pair : xmlTags) 
    { 
     identify(pair.first); 
     std::cout << "It has the following members:\n"; 
     for(const auto& str : pair.second) 
      std::cout << str << '\n'; 
    } 

    std::cout << std::endl; 
} 
+0

うわー。この詳細で徹底したソリューションに感謝します。それは本当に私のために働いています。マクロを消化するのには時間が必要ですが。私はまだ単純なものだけを理解していました。私は最初に思ったように、 'boost-preprocessor'はそれほど気高くはないことを認めなければなりません。 'XML_TAG'に' name'以外に他のプロパティが含まれていない場合には、まだまだ欠点があります。 'XML_TAG(Container)'の場合は私のために起こります。コンパイラはエラーC2065: '" BOOST_PP_SEQ_ENUM_0 ":宣言されていない識別子を示します。 私はこれを自分で処理しなければならないようです。 – Aleph0

+0

私は現時点で回答を修正する時間はありませんが、[this](http://melpon.org/wandbox/permlink/jJDFimOiXLIj2aE8)([Visual C++ on rextester](http:// rextester .com/KLC36940))は、 'name'以外のプロパティを持たない構造体の問題を解決します。 – llonesmiz

関連する問題