Awesome
A Powerful Templating Framework in C++, with a command-line tool and Python module, written by Alvaro J. Genial.
Synopsis
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.
Status
Version 1 is released and stable.
Motivation
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.)
Examples
Command-line
echo '{"user": "Dolph Lundgren"}' > context.json
echo 'Howdy, {{ user }}!' > template.txt
cat template.txt | synth -e django -c context.json
Or on Windows:
echo {"user":"Dolph Lundgren"} > context.json
echo Howdy, {{ user }}! > template.txt
type template.txt | synth -e django -c context.json
Python
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))
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;
}
Reference
Command-line
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: '.')
Installation
Via System Package Manager
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.
Via Python Package Manager
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.
From Source:
- 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:gcc
versions4.2.1
,4.6.3
and4.7.2
clang
versions3.0
and3.3
msvc
version12.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.
Components
Engines
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.)
Bindings
command_line
python
Templates
buffer_template
path_template
stream_template
string_template
Adapters
array
T[N]
T[]
boost::array
bool
bool
complex
std::complex
container
deque
std::deque
list
std::list
map
std::map
std::multimap
set
std::set
std::multiset
stack
std::stack
vector
std::vector
memory
std::auto_ptr
none
boost::none_t
numeric
char
char signed
char unsigned
short
short unsigned
int
int unsigned
long
long unsigned
wchar_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.)float
double
long double
optional
boost::optional
ptime
boost::posix_time::ptime
ptree
boost::property_tree::ptree
pointer
void*
T*
ref
boost::reference_wrapper
smart_ptr
boost::scoped_array
boost::scoped_ptr
boost::shared_array
boost::shared_ptr
string
std::basic_string
char*
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
)
utility
std::pair
variant
boost::variant
Base Components
adapters::base_adapter
bindings::base_binding
engines::base_engine
templates::base_template
Other Components
engines::context
engines::options
engines::state
engines::value
templates::cache
Version
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"
)
Configuration
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
)
Context Data Input Formats
ini
json
xml
Django Engine
Tags
django::builtin_tags::autoescape_tag
django::builtin_tags::block_tag
django::builtin_tags::comment_tag
django::builtin_tags::csrf_token_tag
django::builtin_tags::cycle_tag
django::builtin_tags::cycle_as_tag
django::builtin_tags::cycle_as_silent_tag
django::builtin_tags::debug_tag
django::builtin_tags::extends_tag
django::builtin_tags::filter_tag
django::builtin_tags::firstof_tag
django::builtin_tags::for_tag
django::builtin_tags::for_empty_tag
django::builtin_tags::if_tag
django::builtin_tags::ifchanged_tag
django::builtin_tags::ifequal_tag
django::builtin_tags::ifnotequal_tag
django::builtin_tags::include_tag
django::builtin_tags::include_with_tag
django::builtin_tags::include_with_only_tag
django::builtin_tags::library_tag
django::builtin_tags::load_tag
django::builtin_tags::load_from_tag
django::builtin_tags::now_tag
django::builtin_tags::regroup_tag
django::builtin_tags::spaceless_tag
django::builtin_tags::ssi_tag
django::builtin_tags::templatetag_tag
django::builtin_tags::url_tag
django::builtin_tags::url_as_tag
django::builtin_tags::variable_tag
django::builtin_tags::verbatim_tag
django::builtin_tags::widthratio_tag
django::builtin_tags::with_tag
Filters
django::builtin_filters::add_filter
django::builtin_filters::addslashes_filter
django::builtin_filters::capfirst_filter
django::builtin_filters::center_filter
django::builtin_filters::cut_filter
django::builtin_filters::date_filter
django::builtin_filters::default_filter
django::builtin_filters::default_if_none_filter
django::builtin_filters::dictsort_filter
django::builtin_filters::dictsortreversed_filter
django::builtin_filters::divisibleby_filter
django::builtin_filters::escape_filter
django::builtin_filters::escapejs_filter
django::builtin_filters::filesizeformat_filter
django::builtin_filters::first_filter
django::builtin_filters::fix_ampersands_filter
django::builtin_filters::floatformat_filter
django::builtin_filters::force_escape_filter
django::builtin_filters::get_digit_filter
django::builtin_filters::iriencode_filter
django::builtin_filters::join_filter
django::builtin_filters::last_filter
django::builtin_filters::length_filter
django::builtin_filters::length_is_filter
django::builtin_filters::linebreaks_filter
django::builtin_filters::linebreaksbr_filter
django::builtin_filters::linenumbers_filter
django::builtin_filters::ljust_filter
django::builtin_filters::lower_filter
django::builtin_filters::make_list_filter
django::builtin_filters::phone2numeric_filter
django::builtin_filters::pluralize_filter
django::builtin_filters::pprint_filter
django::builtin_filters::random_filter
django::builtin_filters::removetags_filter
django::builtin_filters::rjust_filter
django::builtin_filters::safe_filter
django::builtin_filters::safeseq_filter
django::builtin_filters::slice_filter
django::builtin_filters::slugify_filter
django::builtin_filters::stringformat_filter
django::builtin_filters::striptags_filter
django::builtin_filters::time_filter
django::builtin_filters::timesince_filter
django::builtin_filters::timeuntil_filter
django::builtin_filters::title_filter
django::builtin_filters::truncatechars_filter
django::builtin_filters::truncatechars_html_filter
django::builtin_filters::truncatewords_filter
django::builtin_filters::truncatewords_html_filter
django::builtin_filters::unordered_list_filter
django::builtin_filters::upper_filter
django::builtin_filters::urlencode_filter
django::builtin_filters::urlize_filter
django::builtin_filters::urlizetrunc_filter
django::builtin_filters::wordcount_filter
django::builtin_filters::wordwrap_filter
django::builtin_filters::yesno_filter
Formats
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 Engine
Tags
ssi::builtin_tags::config_tag
ssi::builtin_tags::echo_tag
ssi::builtin_tags::exec_tag
ssi::builtin_tags::fsize_tag
ssi::builtin_tags::flastmod_tag
ssi::builtin_tags::if_tag
ssi::builtin_tags::include_tag
ssi::builtin_tags::printenv_tag
ssi::builtin_tags::set_tag
Formats
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 Engine
Tags
tmpl::builtin_tags::comment_tag
(Technically, part ofctpp
)tmpl::builtin_tags::if_tag
tmpl::builtin_tags::include_tag
tmpl::builtin_tags::loop_tag
tmpl::builtin_tags::unless_tag
tmpl::builtin_tags::variable_tag
Options
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_none
caching_all
caching_paths
caching_buffers
caching_strings
caching_per_thread
caching_per_process
Future Work
-
Build:
- Pre-commit script/hook
- GCC:
- [4.9+] Add
-fsanitize=undefined
when 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
/.out
files - 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
,-O3
and-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:
lorem
tag- Make markers dynamically configurable
- Native
i18n
library - Native
l10n
library - Native
tz
library - Native
static
library - Native
staticfiles
library - Native
humanize
library - Native
webdesign
library
- SSI:
- Implement additional tags from Jigsaw
- TMPL:
- Consider renaming
html
- Consider renaming
- Other:
- Create
ctpp
engine - [v1+] Create
jinja2
engine - [v1+] Create
cheetah
engine - [v1+] Create
xslt
engine
- Create
- Django:
-
Adapters:
- Adapter for
boost::tuple
- Adapter for
boost::any
- Adapter for
boost::function
- Adapter for
boost::tribool
- Adapters for
boost::fusion
sequences - Adapters for
boost::local_time
types - Adapter for
boost::intrusive_ptr
- Support for
boost::enable_shared_from_this
- [v2] Adapters for
c++11
types - [v2] Adapters for
c++14
types
- Adapter for
-
Templates:
- Create new
file_template
(usingFILE* file
)- Add
render_to_file
to base_template - Add
render_file
to engines
- Add
- Create
descriptor_template
(usingint descriptor
)- Add
render_to_descriptor
to base_template - Add
render_descriptor
to 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_tag
andbuiltin_tags_
tobase_engine::kernel
- Move
render_block
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
) - Modernize codebase to with C++11 support:
- Translate macros to variadic templates
- Replace
boost::assign
use with aggregate initializers - Remove complex redundant
typedef
s 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_PRAGMA
macro that invokes_Pragma
or__pragma
(MSVC) as needed- Add
AJG_SYNTH_PRAGMA(once)
to all header files and see if it speeds up compilations
- Add
- [v1+] Sort
#include
s alphabetically - [v1+] Run entire C++ codebase through clang-format
- Change
class
in 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
Infrequently Asked Questions
-
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=system
toscons
. - 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-python
for 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 boost
in the Package Manager Console
- Using Homebrew:
- A: Here are some suggestions:
License
This library is distributed under the Boost LICENSE.