A Powerful Templating Framework in C++, with a command-line tool and Python module, written by Alvaro J. Genial.
Synth is a template framework—a set of components that can be mixed and matched to build the right functionality; furthermore, components are loosely-coupled, designed to be both extensible and replaceable.
Version 1 is released and stable.
Synth blurs the line between compile-time and runtime, and it does so by blending three worlds: (a) the static C++ type system; (b) the dynamic values that need to be manipulated and formatted, including those from other languages; and (c) the templates to do so. The name is an allusion to this synthesis process, which combines values to generate new ones (streams, files, strings, numbers, etc.)
echo '{"user": "Dolph Lundgren"}' > context.json
echo 'Howdy, {{ user }}!' > template.txt
cat template.txt | synth -e django -c context.jsonOr on Windows:
echo {"user":"Dolph Lundgren"} > context.json
echo Howdy, {{ user }}! > template.txt
type template.txt | synth -e django -c context.json
import synth, sys
def simple_tmpl_example():
t = synth.Template('Howdy, <TMPL_VAR user>!', 'tmpl')
c = {'user': 'Dolph Lundgren'}
# Render to different destinations:
t.render_to_path("greeting.txt", c)
t.render_to_file(sys.stdout, c)
print(t.render_to_string(c))#include <map>
#include <string>
#include <iostream>
#include <ajg/synth.hpp>
typedef ajg::synth::default_traits<char> traits_type;
typedef ajg::synth::engines::ssi::engine<traits_type> engine_type;
typedef ajg::synth::templates::string_template<engine_type> template_type;
int main() {
// Parse the template.
template_type const t(
"Howdy, <!--#echo var='user' -->! "
"Your balance is <!--#echo var='points' -->.");
// Create some data.
std::map<std::string, engine_type::value_type> m;
m["user"] = "Dolph Lundgren";
m["points"] = 42;
// Render to different destinations:
t.render_to_stream(std::cout, m);
t.render_to_path("greeting.txt", m);
std::cout << t.render_to_string(m);
return 0;
}USAGE: synth [OPTIONS...]
Options:
-h, --help print help message
-v, --version print library version
-c file, --context=file contextual data *.{ini,json,xml}
-e name, --engine=name template engine {django,ssi,tmpl}
-d path, --directory=path template location(s) (default: '.')
Using Homebrew:
brew install https://raw.github.com/ajg/synth/master/synth.rb
# Note that you must append `--with-python` to install the Python module.
Using Chocolatey:
choco install synth
Support for other system package managers like Apt or Yum is welcome.
Note that these will only install the Python module.
Using Pip:
pip install pip --upgrade # Ensure wheel support
pip install synth # Prefix with `sudo` if needed.
Using Easy Install:
easy_install synth # Prefix with `sudo` if needed.
If possible, use Pip instead.
- Ensure you have the following:
- A tool to get the source (
git,curl, a browser, etc.) - A system to build it with (SCons or Visual Studio)
- A C++ compiler (
clang,gcc,msvc, etc.) In particular, Synth is known to compile with:gccversions4.2.1,4.6.3and4.7.2clangversions3.0and3.3msvcversion12.0(VS2013)
-
Get the source (e.g. the latest, as shown here):
git clone --depth 1 --recursive https://github.com/ajg/synth.git && cd synth -
Optionally, build the command-line tool:
scons synth # Add debug=1 to generate debugging symbols & disable optimizations.(Note that if you are hell bent on it, you can use a different version of Boost; see Infrequently Asked Questions.)
-
Optionally, build (and install) the Python module:
python setup.py install # Prefix with `sudo` if needed.
django: An implementation of Django Templates.ssi: An implementation of Server Side Includes.tmpl: An implementation of Perl's HTML::Template.null: A minimal implementation that does nothing (i.e. a no-op.)
command_linepython
buffer_templatepath_templatestream_templatestring_template
arrayT[N]T[]boost::array
boolbool
complexstd::complex
containerdequestd::deque
liststd::list
mapstd::mapstd::multimap
setstd::setstd::multiset
stackstd::stack
vectorstd::vector
memorystd::auto_ptr
noneboost::none_t
numericcharchar signedchar unsignedshortshort unsignedintint unsignedlonglong unsignedwchar_t(When available, unless disabled byAJG_SYNTH_CONFIG_NO_WCHAR_T)long long(When available, unless disabled byAJG_SYNTH_CONFIG_NO_LONG_LONG)long long unsigned(When available, unless disabled byAJG_SYNTH_CONFIG_NO_LONG_LONG)__int64(msvc-only.)__int64 unsigned(msvc-only.)floatdoublelong double
optionalboost::optional
ptimeboost::posix_time::ptime
ptreeboost::property_tree::ptree
pointervoid*T*
refboost::reference_wrapper
smart_ptrboost::scoped_arrayboost::scoped_ptrboost::shared_arrayboost::shared_ptr
stringstd::basic_stringchar*char[N]wchar_t*(When available, unless disabled byAJG_SYNTH_CONFIG_NO_WCHAR_T)wchar_t[N](When available, unless disabled byAJG_SYNTH_CONFIG_NO_WCHAR_T)
utilitystd::pair
variantboost::variant
adapters::base_adapterbindings::base_bindingengines::base_enginetemplates::base_template
engines::contextengines::optionsengines::stateengines::valuetemplates::cache
AJG_SYNTH_VERSION(e.g.1.2.3)AJG_SYNTH_VERSION_MAJOR(e.g.1)AJG_SYNTH_VERSION_MINOR(e.g.2)AJG_SYNTH_VERSION_PATCH(e.g.3)AJG_SYNTH_VERSION_SEQ(e.g.(1)(2)(3))AJG_SYNTH_VERSION_TUPLE(e.g.(1, 2, 3))AJG_SYNTH_VERSION_ARRAY(e.g.(3, (1, 2, 3)))AJG_SYNTH_VERSION_STRING(e.g."1.2.3")
AJG_SYNTH_CONFIG_NO_WCHAR_T(default: automatically determined)AJG_SYNTH_CONFIG_NO_LONG_LONG(default: automatically determined)AJG_SYNTH_CONFIG_NO_DEBUG(default: automatically determined)AJG_SYNTH_CONFIG_NO_WINDOWS_H(default: not defined)AJG_SYNTH_CONFIG_DEFAULT_CHAR_TYPE(default:char)AJG_SYNTH_CONFIG_MAX_FRAMES(default:1024)AJG_SYNTH_CONFIG_HANDLE_ASSERT(default:BOOST_ASSERT)AJG_SYNTH_CONFIG_HANDLE_EXCEPTION(default:boost::throw_exception)
inijsonxml
django::builtin_tags::autoescape_tagdjango::builtin_tags::block_tagdjango::builtin_tags::comment_tagdjango::builtin_tags::csrf_token_tagdjango::builtin_tags::cycle_tagdjango::builtin_tags::cycle_as_tagdjango::builtin_tags::cycle_as_silent_tagdjango::builtin_tags::debug_tagdjango::builtin_tags::extends_tagdjango::builtin_tags::filter_tagdjango::builtin_tags::firstof_tagdjango::builtin_tags::for_tagdjango::builtin_tags::for_empty_tagdjango::builtin_tags::if_tagdjango::builtin_tags::ifchanged_tagdjango::builtin_tags::ifequal_tagdjango::builtin_tags::ifnotequal_tagdjango::builtin_tags::include_tagdjango::builtin_tags::include_with_tagdjango::builtin_tags::include_with_only_tagdjango::builtin_tags::library_tagdjango::builtin_tags::load_tagdjango::builtin_tags::load_from_tagdjango::builtin_tags::now_tagdjango::builtin_tags::regroup_tagdjango::builtin_tags::spaceless_tagdjango::builtin_tags::ssi_tagdjango::builtin_tags::templatetag_tagdjango::builtin_tags::url_tagdjango::builtin_tags::url_as_tagdjango::builtin_tags::variable_tagdjango::builtin_tags::verbatim_tagdjango::builtin_tags::widthratio_tagdjango::builtin_tags::with_tag
django::builtin_filters::add_filterdjango::builtin_filters::addslashes_filterdjango::builtin_filters::capfirst_filterdjango::builtin_filters::center_filterdjango::builtin_filters::cut_filterdjango::builtin_filters::date_filterdjango::builtin_filters::default_filterdjango::builtin_filters::default_if_none_filterdjango::builtin_filters::dictsort_filterdjango::builtin_filters::dictsortreversed_filterdjango::builtin_filters::divisibleby_filterdjango::builtin_filters::escape_filterdjango::builtin_filters::escapejs_filterdjango::builtin_filters::filesizeformat_filterdjango::builtin_filters::first_filterdjango::builtin_filters::fix_ampersands_filterdjango::builtin_filters::floatformat_filterdjango::builtin_filters::force_escape_filterdjango::builtin_filters::get_digit_filterdjango::builtin_filters::iriencode_filterdjango::builtin_filters::join_filterdjango::builtin_filters::last_filterdjango::builtin_filters::length_filterdjango::builtin_filters::length_is_filterdjango::builtin_filters::linebreaks_filterdjango::builtin_filters::linebreaksbr_filterdjango::builtin_filters::linenumbers_filterdjango::builtin_filters::ljust_filterdjango::builtin_filters::lower_filterdjango::builtin_filters::make_list_filterdjango::builtin_filters::phone2numeric_filterdjango::builtin_filters::pluralize_filterdjango::builtin_filters::pprint_filterdjango::builtin_filters::random_filterdjango::builtin_filters::removetags_filterdjango::builtin_filters::rjust_filterdjango::builtin_filters::safe_filterdjango::builtin_filters::safeseq_filterdjango::builtin_filters::slice_filterdjango::builtin_filters::slugify_filterdjango::builtin_filters::stringformat_filterdjango::builtin_filters::striptags_filterdjango::builtin_filters::time_filterdjango::builtin_filters::timesince_filterdjango::builtin_filters::timeuntil_filterdjango::builtin_filters::title_filterdjango::builtin_filters::truncatechars_filterdjango::builtin_filters::truncatechars_html_filterdjango::builtin_filters::truncatewords_filterdjango::builtin_filters::truncatewords_html_filterdjango::builtin_filters::unordered_list_filterdjango::builtin_filters::upper_filterdjango::builtin_filters::urlencode_filterdjango::builtin_filters::urlize_filterdjango::builtin_filters::urlizetrunc_filterdjango::builtin_filters::wordcount_filterdjango::builtin_filters::wordwrap_filterdjango::builtin_filters::yesno_filter
context::formats["TEMPLATE_STRING_IF_INVALID"](default:"")context::formats["DATE_FORMAT"](default:"N j, Y")context::formats["DATETIME_FORMAT"](default:"N j, Y, P")context::formats["MONTH_DAY_FORMAT"](default:"F j")context::formats["SHORT_DATE_FORMAT"](default:"m/d/Y")context::formats["SHORT_DATETIME_FORMAT"](default:"m/d/Y P")context::formats["TIME_FORMAT"](default:"P")context::formats["YEAR_MONTH_FORMAT"](default:"F Y")
(Note: Django's TEMPLATE_DEBUG and TEMPLATE_DIRS are handled through options::debug and options::directories, respectively.)
ssi::builtin_tags::config_tagssi::builtin_tags::echo_tagssi::builtin_tags::exec_tagssi::builtin_tags::fsize_tagssi::builtin_tags::flastmod_tagssi::builtin_tags::if_tagssi::builtin_tags::include_tagssi::builtin_tags::printenv_tagssi::builtin_tags::set_tag
context::formats["echomsg"](default:"(none)")context::formats["errormsg"](default:"[an error occurred while processing this directive]")context::formats["sizefmt"](default:"bytes")context::formats["timefmt"](default:"%A, %d-%b-%Y %H:%M:%S %Z")
tmpl::builtin_tags::comment_tag(Technically, part ofctpp)tmpl::builtin_tags::if_tagtmpl::builtin_tags::include_tagtmpl::builtin_tags::loop_tagtmpl::builtin_tags::unless_tagtmpl::builtin_tags::variable_tag
options::debug(default:false)options::directories(default:".")options::libraries(for dynamic tags & filters)options::loaders(for dynamic library loading)options::resolvers(for dynamic URL resolution and reversal)options::caching(a bit mask of caching behavior)caching_nonecaching_allcaching_pathscaching_bufferscaching_stringscaching_per_threadcaching_per_process
-
Build:
- Pre-commit script/hook
- GCC:
- [4.9+] Add
-fsanitize=undefinedwhen available
- [4.9+] Add
- Visual Studio:
- [2013] Pass /W4 cleanly
- [2013] Pass /Wall cleanly
- [2012] Create solution & projects
-
Distribution:
- RPM package
- Apt package
- Yum package
- MacPort support
- Standalone pre-built Darwin binaries
- Standalone pre-built Windows binaries
- MSI installer
-
Documentation:
- Produce Boost-compatible documentation
- Create
conf.py(et al.) to enable ReadTheDocs
-
Testing:
- Rewrite the majority of unit tests as a set of
.in/.outfiles - Add exhaustive date/time formatting tests
- Add way to specify expected failures; re-enable commented out tests
- Rewrite the majority of unit tests as a set of
-
Optimization:
- Compare benefit/cost of
-O,-O2,-O3and-Ofast - Investigate using thread locals instead of statics for medium-expense objects
- Investigate
-fvisibility-inlines-hidden - Replace
ostream << string(a, b)constructs withstd::ostream_iterator+std::copy
- Compare benefit/cost of
-
Bindings:
- Command-line:
- Allow specifying formats option
- Allow specifying debug option
- Allow named input files
- Allow named output files
- [v1+] Allow using arbitrary programs as tags
- [v1+] Allow using arbitrary programs as filters
- Python:
- [v1] Set docstrings where appropriate
- Support is_safe, needs_autoescape, and expects_localtime in custom filters
- Other:
- Command-line:
-
Engines:
- Django:
loremtag- Make markers dynamically configurable
- Native
i18nlibrary - Native
l10nlibrary - Native
tzlibrary - Native
staticlibrary - Native
staticfileslibrary - Native
humanizelibrary - Native
webdesignlibrary
- SSI:
- Implement additional tags from Jigsaw
- TMPL:
- Consider renaming
html
- Consider renaming
- Other:
- Create
ctppengine - [v1+] Create
jinja2engine - [v1+] Create
cheetahengine - [v1+] Create
xsltengine
- Create
- Django:
-
Adapters:
- Adapter for
boost::tuple - Adapter for
boost::any - Adapter for
boost::function - Adapter for
boost::tribool - Adapters for
boost::fusionsequences - Adapters for
boost::local_timetypes - Adapter for
boost::intrusive_ptr - Support for
boost::enable_shared_from_this - [v2] Adapters for
c++11types - [v2] Adapters for
c++14types
- Adapter for
-
Templates:
- Create new
file_template(usingFILE* file)- Add
render_to_fileto base_template - Add
render_fileto engines
- Add
- Create
descriptor_template(usingint descriptor)- Add
render_to_descriptorto base_template - Add
render_descriptorto engines
- Add
- Create new
-
Refactoring:
- Make documentation comments uniformly
/// - Replace local version of Boost with minimal, shallow submodules once boostorg/boost is ready
- Move
render_tagandbuiltin_tags_tobase_engine::kernel - Move
render_blocktobase_engine::kernel - Remove all no-op tags (e.g.
cycle_as_silent_tag) - Fold all variant tags into their main version (e.g.
load_from_tag) - Modernize codebase to with C++11 support:
- Translate macros to variadic templates
- Replace
boost::assignuse with aggregate initializers - Remove complex redundant
typedefs in favor ofauto - Replace
<boost/cstdint.hpp>with<cstdint> - Replace Boost.Random with
<random> - Replace Boost.Atomic with
<atomic> - Consider switching to unordered_map/unordered_set where possible
- [v1+] Add
AJG_SYNTH_PRAGMAmacro that invokes_Pragmaor__pragma(MSVC) as needed- Add
AJG_SYNTH_PRAGMA(once)to all header files and see if it speeds up compilations
- Add
- [v1+] Sort
#includes alphabetically - [v1+] Run entire C++ codebase through clang-format
- Change
classin template signatures totypename - [v1] Reformat all
operator _()'s tooperator_() - [v1+] Remove header guard closing comments
- [v1+] Remove namespace closing comments
- [v2+] Factor out values & adapters into separate library for generic language interop
- Make documentation comments uniformly
-
Q: Can I use a version of Boost other than the one bundled?
- A: Yes, you can, though note that unless you're already using Boost in your project, there is usually no good reason to; anyway:
- On most systems, you can build Synth with the system's Boost by passing
boost=systemtoscons. - On Windows, you'll need to edit the project file(s) in Visual Studio and set the include and library directories to point to the existing Boost installation.
- The Python module does not support this option yet.
- On most systems, you can build Synth with the system's Boost by passing
- A: Yes, you can, though note that unless you're already using Boost in your project, there is usually no good reason to; anyway:
-
Q: How can I install a system-wide version of Boost?
- A: Here are some suggestions:
- Using Homebrew:
brew install boost(Append--with-pythonfor Boost.Python support.) - Using Apt:
sudo apt-get install libboost-all-dev - Using Yum:
sudo yum install boost-devel - On Windows, try
PM> Install-Package boostin the Package Manager Console
- Using Homebrew:
- A: Here are some suggestions:
This library is distributed under the Boost LICENSE.
