A Powerful C++ Templating Framework 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.
Beta, approaching a first full release.
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.jsonimport synth, sys
def simple_tmpl_example():
t = synth.Template('Howdy, <TMPL_VAR user>!', 'tmpl')
c = {'user': 'Dolph Lundgren'}
t.render_to_path("greeting.txt", c) # or, e.g.:
t.render_to_file(sys.stdout, c) # or, e.g.:
print t.render_to_string(c)#include <iostream>
#include <ajg/synth.hpp>
namespace synth = ajg::synth;
typedef synth::default_traits<char> traits_type;
typedef synth::engines::ssi::engine<traits_type> engine_type;
typedef synth::templates::string_template<engine_type> template_type;
int main() {
template_type const t("Howdy, <!--#echo var=\"user\" -->!");
template_type::context_type c;
c["user"] = "Dolph Lundgren";
// Render to different destinations:
t.render_to_stream(std::cout);
t.render_to_path("greeting.txt", c);
std::cout << t.render_to_string(c);
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}
-a, --autoescape[=bool] automatically escape values (default: 'true')
-d path, --directory=path template location(s) (default: '.')
-r text, --replacement=text replaces missing values (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.
(Support for other system package managers like Apt or Yum is welcome.)
Using Pip:
pip install synth # Prefix with `sudo` if needed.
Using Easy Install:
easy_install synth # Prefix with `sudo` if needed.
(Pre-built binary releases for common platforms are welcome.)
- 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
char_templatepath_templatestream_templatestring_template
array[N](Native static array.)[](Native dynamic array.)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
pointer*(Native pointer.)
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
inijsonxml
base_adapterbindings::base_bindingengines::base_enginetemplates::base_template
value_facade
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)
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::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_tagdjango::builtin_tags::library_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
django::options::autoescapedjango::options::nonbreaking_spacedjango::options::default_value(forTEMPLATE_STRING_IF_INVALID)django::options::formats(forTIME_FORMAT,DATE_FORMAT, etc.)django::options::debug(forTEMPLATE_DEBUG)django::options::directories(forTEMPLATE_DIRS)django::options::libraries(for external tags & filters)django::options::loaders(for dynamically loading libraries)
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
ssi::options::echo_messagessi::options::directoriesssi::options::size_formatssi::options::time_formatssi::options::error_message
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
-
Build:
- Pre-commit script/hook
- Add
-fsanitize=undefinedwhen available (GCC 4.9+ so far) - Visual Studio 2013:
- Add project for command-line tool
- Pass /W4 cleanly
- Pass /Wall cleanly
- Create Visual Studio 2012 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
- [v1] Turn optional arguments to synth.Template into kwargs
- [v1+] Support for Python 3
- Support is_safe, needs_autoescape, and expects_localtime in custom filters
- Support for non-simple custom tags via token and parser objects
- Custom inclusion tags
- Custom assignment tags
- Custom tags with arbitrary blocks
- Other:
- Command-line:
-
Engines:
- Django:
- Implement multi-level inheritance
- Option to pre-load libraries, tags and filters
- Make markers dynamically configurable
- Consider using hex character entities (vs. named) to match Django's engine
- Native
i18nlibrary - Native
l10nlibrary - Native
tzlibrary - Native
staticlibrary
- 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::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:
- Rename value_facade to base_value
- Make documentation comments uniformly
/// - Replace catching
invalid_methodwith dedicated attribute methods - Replace
BOOST_ASSERTwithAJG_SYNTH_ASSERT - Remove as much dead weight from local Boost copy as possible; and/or
- Replace local version of Boost with minimal, shallow submodules once boostorg/boost is ready
- Move
render_tagandbuiltin_tags_tobase_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) - [v2] Create
c++11/c++14branch- Translate macros to variadic templates
- Replace
BOOST_FOREACHwith newforloop - Replace
BOOST_STATIC_CONSTANTwithstatic const - Replace
BOOST_STATIC_ASSERTwithstatic_assert - 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
-
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 http://sourceforge.net/projects/boost/files/boost-binaries/
- Using Homebrew:
- A: Here are some suggestions:
This library is distributed under the Boost LICENSE.
