Giter Site home page Giter Site logo

bustache's Introduction

{{ bustache }}

C++20 implementation of {{ mustache }}, compliant with spec v1.1.3.

Dependencies

  • fmt (or C++20 <format>)

    Note
    This can be controlled by BUSTACHE_USE_FMT

Optional Dependencies

Supported Features

  • Variables
  • Sections
  • Inverted Sections
  • Comments
  • Partials
  • Set Delimiter
  • Lambdas
  • HTML escaping (configurable)
  • Inheritance (extension)
  • Dynamic Names (extension)

Other Features

  • Customizable behavior on unresolved variable
  • Trait-based user-defined model
  • Variable format string, e.g.
    {{var:*^10}}.
  • List expansion section, e.g.
    {{*map}}({{key}} -> {{value}}){{/map}}.
  • Filter section, e.g.
    {{?filter}}...{{/filter}}.

Basics

{{ mustache }} is a template language for text-replacing. When it comes to formatting, there are 2 essential things -- Format and Data. {{ mustache }} also allows an extra lookup-context for Partials. In {{ bustache }}, we represent the Format as a bustache::format object, and Data and Partials can be anything that implements the required traits.

Quick Example

bustache::format format{"{{mustache}} templating"};
std::unordered_map<std::string, std::string> data{{"mustache", "bustache"}};
std::cout << format(data); // should print "bustache templating"

Manual

Data Model

{{ bustache }} doesn't required a fixed set of predefined data types to be used as data model. Instead, any type can be used as data model. Most STL-compatible containers will work out-of-the-box, including the ones that you defined yourself!

Header

#include <bustache/model.hpp>

Adapted Model

Model Traits

To meet the Model concept, you have to implement the traits:

template<>
struct bustache::impl_model<T>
{
    static constexpr model kind;
};

where model can be one of the following:

  • model::atom
  • model::object
  • model::list
// Required by model::atom.
template<>
struct bustache::impl_test<T>
{
    static bool test(T const& self);
};

// Required by model::atom.
template<>
struct bustache::impl_print<T>
{
    static void print(T const& self, output_handler os, char const* fmt);
};

// Required by model::object.
template<>
struct bustache::impl_object<T>
{
    static void get(T const& self, std::string const& key, value_handler visit);
};

// Required by model::list.
template<>
struct bustache::impl_list<T>
{
    static bool empty(T const& self);
    static void iterate(T const& self, value_handler visit);
};

See udt.cpp for more examples.

Compatible Trait

Some types cannot be categorized into a single model (e.g. variant), to make it compatible, you can implement the trait:

template<>
struct bustache::impl_compatible<T>
{
    static value_ptr get_value_ptr(T const& self);
};

See model.hpp for example.

Format Object

bustache::format parses in-memory string into AST.

Header

#include <bustache/format.hpp>

Synopsis

Constructors

explicit format(std::string_view source); // [1]
format(std::string_view source, bool copytext); // [2]
format(ast::document doc, bool copytext); // [3]
  • Version 1 doesn't hold the text, you must ensure the source is valid and not modified during its use.
  • Version 2~3, if copytext == true the text will be copied into the internal buffer.

Manipulator

A manipulator combines the format & data and allows you to specify some options.

template<class T>
manipulator</*unspecified*/> format::operator()(T const& data) const;

// Specify the context for partials.
template<class T>
manipulator</*unspecified*/> manipulator::context(T const&) const noexcept;

// Specify the escape action.
template<class T>
manipulator</*unspecified*/> manipulator::escape(T const&) const noexcept;

Render API

render can be used for customized output.

Header

#include <bustache/render.hpp>

template<class Sink, class Escape = no_escape_t>
void render
(
    Sink const& os, format const& fmt, value_ref data,
    context_handler context = no_context_t{}, Escape escape = {},
    unresolved_handler f = nullptr
);

Context Handler

The context for partials can be any callable that meets the signature:

(std::string const& key) -> format const*;

Unresolved Handler

The unresolved handler can be any callable that meets the signature:

(std::string const& key) -> value_ptr;

Sink (Output Handler)

The sink can be any callable that meets the signature:

(char const* data, std::size_t count) -> void;

Escape Action

The escape action can be any callable that meets the signature:

template<class OldSink>
(OldSink const& sink) -> NewSink;

There're 2 predefined actions: no_escape (default) and escape_html, if no_escape is chosen, there's no difference between {{Tag}} and {{{Tag}}}, the text won't be escaped in both cases.

Stream-based Output

Output directly to the std::basic_ostream.

Header

#include <bustache/render/ostream.hpp>

Synopsis

template<class CharT, class Traits, class Escape = no_escape_t>
void render_ostream
(
    std::basic_ostream<CharT, Traits>& out, format const& fmt,
    value_ref data, context_handler context = no_context_t{},
    Escape escape = {}, unresolved_handler f = nullptr
);

template<class CharT, class Traits, class... Opts>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& out, manipulator<Opts...> const& manip)

Example

// Create format from source.
bustache::format format(...);
// Create the data we want to output.
my_data data{...};
// Create the context for Partials.
my_context context{...};
// Output the result.
std::cout << format(data).context(context).escape(bustache::escape_html);

String Output

Generate a std::string.

Header

#include <bustache/render/string.hpp>

Synopsis

template<class String, class Escape = no_escape_t>
void render_string
(
    String& out, format const& fmt,
    value_ref data, context_handler context = no_context_t{},
    Escape escape = {}, unresolved_handler f = nullptr
);

template<class... Opts>
std::string to_string(manipulator<Opts...> const& manip);

Example

bustache::format format(...);
std::string txt = to_string(format(data).context(context).escape(bustache::escape_html));

Advanced Topics

Lambdas

The lambdas in {{ bustache }} accept signatures below:

  • (ast::view const* view) -> format
  • (ast::view const* view) -> Value

A ast::view is a parsed list of AST nodes, you can make a new ast::view out of the old one and give it to a format. Note that view will be null if the lambda is used as variable.

Error Handling

The constructor of bustache::format may throw bustache::format_error if the parsing fails.

class format_error : public std::runtime_error
{
public:
    explicit format_error(error_type err, std::ptrdiff_t position);

    error_type code() const noexcept;
    std::ptrdiff_t position() const noexcept;
};

error_type has these values:

  • error_set_delim
  • error_baddelim
  • error_delim
  • error_section
  • error_badkey

You can also use what() for a descriptive text.

Performance

Compare with 2 other libs - mstch and Kainjow.Mustache. See benchmark.cpp.

Sample run (VS2019 16.7.6, boost 1.73.0, 64-bit release build):

2020-10-27T16:10:49+08:00
Running F:\code\bus\x64\Release\bus.exe
Run on (8 X 3600 MHz CPU s)
CPU Caches:
  L1 Data 32 KiB (x8)
  L1 Instruction 32 KiB (x8)
  L2 Unified 256 KiB (x8)
  L3 Unified 12288 KiB (x1)
---------------------------------------------------------
Benchmark               Time             CPU   Iterations
---------------------------------------------------------
bustache_usage       4675 ns         4708 ns       149333
mstch_usage         80919 ns        81961 ns         8960
kainjow_usage       23993 ns        24065 ns        29867

Lower is better.

benchmark

License

Copyright (c) 2014-2023 Jamboree

Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

bustache's People

Contributors

jamboree avatar kainjow avatar martinzink avatar mbeutel avatar sjoubert avatar suy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bustache's Issues

Trouble getting Partials (embedded templates) working when using the Nlohmann Json adapter.

I am using Bustache heavily in a multi-threaded application using the adapter for Nlohmann Json found here: [https://github.com/jamboree/bustache/blob/master/example/nlohmann.cpp]

Bustache works perfectly well with this adapter, except that I cannot get partials to work with it.

Here is a minimalist example:

void test() {

	std::unordered_map<std::string, bustache::format> context
	{
		{"EMBEDDED_TEMPLATE", ", goodbye {{goodbyeTag}}"_fmt}
	};

	nlohmann::json data;
	data["helloTag"] = "Bustache";
	data["goodbyeTag"] = "Mustache";

	bustache::format format("Hello {{helloTag}}{{>EMBEDDED_TEMPLATE}} templating system.\n");

	// Prints: Hello Bustache templating system.
	std::cout << format(data) << "\n";

	// Should print: Hello Bustache, goodbye Mustache templating system.
	auto str = format(data).context(context);
	std::cout << str << '\n';	// 'render_ostream': no matching overloaded function
}

Bustache works so nicely with Nlohmann, and my hope is that the Jamboree or some other knowledgeable person can adjust the code for partials for this adapter. Thanks!

Empty string on one case but not a similar other one

I'm still trying to find my way with the library, but when looking at examples of how to use it in the tests, I did some attempts and I got something very strange to me:

    using namespace bustache;
    context cont = {{"include", "{{$person}}customer{{/person}}"_fmt}};
    auto x = "Greetings {{<include}} IGNORED {{$person}}Alice{{/person}}{{/include}}"_fmt(nullptr);
    std::cout << "1 " << x.context(cont) << std::endl;
    auto y = "Greetings {{<include}} IGNORED {{$person}}Alice{{/person}}{{/include}}"_fmt;
    std::cout << "2 " << y(nullptr).context(cont) << std::endl;

I get:

1 
2 Greetings Alice

And I doing something wrong? I don't see why one usage should be different from the other, though I'm not familiar with the types returned in each case.

Thank you.

Will latest versions of Bustache compile on gcc 7.4?

I have no problem building Bustache on GCC 13 or CLANG 17. Previously, I have built and used older versions of Bustache with GCC 7.4.

I do not believe that GCC 7.4 has support for C++20 and that this may be why I can't get it to compile. I did build the FMT library to see if that would help but CMake complained about lack of C++20 support.

I am hoping to get this working on Ubuntu 18.04 but I don't want to waste more time unless it is possible to get the latest version working without C++20 support. Thank you.

Bustache RVO

Is it safe to assume that Bustache objects are returned via RVO just like other STD container are?

bustache::array getData()
{
bustache::array array;
...
return array;
}

Thank you in advance!

Is it possible to execute a function when a specific replacement tag is rendered?

I currently use Bustache the normal way, I first load a json object with data that is mapped to replacement tags.

If no mapping is found, is it possible to execute a function when a certain replacement tag is encountered instead. This function would then provide the data to the replacement tag.

Let me explain why I ask. Assume that you have a replacement tag {{debugInformation}} that is expensive to compute. This tag is only inserted arbitrarily by the user (when the template is developed or modified).

This is not an enhancement request, if this is not possible, I can use arguments configuration options to either compute or not compute this debug information. Thank you.

Compilation error with C++17 variant conflict

The following example fails to compile if C++17 is active and its std::variant is included.
This issue is that the visit call inside bustache are resolved to std::visit instead of bustache::visit.

$ cat main.cpp
#include <bustache/generate/string.hpp>
#include <unordered_map>
#include <variant>

int main()
{
  std::string out;
  bustache::generate_string(out, bustache::format{}, bustache::object{}, std::unordered_map<std::string, bustache::format>{}, bustache::normal);
}

$ clang++ main.cpp -lbustache -std=c++17
In file included from main.cpp:1:
In file included from /usr/include/bustache/generate/string.hpp:12:
In file included from /usr/include/bustache/generate.hpp:10:
In file included from /usr/include/bustache/model.hpp:10:
In file included from /usr/include/bustache/format.hpp:10:
In file included from /usr/include/bustache/ast.hpp:12:
In file included from /usr/include/boost/unordered_map.hpp:17:
In file included from /usr/include/boost/unordered/unordered_map.hpp:18:
In file included from /usr/include/boost/functional/hash.hpp:6:
In file included from /usr/include/boost/container_hash/hash.hpp:114:
/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/variant:1386:23: error: no member named 'valueless_by_exception' in 'bustache::ast::content'
      if ((__variants.valueless_by_exception() || ...))
           ~~~~~~~~~~ ^
/usr/include/bustache/generate.hpp:567:13: note: in instantiation of function template specialization
      'std::visit<bustache::detail::content_visitor<bustache::detail::string_sink<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >,
      std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bustache::format, std::hash<std::__cxx11::string>,
      std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::__cxx11::basic_string<char,
      std::char_traits<char>, std::allocator<char> >, bustache::format> > >, bustache::default_unresolved_handler> &, const bustache::ast::content &>' requested here
            visit(visitor, content);
            ^
/usr/include/bustache/generate/string.hpp:96:9: note: in instantiation of function template specialization 'bustache::generate<bustache::detail::string_sink<std::__cxx11::basic_string<char,
      std::char_traits<char>, std::allocator<char> > >, std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bustache::format,
      std::hash<std::__cxx11::string>, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const
      std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bustache::format> > >, bustache::default_unresolved_handler>' requested here
        generate(sink, fmt, data, context, flag, std::forward<UnresolvedHandler>(f));
        ^
main.cpp:9:13: note: in instantiation of function template specialization 'bustache::generate_string<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >,
      std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bustache::format, std::hash<std::__cxx11::string>,
      std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::__cxx11::basic_string<char,
      std::char_traits<char>, std::allocator<char> >, bustache::format> > >, bustache::default_unresolved_handler>' requested here
  bustache::generate_string(out, bustache::format("{{foo}}"), bustache::object{}, context, bustache::normal);
            ^

thank you and may I have some more, please

First of all thank you for providing a C++ mustache implementation, that is fast, works with any data types and supports template inheritance. I do have 2 questions/wishlist/documentation items. I am going to use handlebars.js as a reference here to help explain nice to haves but am not recommending its exact choice to address the issues.

  1. mustache related: Do you have any way to control the whitespace? This is a deficiency of the mustache specification that many would like fixed in mustache 2 if it ever gets created.
    https://dev.to/cocoroutine/truth-about-template-engines-part-2-nf6
    https://handlebarsjs.com/guide/expressions.html#whitespace-control
    https://dev.to/cocoroutine/truth-about-template-engines-3a7
    The goal is to have templates that look good but also can created results that look good also. This means allowing template writer to manually control indentation to the right and left of per line output and getting rid of empty line.
    This is less important than 2) but is mentioned first due to its brevity.

  2. Everything thing else is "Trait-based user-defined model" related. Your documentation says is for a type that be both a atom, object and list but the actual example presented was for a variant which is more a exclusive or type. Ultimately what is be asked for is for all list and objects to have additional runtime properties without any customization.
    https://handlebarsjs.com/api-reference/data-variables.html#root
    This is purely a data model issue and does not go beyond the current mustache specification.
    handlebars has @root, @FIRST, @Index, @key, @last, this, ., ..
    A) The first 2 that I would be looking for on list is @empty and @notempty
    You have these now in the list model just not exposed to the mustache template via the following
    static bool empty(T const& self);
    use case is as follows
    {{#some_list.@isnotempty}}
    HTML table element start tag
    {{/some_list.@isnotempty}}
    {{#some_list}}
    HTML rows
    {{/some_list}}
    {{#some_list.@isnotempty}}
    HTML table element end tag
    {{/some_list.@isnotempty}}
    B) @root, this, ., ..
    would be good to reference relative data from current or root
    C) @isfirst, @Islast
    is good for in between rows as in separators by remove either first or last since mustache spec does NOT provide separator support
    D) @iseven, @isodd
    is good for alternating styling
    E) @key and other handlebars voodoo is about pivoting data ... a @pivot property for the bustache object model may be better
    so objects can be viewed as an object with members, accessed by name, which is already standard in mustache, which is good with static typing but not dynamic typing because objects can also be viewed as maps, instead of classes, so a list of key value pairs. @pivot would then be a dynamic object property that returns a list view of the map. As a consequence A) through D) would then be available.

While I wouldn't mind doing it myself via your "Trait-based user-defined model" functionality if it could handle it. It would be much better if these data model functionality was provided globally.

How to search Bustache Object for data?

I've been using Bustache templates with great success. Now, I would also like to use Bustache Object as a container for storing and searching for data but I am having problems finding the second value as you can see here:

// find a key in the Bustache container
auto result = bustObj.find("1IX99OAVFXOA");

// display the first value
std::cout << result->first << std::endl; // 1IX99OAVFXOA

// display the second value
//std::cout << result->second << std::endl; // compiler error
bustache::value x = result->second;

I cannot figure out how to obtain the second value, please help me.

Thank you

Cannot compile with clang 14

Hey there,
i stumbled across your lib when i was searching for a performant template engine.
Compiling with GCC12 is just fine. However, it fails with clang 14 on linux.

/home/minski/bustache/src/format.cpp:60: error: call to consteval function 'bustache::parser::(anonymous namespace)::constv<unsigned long>' is not a constant expression
/home/minski/bustache/src/format.cpp:60:45: error: call to consteval function 'bustache::parser::(anonymous namespace)::constv<unsigned long>' is not a constant expression
        const auto match = (... & ((ascii ^ constv(broadcast(c))) + mask)) | word;
                                            ^
/home/minski/bustache/src/format.cpp:60:62: note: subexpression not valid in a constant expression
        const auto match = (... & ((ascii ^ constv(broadcast(c))) + mask)) | word;
                                                             ^

Next thing to tackle is macOS compatiblity. clang14 from macOS does not support std::ranges.
Might there be a workaround for this? In other cases, macOS users would be locked out from your lib.

Thanks and best regards

Add line/column info to `format_error`

The following commit adds line and column info to the format_error class, which makes locating errors in templates much easier.

mbeutel@50d0904

Using the additional info, one can generate a helpful error message from a format_error as follows:

[[noreturn]] static void throwParseError(const std::filesystem::path& file, const bustache::format_error& error)
{
        // we use 1-based indexing here
    auto line = error.line() + 1,
         column = error.column() + 1;

    std::ostringstream sstr;
    sstr << "while parsing file '" << minunicode::path_to_utf8(file) << "':\n";
    auto indent = std::string_view{ "    " };
    auto curIndent = std::string(indent);
    if (line > 0 && column > 0)
    {
        sstr << curIndent;
        if (multiline)
            sstr << "in line " << line << ", column ";
        else
            sstr << "in column ";
        sstr << column << ":\n";
        curIndent += std::string(indent);
    }
    sstr << curIndent << bustache::error_type_to_message(error.code());
    std::throw_with_nested(std::runtime_error(sstr.str()));
}

The implementation approach is probably not ideal; I opted to be minimally invasive because I didn't want to change too many function signatures. Also, the error message returned by format_error::what() currently remains unchanged, and I added a slightly redundant error_type_to_message() function which omits the non-human-readable parts of the error message. A better approach might be to pass through the start of the range to all parsing functions and to generate a more helpful error message at the throw site rather than catching the plain error code and re-throwing another exception.

How would you like this to be implemented in order to be eligible for merging? Do you want format_error::what() to remain unchanged, or would it be acceptable to have the error message include line and column info?

Bustache compiles fine using nlohmann::json but not with nlohmann::ordered_json containers

Not really an issue but I noticed that using nlohmann::ordered_json causes a compiler error (when rendering the template):

error C7608: atomic constraint should be a constant expression

I don't know if there are plans to allow bustache to work with ordered_json or not. Anyway, I looked at a very interesting post regarding ordered vs unordered maps: https://stackoverflow.com/questions/2196995/is-there-any-advantage-of-using-map-over-unordered-map-in-case-of-trivial-keys

Some users posted some test results for the containers. For the purpose of Bustache users, I personally do not see any benefit whatsoever of an ordered vs unordered container.

So the only applicable reason, that I could think of using an UNORDERED map would be if you wanted to save Json to a file while keeping the exact unordered or natural order as it was created. Using an ordered container means that key/value pairs will not be reordered in alphabetical order by 'key'.

Another possible benefit of orderd_json would be that someone may want to pretty print Json in its natural order for debugging purposes.

Compiling Error

Sorry to bother you ,I tried to compile on Msys2(Mingw64) , but failed.
Cmake:

-- Building for: Ninja
-- The CXX compiler identification is GNU 13.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: E:/msys64/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Started CMake for bustache v0.1.0...

-- Configuring done (2.9s)
-- Build files have been written to: E:/msys64/home/test/bustache-master/build

Error:

FAILED: CMakeFiles/bustache.dir/src/render.cpp.obj
E:\msys64\mingw64\bin\c++.exe  -IE:/msys64/home/Qrm/test/bustache-master/include -IE:/msys64/home/Qrm/test/bustache-master/src -std=gnu++20 -MD -MT CMakeFiles/bustache.dir/src/render.cpp.obj -MF CMakeFiles\bustache.dir\src\render.cpp.obj.d -o CMakeFiles/bustache.dir/src/render.cpp.obj -c E:/msys64/home/Qrm/test/bustache-master/src/render.cpp
In file included from E:/msys64/home/Qrm/test/bustache-master/include/bustache/render.hpp:10,
                 from E:/msys64/home/Qrm/test/bustache-master/src/render.cpp:8:
E:/msys64/home/Qrm/test/bustache-master/include/bustache/model.hpp: In instantiation of 'void bustache::detail::print_fmt(const T&, bustache::output_handler, const char*) [with T = std::basic_string_view<char>; bustache::output_handler = bustache::fn_ref<void(const char*, long long unsigned int)>]':
E:/msys64/home/Qrm/test/bustache-master/src/render.cpp:503:30:   required from here
E:/msys64/home/Qrm/test/bustache-master/include/bustache/model.hpp:355:16: error: no matching function for call to 'std::basic_format_context<std::back_insert_iterator<bustache::detail::output_buffer>, char>::basic_format_context(<brace-enclosed initializer list>)'
  355 |         FmtCtx ctx{OutIter(buf), fmt::make_format_args<FmtCtx>()};
      |                ^~~
In file included from E:/msys64/home/Qrm/test/bustache-master/include/bustache/model.hpp:20:
E:/msys64/mingw64/include/c++/13.2.0/format:3373:7: note: candidate: 'std::basic_format_context<_Out, _CharT>::basic_format_context(std::basic_format_args<std::basic_format_context<_Out, _CharT> >, _Out, const std::locale&) [with _Out = std::back_insert_iterator<bustache::detail::output_buffer>; _CharT = char]'
 3373 |       basic_format_context(basic_format_args<basic_format_context> __args,
      |       ^~~~~~~~~~~~~~~~~~~~
E:/msys64/mingw64/include/c++/13.2.0/format:3373:7: note:   candidate expects 3 arguments, 2 provided
E:/msys64/mingw64/include/c++/13.2.0/format:3368:7: note: candidate: 'std::basic_format_context<_Out, _CharT>::basic_format_context(std::basic_format_args<std::basic_format_context<_Out, _CharT> >, _Out) [with _Out = std::back_insert_iterator<bustache::detail::output_buffer>; _CharT = char]'
 3368 |       basic_format_context(basic_format_args<basic_format_context> __args,
      |       ^~~~~~~~~~~~~~~~~~~~
E:/msys64/mingw64/include/c++/13.2.0/format:3368:68: note:   no known conversion for argument 1 from 'OutIter' {aka 'std::back_insert_iterator<bustache::detail::output_buffer>'} to 'std::basic_format_args<std::basic_format_context<std::back_insert_iterator<bustache::detail::output_buffer>, char> >'
 3368 |       basic_format_context(basic_format_args<basic_format_context> __args,
      |                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
E:/msys64/mingw64/include/c++/13.2.0/format:3360:11: note: candidate: 'std::basic_format_context<std::back_insert_iterator<bustache::detail::output_buffer>, char>::basic_format_context(const std::basic_format_context<std::back_insert_iterator<bustache::detail::output_buffer>, char>&)'
 3360 |     class basic_format_context
      |           ^~~~~~~~~~~~~~~~~~~~
E:/msys64/mingw64/include/c++/13.2.0/format:3360:11: note:   candidate expects 1 argument, 2 provided
E:/msys64/home/Qrm/test/bustache-master/include/bustache/model.hpp: In instantiation of 'void bustache::detail::print_fmt(const T&, bustache::output_handler, const char*) [with T = bool; bustache::output_handler = bustache::fn_ref<void(const char*, long long unsigned int)>]':
E:/msys64/home/Qrm/test/bustache-master/src/render.cpp:511:30:   required from here
E:/msys64/home/Qrm/test/bustache-master/include/bustache/model.hpp:355:16: error: no matching function for call to 'std::basic_format_context<std::back_insert_iterator<bustache::detail::output_buffer>, char>::basic_format_context(<brace-enclosed initializer list>)'
  355 |         FmtCtx ctx{OutIter(buf), fmt::make_format_args<FmtCtx>()};
      |                ^~~
E:/msys64/mingw64/include/c++/13.2.0/format:3373:7: note: candidate: 'std::basic_format_context<_Out, _CharT>::basic_format_context(std::basic_format_args<std::basic_format_context<_Out, _CharT> >, _Out, const std::locale&) [with _Out = std::back_insert_iterator<bustache::detail::output_buffer>; _CharT = char]'
 3373 |       basic_format_context(basic_format_args<basic_format_context> __args,
      |       ^~~~~~~~~~~~~~~~~~~~
E:/msys64/mingw64/include/c++/13.2.0/format:3373:7: note:   candidate expects 3 arguments, 2 provided
E:/msys64/mingw64/include/c++/13.2.0/format:3368:7: note: candidate: 'std::basic_format_context<_Out, _CharT>::basic_format_context(std::basic_format_args<std::basic_format_context<_Out, _CharT> >, _Out) [with _Out = std::back_insert_iterator<bustache::detail::output_buffer>; _CharT = char]'
 3368 |       basic_format_context(basic_format_args<basic_format_context> __args,
      |       ^~~~~~~~~~~~~~~~~~~~
E:/msys64/mingw64/include/c++/13.2.0/format:3368:68: note:   no known conversion for argument 1 from 'OutIter' {aka 'std::back_insert_iterator<bustache::detail::output_buffer>'} to 'std::basic_format_args<std::basic_format_context<std::back_insert_iterator<bustache::detail::output_buffer>, char> >'
 3368 |       basic_format_context(basic_format_args<basic_format_context> __args,
      |                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
E:/msys64/mingw64/include/c++/13.2.0/format:3360:11: note: candidate: 'std::basic_format_context<std::back_insert_iterator<bustache::detail::output_buffer>, char>::basic_format_context(const std::basic_format_context<std::back_insert_iterator<bustache::detail::output_buffer>, char>&)'
 3360 |     class basic_format_context
      |           ^~~~~~~~~~~~~~~~~~~~
E:/msys64/mingw64/include/c++/13.2.0/format:3360:11: note:   candidate expects 1 argument, 2 provided
ninja: build stopped: subcommand failed.

Can't compile tests

I just found your benchmark program, which is great! I'd like to improve my implementation's performance and am trying to compile your program. I can compile the library but once I try enabling tests I get some errors.

Here are my steps:

mkdir build && cd build
cmake -DCATCH_INCLUDE_DIR=/path/to/Mustache -DBUSTACHE_ENABLE_TESTING=ON ..
cmake --build .

The first error:

/test/specs.cpp:655:46: error: no matching constructor for initialization of 'object' (aka
'unordered_map<basic_string<char, char_traits, allocator >, bustache::value>')
CHECK(to_string("Hello, {{lambda}}!"_fmt(object{{"lambda", [] { return "world"; }}})) == "Hello, world!");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Note that I installed Boost via Homebrew, so it's using version 1.63.0. Also since I'm using my project's include directory which includes the latest catch.hpp maybe that's an issue as well.

Also, maybe the benchmark program should verify the rendered value matches an expected value, otherwise it's not clear whether an implementation is even functioning properly.

Make context misses trigger an error

As per mustache(5), it is suggested that variable misses default to empty value, but also that this behavior be configurable in the library. This is not the case for bustache.

I'm willing to contribute such an improvement if you consider it acceptable. However, I have a few questions:

  1. The spec does not suggest a similar behavior for section misses. However, I'd really like to trigger an error too. My use case for misses triggering errors is that I don't want my template file to contain unused elements and that any typo between the format and the context be spotted as early as possible. Do we handle variable and section misses the same or do we use two different configuration switches?
  2. I think the best way to configure the behavior is to pass an option at generate time, in the same way the html_escape behavior is provided. I think the best way would be to convert option_type to a proper option struct with multiple configuration switches. Wha't your policy regarding breaking changes? Is there another way to do that?

Thanks.

Bustache data adapter for Nlohmann Json?

I downloaded Jamboree Bustache and I am getting compiler errors when trying to use Nlohmann Json with it.

The documentation states: "Most STL-compatible containers will work out-of-the-box" but I guess that Nlohmann Json does need an adapter to work with Bustache.

I see that Boost Json does have an adapter for it.

I looked at adapted/boost_json.hpp to see if I could do something for Nlohmann but this is way beyond my ability. Would someone who understands this kind of complexity please create one for Nlohmann since it is such a popular and easy to use Json library?

New kid on the block

Hey there,
i just wanted to let you know, that there is a new kid on the block.
Since i had the need for a small, fast and memory efficient template engine for a micro controller (e.g. ESP8266), i wrote one:
https://github.com/mincequi/tinja

Also did some performance checks with bustache. But please, check yourself.

Hopefully, you find this interesting.

Improve CMake package integration

In my fork I made some changes to improve the CMake package integration:

master...mbeutel:b917d54

  • The package now has an option EXPORT_BUILD_DIR which exports the build directory to the CMake package registry. This means that find_package() calls in consuming projects can find the package in its build directory. It is useful when working on both the library and consuming projects at the same time as it obviates the need to run make install after every change to the library.

  • bustache depends on Boost in public header files. CMake target dependencies are transitive, so a target linking to "bustache" inherits the Boost dependency; however, CMake package dependencies are not automatically transitive. The "bustache-config.cmake" previously generated by install() defines an imported target "bustache" and links it to the target "Boost", but it does not call find_package(Boost REQUIRED). This forces consumers of bustache to put this find_package(Boost REQUIRED) call in their own projects before finding bustache, which is obviously undesirable. To avoid this, the package now installs the export (= set of targets) to "bustache-targets.cmake" and generates a separate "bustache-config.cmake" file from the template "bustache-config.cmake.in" which first calls find_dependency(Boost REQUIRED) and then includes "bustache-targets.cmake". This is the recommended approach for handling transitive package dependencies.

  • The package no also generates and installs a "bustache-config-version.cmake" file which enforces semantic versioning (SameMajorVersion) during package lookup. This has the advantage that it allows installed packages and exported build directories to coexist. Example:

    1. The user installs bustache (current version 4.2.0) with the package manager of her choice (e.g. vcpkg).
    2. The user needs to add a feature to bustache. To do this, she forks and clones the repository and builds it with EXPORT_BUILD_DIR set to ON. However, because find_package() looks for installed packages first, the exported build directory is not found by consuming projects.
    3. After adding the feature, the user increments the minor version number in the local bustache clone (which is now 4.3.0).
    4. In the consuming project, the user refers to the incremented minor version number with find_package(bustache 4.3 REQUIRED). Because of the versioning logic in "bustache-config-version.cmake", CMake will skip the installed version of bustache and find the exported build directory.

Would you be interested in merging these changes into your repository?

Can't compile with gcc on Linux

In file included from .../bustache/include/bustache/generate.hpp:10:0,
                 from .../bustache/src/generate.cpp:8:
.../bustache/include/bustache/model.hpp:165:23: error: member ‘_vptr’ conflicts with virtual function table field name
         vtable const* _vptr;
                       ^~~~~

Compiled fine fine when renamed the _vptr. However, clang finished well without renaming.

example of compiling the exmple

Hi, would be great if you could give some instructions on compiling the example.

I've tried following the example at https://wandbox.org/permlink/Vxmrb2GgcLKicC7N and I'm running

g++ example/main.cpp -Wall -Wextra  -std=c++11 "-I/usr/include" "-I./include"

but I'm getting masses of undefined reference to bustache::* and undefined reference to boost::*

cmake .; make; sudo make install all work fine. I'm on ubuntu 17.04.

Is interface for Bustache C++20 incomplete?

I am a Mustache for Javascript user and downloaded Bustache (C++20). I wanted to use the variant data type for Json values and had trouble.

After looking at issues, I noticed messages for Bustache (C++11) so I downloaded and easily used variant for Json values.

Ok, interface for C++11's bustache::object and bustache::array seem much easier to use than the example given for C++20.

My next step was to give Bustache C++20 another shot and included "../test/model.hpp", replaced namespace to test::object and test::array. I was so happy to see everything compile but then when I went to build, I got a cryptic "error LNK2005" about something already being defined.

My guess is that the test/model.hpp has not been finalized and that struct value; is currently a global causing the link error.

I don't have C++ knowledge to work around this linker error, does anyone have a revision available to test/model.hpp so that I can use variant value type?

Search Bustache Object for data - enhansement

Jamboree, I thought that the following modification would not be difficult but I cannot figure out how to make the Printer object return values, for example:

std::cout << bustache::visit(Printer{}, result->second) << '\n';

I tried to modify the operator so that a value is returned.

#include <iostream>
#include <string>
#include <bustache/model.hpp>

struct Printer
{
    int operator()(int val) { return val; }
    double operator()(double val) { return val; }
    std::string operator()(std::string const& val) { return val; }

    template<class T>
    void operator()(T const&) { std::cout << "(something else)" << '\n'; }
};

int main()
{
    bustache::object bustObj
    {
        {"1IX99OAVFXOA", 123}
    };

    // find a key in the Bustache container
    auto result = bustObj.find("1IX99OAVFXOA");

    if (result != bustObj.end())
    {
        // display the first value
        std::cout << result->first << '\n'; // 1IX99OAVFXOA

        // display the second value
        // bustache::visit(Printer{}, result->second);
        std::cout << bustache::visit(Printer{}, result->second) << '\n';
    }
}

Of course, I was unable to figure out how to get values returned.

Cannot compile with Apple LLVM version 9.1.0 (clang-902.0.39.1)

I failed to compile the code under MacOS with the following configuration:

cmake version: 3.11.0,

clang --version:
Apple LLVM version 9.1.0 (clang-902.0.39.1)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

boost version: 1.66

Building Output:

`[ 66%] Building CXX object CMakeFiles/bustache.dir/src/format.cpp.o
[ 66%] Building CXX object CMakeFiles/bustache.dir/src/generate.cpp.o
In file included from /Users/build/bustache-master/src/generate.cpp:8:
In file included from /Users/build/bustache-master/include/bustache/generate.hpp:10:
In file included from /Users/build/bustache-master/include/bustache/model.hpp:10:
In file included from /Users/build/bustache-master/include/bustache/format.hpp:10:
In file included from /Users/build/bustache-master/include/bustache/ast.hpp:10:
In file included from /Users/build/bustache-master/include/bustache/detail/variant.hpp:12:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/utility:198:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/__tuple:16:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:4323:23: error: calling 'operator()' with incomplete return type 'bustache::value'
_LIBCPP_INVOKE_RETURN(_VSTD::forward<_Fp>(__f)(_VSTD::forward<_Args>(__args)...))

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/__config:441:15: note: expanded from macro '_VSTD'
#define _VSTD std::_LIBCPP_NAMESPACE
              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:4220:23: note: expanded from macro '_LIBCPP_INVOKE_RETURN'
    noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) \
                      ^~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:4340:9: note: in instantiation of exception specification for '__invoke<std::__1::function<bustache::value ()> &>' requested here
        _VSTD::__invoke(_VSTD::declval<_Fp>(), _VSTD::declval<_Args>()...));
        ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/__config:441:15: note: expanded from macro '_VSTD'
#define _VSTD std::_LIBCPP_NAMESPACE
              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1601:33: note: in instantiation of template class 'std::__1::__invokable_r<void, std::__1::function<bustache::value ()> &>' requested here
                                __invokable<_Fp&, _ArgTypes...>::value>
                                ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1626:9: note: in instantiation of default argument for '__callable<std::__1::function<bustache::value ()> >' required here
        __callable<_Fp>::value && !is_same<_Fp, function>::value
        ^~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1628:5: note: in instantiation of default argument for 'function<std::__1::function<bustache::value ()> >' required here
    function(_Fp);
    ^~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1588:28: note: while substituting deduced template arguments into function template 'function' [with _Fp = std::__1::function<bustache::value ()>, $1 = (no value)]
class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
                           ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1680:9: note: 'operator()' declared here
    _Rp operator()(_ArgTypes...) const;
        ^
/Users/build/bustache-master/include/bustache/model.hpp:56:11: note: definition of 'bustache::value' is not complete until the closing '}'
    class value : public variant_base<value>
          ^
In file included from /Users/build/bustache-master/src/generate.cpp:8:
In file included from /Users/build/bustache-master/include/bustache/generate.hpp:10:
In file included from /Users/build/bustache-master/include/bustache/model.hpp:10:
In file included from /Users/build/bustache-master/include/bustache/format.hpp:10:
In file included from /Users/build/bustache-master/include/bustache/ast.hpp:10:
In file included from /Users/build/bustache-master/include/bustache/detail/variant.hpp:12:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/utility:198:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/__tuple:16:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:4323:23: error: calling 'operator()' with incomplete return type 'bustache::value'
_LIBCPP_INVOKE_RETURN(_VSTD::forward<_Fp>(__f)(_VSTD::forward<_Args>(__args)...))
~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/__config:441:15: note: expanded from macro '_VSTD'
#define _VSTD std::_LIBCPP_NAMESPACE
              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:4220:23: note: expanded from macro '_LIBCPP_INVOKE_RETURN'
    noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) \
                      ^~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:4340:9: note: in instantiation of exception specification for '__invoke<std::__1::function<bustache::value (const std::__1::vector<bustache::ast::content, std::__1::allocator<bustache::ast::content> > &)> &, const std::__1::vector<bustache::ast::content, std::__1::allocator<bustache::ast::content> > &>' requested here
        _VSTD::__invoke(_VSTD::declval<_Fp>(), _VSTD::declval<_Args>()...));
        ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/__config:441:15: note: expanded from macro '_VSTD'
#define _VSTD std::_LIBCPP_NAMESPACE
              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1601:33: note: in instantiation of template class 'std::__1::__invokable_r<void, std::__1::function<bustache::value (const std::__1::vector<bustache::ast::content, std::__1::allocator<bustache::ast::content> > &)> &, const std::__1::vector<bustache::ast::content, std::__1::allocator<bustache::ast::content> > &>' requested here
                                __invokable<_Fp&, _ArgTypes...>::value>
                                ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1626:9: note: in instantiation of default argument for '__callable<std::__1::function<bustache::value (const std::__1::vector<bustache::ast::content, std::__1::allocator<bustache::ast::content> > &)> >' required here
        __callable<_Fp>::value && !is_same<_Fp, function>::value
        ^~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1628:5: note: in instantiation of default argument for 'function<std::__1::function<bustache::value (const std::__1::vector<bustache::ast::content, std::__1::allocator<bustache::ast::content> > &)> >' required here
    function(_Fp);
    ^~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1588:28: note: while substituting deduced template arguments into function template 'function' [with _Fp = std::__1::function<bustache::value (const std::__1::vector<bustache::ast::content, std::__1::allocator<bustache::ast::content> > &)>, $1 = (no value)]
class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
                           ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1680:9: note: 'operator()' declared here
    _Rp operator()(_ArgTypes...) const;
        ^
/Users/build/bustache-master/include/bustache/model.hpp:56:11: note: definition of 'bustache::value' is not complete until the closing '}'
    class value : public variant_base<value>
          ^
2 errors generated.
make[3]: *** [CMakeFiles/bustache.dir/src/generate.cpp.o] Error 1
make[3]: *** Waiting for unfinished jobs....
make[2]: *** [CMakeFiles/bustache.dir/all] Error 2
make[1]: *** [CMakeFiles/bustache.dir/rule] Error 2
make: *** [bustache] Error 2`

Please support template inheritance

I understand you are targetting v1.1.3 of the mustache spec. However, template inheritance is turning into a defacto standard. It is supported by numerous mustache implementations, javascript, Java and others. For instance, it is supported by 3 of the 4 most popular Java implementations. Its support in javascript implementations mean that it can be used client and server side.

Consider Trimou as a documentation example
http://trimou.org/

^^^^^^^^^^^^

Template inheritance
This useful feature is not supported in the spec. Trimou basically follows the way mustache.java implements the template inheritance.

In the extended template (parent), the sections to extend are defined - use $ to identify such sections.
A section identified with < includes the extended template
In the extending template (child), the extending sections are defined - again, use $ to identify such sections. Sections to extend may define the default content.
The following template with name "super":

{{$header}}
The default header
{{/header}}
In between...
{{$content}}
The default content
{{/content}}
Can be extended in this way:

Hello world!
{{<super}}
{{$header}}
My own header
{{/header}}
Only extending sections are considered...
{{/super}}
Lalala...
And the result is:

Hello world!
My own header
In between...
The default content
Lalala...

Insert a template into another template?

Hello and thank you for such a nice library. Is there some way to include (or embed) one template into another template? For example:

{{header}}
{{EMBEDDED_TEMPLATE}}
{{footer}}

data.emplace("header", "Hello World!");
data.emplace("EMBEDDED_TEMPLATE", "{{header}} - {{footer}}");
data.emplace("footer", "Goodbye World!");

Actual output:

Hello World!
{{EMBEDDED_TEMPLATE}}
Goodbye World!

Desired output:

Hello World!
Hello World! - Goodbye World!
Goodbye World!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.