Created
February 20, 2016 16:10
-
-
Save meetingcpp/51543bb73694427215f2 to your computer and use it in GitHub Desktop.
boostache example with boost fusion and adapted struct
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * \file boostache_adapted_fusion_example.cpp | |
| * | |
| * Copyright 2016 Jens Weller : meetingcpp.com | |
| * | |
| * Distributed under the Boost Software License, Version 1.0. | |
| */ | |
| #include <iostream> | |
| #include <boost/spirit/include/support_extended_variant.hpp> | |
| #include <boost/fusion/include/adapt_struct.hpp> | |
| #include <boost/mpl/range_c.hpp> | |
| #include <boost/fusion/include/for_each.hpp> | |
| #include <boost/fusion/include/mpl.hpp> | |
| #define BOOSTACHE_USE_CPP11 | |
| #include <boost/boostache/boostache.hpp> | |
| #include <boost/boostache/frontend/stache/grammar_def.hpp> // need to work out header only syntax | |
| #include <boost/boostache/stache.hpp> | |
| #include <boost/boostache/model/helper.hpp> | |
| namespace boostache = boost::boostache; | |
| std::string tpl = R"(<html> | |
| <head> | |
| {{#cssfiles}} | |
| <link rel={{rel}} type="{{type}}" href="{{content}}" media="{{media}}"> | |
| {{/cssfiles}} | |
| {{#jsfiles}} | |
| <script href="{{file}}"/> | |
| {{/jsfiles}} | |
| </head> | |
| <body> | |
| {{bodyhtml}} | |
| </body> | |
| <html>)"; | |
| struct HeadTag | |
| { | |
| std::string rel,type,media,content; | |
| HeadTag(const std::string& rel,const std::string& type, const std::string& media,const std::string& content):rel(rel),type(type),media(media),content(content){} | |
| }; | |
| BOOST_FUSION_ADAPT_STRUCT( | |
| HeadTag, | |
| (std::string,rel) | |
| (std::string,type) | |
| (std::string,media) | |
| (std::string,content)) | |
| using strpair = std::pair<std::string,std::string>; | |
| namespace boost{namespace boostache{namespace extension{ | |
| using pair_attribute = plain_attribute; | |
| bool test( strpair const & context, std::string const & tag | |
| , pair_attribute) | |
| { | |
| return context.first == tag; | |
| } | |
| bool test( strpair const & | |
| , pair_attribute) | |
| { | |
| return true; | |
| } | |
| template<class Stream> | |
| void render(Stream& stream,const strpair& context, const std::string& name,pair_attribute) | |
| { | |
| if(context.first == name) | |
| stream << context.second; | |
| } | |
| using headtag_attribute = plain_attribute; | |
| template<> | |
| struct render_category<HeadTag> : boost::mpl::identity<headtag_attribute>{}; | |
| // REALLY UGLY fusion code, but it works (and compiles forever) | |
| template<class Sequence> | |
| struct has_field | |
| { | |
| mutable bool found = false; | |
| const std::string fieldname; | |
| template<class Index> | |
| void operator()(Index idx)const | |
| { | |
| std::string field_name = fusion::extension::struct_member_name<Sequence,idx>::call(); | |
| if(!found) | |
| found = fieldname == field_name; | |
| } | |
| has_field(const std::string& fieldname):fieldname(fieldname){} | |
| }; | |
| template<class Sequence> | |
| bool find_field(const std::string& name, const Sequence&) | |
| { | |
| typedef mpl::range_c<unsigned, 0, fusion::result_of::size<Sequence>::value > Indices; | |
| has_field<Sequence> f(name); | |
| boost::fusion::for_each(Indices(),f); | |
| return f.found; | |
| } | |
| template<class Sequence, class Value = std::string> | |
| struct has_value | |
| { | |
| mutable bool found = false; | |
| const std::string fieldname; | |
| mutable Value v; | |
| const Sequence& seq; | |
| template<class Index> | |
| void operator()(Index idx)const | |
| { | |
| std::string field_name = fusion::extension::struct_member_name<Sequence,idx>::call(); | |
| if(!found) | |
| { | |
| found = fieldname == field_name; | |
| if(found) | |
| v = boost::fusion::at_c<idx>(seq); | |
| } | |
| } | |
| has_value(const std::string& fieldname,const Sequence& seq):fieldname(fieldname),seq(seq){} | |
| }; | |
| template<class Seq> | |
| std::string find_value(const std::string& name, const Seq& seq) | |
| { | |
| has_value<Seq> value(name,seq); | |
| typedef mpl::range_c<unsigned, 0, fusion::result_of::size<Seq>::value > Indices; | |
| boost::fusion::for_each(Indices(),value); | |
| if(value.found) | |
| return value.v; | |
| return ""; | |
| }//*/ | |
| bool test( HeadTag const & context, std::string const & tag | |
| , headtag_attribute) | |
| { | |
| //return has_tag<HeadTag>(tag); | |
| return find_field(tag,context); | |
| //return "rel" == tag || "media" == tag || "content" == tag || "type" == tag; | |
| } | |
| bool test( HeadTag const & | |
| , headtag_attribute) | |
| { | |
| return true; | |
| } | |
| template<class Stream> | |
| void render(Stream& stream,const HeadTag& context, const std::string& name,headtag_attribute) | |
| { | |
| //render_tag(stream,context,name); | |
| stream << find_value(name,context); | |
| // if("rel" == name) | |
| // stream << context.rel; | |
| // else if("media" == name) | |
| // stream << context.media; | |
| // else if("content" == name) | |
| // stream << context.content; | |
| // else if("type" == name) | |
| // stream << context.type; | |
| } | |
| }}} | |
| template<class T> | |
| std::string generate_boostache(const std::string& tpl,const T& data) | |
| { | |
| auto iter = tpl.begin(); | |
| auto templ = boostache::load_template<boostache::format::stache>(iter, tpl.end()); | |
| std::stringstream stream; | |
| boostache::generate(stream, templ, data); | |
| return stream.str(); | |
| } | |
| struct page_t; | |
| using map_t = std::map<std::string,page_t>; | |
| using cssfiles_t = std::vector<HeadTag>; | |
| using file_t = std::vector<strpair>; | |
| struct page_t : boost::spirit::extended_variant< std::string | |
| , map_t | |
| , cssfiles_t | |
| , file_t | |
| > | |
| { | |
| page_t() : base_type() {} | |
| page_t(std::string const & rhs) : base_type(rhs) {} | |
| page_t(char const * rhs) : base_type(std::string{rhs}) {} | |
| page_t(cssfiles_t const & rhs) : base_type(rhs) {} | |
| page_t(file_t const & rhs) : base_type(rhs) {} | |
| page_t(map_t const & rhs) : base_type(rhs) {} | |
| }; | |
| std::string generatePage(const std::string& tpl,const std::string& bodyhtml) | |
| { | |
| cssfiles_t css; | |
| css.emplace_back("foobar","text/css","main.css","all"); | |
| css.emplace_back("df","text/css","print.css","print"); | |
| css.emplace_back("foodfadbar","text/css","mobile.css","mobile"); | |
| css.emplace_back("dfadf","text/css","menu.css","all"); | |
| file_t js; | |
| js.emplace_back("file","js.js"); | |
| map_t page = {{"bodyhtml", bodyhtml}, | |
| {"cssfiles" , css}, | |
| {"jsfiles" , js } }; | |
| for(auto pair: page) | |
| std::cout << pair.first << std::endl; | |
| return generate_boostache(tpl,page); | |
| } | |
| int main() | |
| { | |
| std::cout << generatePage(tpl,"<p>test</p>"); | |
| return 0; | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment