Skip to content

Instantly share code, notes, and snippets.

@meetingcpp
Created February 20, 2016 16:10
Show Gist options
  • Select an option

  • Save meetingcpp/51543bb73694427215f2 to your computer and use it in GitHub Desktop.

Select an option

Save meetingcpp/51543bb73694427215f2 to your computer and use it in GitHub Desktop.
boostache example with boost fusion and adapted struct
/**
* \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