Merge branch 'master' of https://github.com/pantor/inja
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6d9f87b..fed4ad6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,11 +16,11 @@
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake)
 
 if(CMAKE_COMPILER_IS_GNUCC)
-  set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -Wall")
+  add_compile_options(-Wall)
 endif()
 
 if(MSVC)
-  set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} /W4")
+  add_compile_options(/W4 /permissive- /utf-8 /Zc:__cplusplus)
 endif()
 
 
diff --git a/appveyor.yml b/appveyor.yml
index b13849c..33ace31 100755
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,14 +8,14 @@
   - x64
 
 configuration:
-#  - Debug
+  - Debug
   - Release
 
 environment:
   matrix:
     # Visual Studio 2015
-#    - TOOLCHAIN: v140
-#      STD: 14
+    - TOOLCHAIN: v140
+      STD: 14
     # Visual Studio 2017
     - TOOLCHAIN: v141
       STD: 14
@@ -34,7 +34,7 @@
 before_build:
   - mkdir -p build
   - cd build
-  - cmake .. -A %PLATFORM% -T %TOOLCHAIN% -DCMAKE_CXX_STANDARD=%STD% -DCMAKE_CXX_FLAGS="/permissive- /utf-8 /Zc:__cplusplus"
+  - cmake .. -A %PLATFORM% -T %TOOLCHAIN% -DCMAKE_CXX_STANDARD=%STD%
 
 build_script:
   - cmake --build . --config %CONFIGURATION% -- -verbosity:n
diff --git a/include/inja/environment.hpp b/include/inja/environment.hpp
index 05bb5e8..a8dd22d 100644
--- a/include/inja/environment.hpp
+++ b/include/inja/environment.hpp
@@ -28,84 +28,64 @@
  * \brief Class for changing the configuration.
  */
 class Environment {
-  class Impl {
-   public:
-    std::string input_path;
-    std::string output_path;
-
-    LexerConfig lexer_config;
-    ParserConfig parser_config;
-
-    FunctionStorage callbacks;
-    TemplateStorage included_templates;
-  };
-
-  std::unique_ptr<Impl> m_impl;
-
  public:
   Environment(): Environment("") { }
 
-  explicit Environment(const std::string& global_path): m_impl(stdinja::make_unique<Impl>()) {
-    m_impl->input_path = global_path;
-    m_impl->output_path = global_path;
-  }
+  explicit Environment(const std::string& global_path): m_input_path(global_path), m_output_path(global_path) {}
 
-  explicit Environment(const std::string& input_path, const std::string& output_path): m_impl(stdinja::make_unique<Impl>()) {
-    m_impl->input_path = input_path;
-    m_impl->output_path = output_path;
-  }
+  Environment(const std::string& input_path, const std::string& output_path): m_input_path(input_path), m_output_path(output_path) {}
 
   /// Sets the opener and closer for template statements
   void set_statement(const std::string& open, const std::string& close) {
-    m_impl->lexer_config.statement_open = open;
-    m_impl->lexer_config.statement_close = close;
-    m_impl->lexer_config.update_open_chars();
+    m_lexer_config.statement_open = open;
+    m_lexer_config.statement_close = close;
+    m_lexer_config.update_open_chars();
   }
 
   /// Sets the opener for template line statements
   void set_line_statement(const std::string& open) {
-    m_impl->lexer_config.line_statement = open;
-    m_impl->lexer_config.update_open_chars();
+    m_lexer_config.line_statement = open;
+    m_lexer_config.update_open_chars();
   }
 
   /// Sets the opener and closer for template expressions
   void set_expression(const std::string& open, const std::string& close) {
-    m_impl->lexer_config.expression_open = open;
-    m_impl->lexer_config.expression_close = close;
-    m_impl->lexer_config.update_open_chars();
+    m_lexer_config.expression_open = open;
+    m_lexer_config.expression_close = close;
+    m_lexer_config.update_open_chars();
   }
 
   /// Sets the opener and closer for template comments
   void set_comment(const std::string& open, const std::string& close) {
-    m_impl->lexer_config.comment_open = open;
-    m_impl->lexer_config.comment_close = close;
-    m_impl->lexer_config.update_open_chars();
+    m_lexer_config.comment_open = open;
+    m_lexer_config.comment_close = close;
+    m_lexer_config.update_open_chars();
   }
 
   /// Sets whether to remove the first newline after a block
   void set_trim_blocks(bool trim_blocks) {
-    m_impl->lexer_config.trim_blocks = trim_blocks;
+    m_lexer_config.trim_blocks = trim_blocks;
   }
 
   /// Sets whether to strip the spaces and tabs from the start of a line to a block
   void set_lstrip_blocks(bool lstrip_blocks) {
-    m_impl->lexer_config.lstrip_blocks = lstrip_blocks;
+    m_lexer_config.lstrip_blocks = lstrip_blocks;
   }
 
   /// Sets the element notation syntax
   void set_element_notation(ElementNotation notation) {
-    m_impl->parser_config.notation = notation;
+    m_parser_config.notation = notation;
   }
 
 
   Template parse(nonstd::string_view input) {
-    Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
+    Parser parser(m_parser_config, m_lexer_config, m_included_templates);
     return parser.parse(input);
   }
 
   Template parse_template(const std::string& filename) {
-    Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
-    return parser.parse_template(m_impl->input_path + static_cast<std::string>(filename));
+    Parser parser(m_parser_config, m_lexer_config, m_included_templates);
+    return parser.parse_template(m_input_path + static_cast<std::string>(filename));
   }
 
   std::string render(nonstd::string_view input, const json& data) {
@@ -128,13 +108,13 @@
   }
 
   void write(const std::string& filename, const json& data, const std::string& filename_out) {
-    std::ofstream file(m_impl->output_path + filename_out);
+    std::ofstream file(m_output_path + filename_out);
     file << render_file(filename, data);
     file.close();
   }
 
   void write(const Template& temp, const json& data, const std::string& filename_out) {
-    std::ofstream file(m_impl->output_path + filename_out);
+    std::ofstream file(m_output_path + filename_out);
     file << render(temp, data);
     file.close();
   }
@@ -150,24 +130,24 @@
   }
 
   std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
-    Renderer(m_impl->included_templates, m_impl->callbacks).render_to(os, tmpl, data);
+    Renderer(m_included_templates, m_callbacks).render_to(os, tmpl, data);
     return os;
   }
 
   std::string load_file(const std::string& filename) {
-    Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
-    return parser.load_file(m_impl->input_path + filename);
+    Parser parser(m_parser_config, m_lexer_config, m_included_templates);
+    return parser.load_file(m_input_path + filename);
   }
 
   json load_json(const std::string& filename) {
-    std::ifstream file = open_file_or_throw(m_impl->input_path + filename);
+    std::ifstream file = open_file_or_throw(m_input_path + filename);
     json j;
     file >> j;
     return j;
   }
 
   void add_callback(const std::string& name, unsigned int numArgs, const CallbackFunction& callback) {
-    m_impl->callbacks.add_callback(name, numArgs, callback);
+    m_callbacks.add_callback(name, numArgs, callback);
   }
 
   /** Includes a template with a given name into the environment.
@@ -175,8 +155,18 @@
    * include "<name>" syntax.
    */
   void include_template(const std::string& name, const Template& tmpl) {
-    m_impl->included_templates[name] = tmpl;
+    m_included_templates[name] = tmpl;
   }
+
+ private:
+  std::string m_input_path;
+  std::string m_output_path;
+
+  LexerConfig m_lexer_config;
+  ParserConfig m_parser_config;
+
+  FunctionStorage m_callbacks;
+  TemplateStorage m_included_templates;
 };
 
 /*!
diff --git a/include/inja/string_view.hpp b/include/inja/string_view.hpp
index 9d52d61..42cd4f0 100644
--- a/include/inja/string_view.hpp
+++ b/include/inja/string_view.hpp
@@ -12,7 +12,7 @@
 #define NONSTD_SV_LITE_H_INCLUDED
 
 #define string_view_lite_MAJOR  1
-#define string_view_lite_MINOR  1
+#define string_view_lite_MINOR  4
 #define string_view_lite_PATCH  0
 
 #define string_view_lite_VERSION  nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
@@ -218,7 +218,7 @@
 # define nssv_COMPILER_MSVC_VERSION  0
 #endif
 
-#define nssv_COMPILER_VERSION( major, minor, patch )  (10 * ( 10 * major + minor) + patch)
+#define nssv_COMPILER_VERSION( major, minor, patch )  ( 10 * ( 10 * (major) + (minor) ) + (patch) )
 
 #if defined(__clang__)
 # define nssv_COMPILER_CLANG_VERSION  nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
@@ -273,8 +273,10 @@
 #define nssv_HAVE_WCHAR16_T             nssv_CPP11_100
 #define nssv_HAVE_WCHAR32_T             nssv_CPP11_100
 
-#if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
+#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
 # define nssv_HAVE_STD_DEFINED_LITERALS  nssv_CPP11_140
+#else
+# define nssv_HAVE_STD_DEFINED_LITERALS  0
 #endif
 
 // Presence of C++14 language features:
@@ -402,6 +404,22 @@
 
 namespace nonstd { namespace sv_lite {
 
+#if nssv_CPP11_OR_GREATER
+
+namespace detail {
+
+// Expect tail call optimization to make length() non-recursive:
+
+template< typename CharT >
+inline constexpr std::size_t length( CharT * s, std::size_t result = 0 )
+{
+    return *s == '\0' ? result : length( s + 1, result + 1 );
+}
+
+} // namespace detail
+
+#endif // nssv_CPP11_OR_GREATER
+
 template
 <
     class CharT,
@@ -455,14 +473,20 @@
     {}
 #endif
 
-    nssv_constexpr basic_string_view( CharT const * s, size_type count )
+    nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept
         : data_( s )
         , size_( count )
     {}
 
-    nssv_constexpr basic_string_view( CharT const * s)
+    nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept
         : data_( s )
+#if nssv_CPP17_OR_GREATER
         , size_( Traits::length(s) )
+#elif nssv_CPP11_OR_GREATER
+        , size_( detail::length(s) )
+#else
+        , size_( Traits::length(s) )
+#endif
     {}
 
     // Assignment:
@@ -518,7 +542,7 @@
 #else
         if ( pos >= size() )
         {
-            throw std::out_of_range("nonst::string_view::at()");
+            throw std::out_of_range("nonstd::string_view::at()");
         }
 #endif
         return data_at( pos );
@@ -560,7 +584,7 @@
 #else
         if ( pos > size() )
         {
-            throw std::out_of_range("nonst::string_view::copy()");
+            throw std::out_of_range("nonstd::string_view::copy()");
         }
 #endif
         const size_type rlen = (std::min)( n, size() - pos );
@@ -577,7 +601,7 @@
 #else
         if ( pos > size() )
         {
-            throw std::out_of_range("nonst::string_view::substr()");
+            throw std::out_of_range("nonstd::string_view::substr()");
         }
 #endif
         return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
@@ -588,7 +612,9 @@
     nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1)
     {
         if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
+        {
             return result;
+        }
 
         return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
     }
@@ -684,10 +710,14 @@
     nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
     {
         if ( size() < v.size() )
+        {
             return npos;
+        }
 
         if ( v.empty() )
+        {
             return (std::min)( size(), pos );
+        }
 
         const_iterator last   = cbegin() + (std::min)( size() - v.size(), pos ) + v.size();
         const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq );
@@ -825,7 +855,7 @@
     {
         const basic_string_view v;
 
-        nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {}
+        nssv_constexpr explicit not_in_view( basic_string_view v ) : v( v ) {}
 
         nssv_constexpr bool operator()( CharT c ) const
         {
@@ -952,7 +982,167 @@
 // constexpr and noexcept so that an object t with an implicit conversion
 // to S can be compared according to Table 67.
 
-#if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
+#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
+
+// accomodate for older compilers:
+
+// ==
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+// !=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) != 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) != 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() != rhs.size() && lhs.compare( rhs ) != 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return lhs.size() != rhs.size() || rhs.compare( lhs ) != 0; }
+
+// <
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) > 0; }
+
+// <=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) >= 0; }
+
+// >
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) < 0; }
+
+// >=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) <= 0; }
+
+#else // newer compilers:
 
 #define nssv_BASIC_STRING_VIEW_I(T,U)  typename std::decay< basic_string_view<T,U> >::type
 
@@ -1049,7 +1239,7 @@
 #undef nssv_MSVC_ORDER
 #undef nssv_BASIC_STRING_VIEW_I
 
-#endif // nssv_CPP11_OR_GREATER
+#endif // compiler-dependent approach to comparisons
 
 // 24.4.4 Inserters and extractors:
 
diff --git a/single_include/inja/inja.hpp b/single_include/inja/inja.hpp
index beb284e..6cd3bf5 100644
--- a/single_include/inja/inja.hpp
+++ b/single_include/inja/inja.hpp
@@ -1,5 +1,7 @@
-#ifndef PANTOR_INJA_HPP
-#define PANTOR_INJA_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_INJA_HPP_
+#define INCLUDE_INJA_INJA_HPP_
 
 #include <functional>
 #include <iostream>
@@ -12,8 +14,10 @@
 #include <nlohmann/json.hpp>
 
 // #include "environment.hpp"
-#ifndef PANTOR_INJA_ENVIRONMENT_HPP
-#define PANTOR_INJA_ENVIRONMENT_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
+#define INCLUDE_INJA_ENVIRONMENT_HPP_
 
 #include <memory>
 #include <fstream>
@@ -23,8 +27,10 @@
 #include <nlohmann/json.hpp>
 
 // #include "config.hpp"
-#ifndef PANTOR_INJA_CONFIG_HPP
-#define PANTOR_INJA_CONFIG_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_CONFIG_HPP_
+#define INCLUDE_INJA_CONFIG_HPP_
 
 #include <functional>
 #include <string>
@@ -44,7 +50,7 @@
 #define NONSTD_SV_LITE_H_INCLUDED
 
 #define string_view_lite_MAJOR  1
-#define string_view_lite_MINOR  1
+#define string_view_lite_MINOR  4
 #define string_view_lite_PATCH  0
 
 #define string_view_lite_VERSION  nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
@@ -250,7 +256,7 @@
 # define nssv_COMPILER_MSVC_VERSION  0
 #endif
 
-#define nssv_COMPILER_VERSION( major, minor, patch )  (10 * ( 10 * major + minor) + patch)
+#define nssv_COMPILER_VERSION( major, minor, patch )  ( 10 * ( 10 * (major) + (minor) ) + (patch) )
 
 #if defined(__clang__)
 # define nssv_COMPILER_CLANG_VERSION  nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
@@ -305,8 +311,10 @@
 #define nssv_HAVE_WCHAR16_T             nssv_CPP11_100
 #define nssv_HAVE_WCHAR32_T             nssv_CPP11_100
 
-#if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
+#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
 # define nssv_HAVE_STD_DEFINED_LITERALS  nssv_CPP11_140
+#else
+# define nssv_HAVE_STD_DEFINED_LITERALS  0
 #endif
 
 // Presence of C++14 language features:
@@ -434,6 +442,22 @@
 
 namespace nonstd { namespace sv_lite {
 
+#if nssv_CPP11_OR_GREATER
+
+namespace detail {
+
+// Expect tail call optimization to make length() non-recursive:
+
+template< typename CharT >
+inline constexpr std::size_t length( CharT * s, std::size_t result = 0 )
+{
+    return *s == '\0' ? result : length( s + 1, result + 1 );
+}
+
+} // namespace detail
+
+#endif // nssv_CPP11_OR_GREATER
+
 template
 <
     class CharT,
@@ -487,14 +511,20 @@
     {}
 #endif
 
-    nssv_constexpr basic_string_view( CharT const * s, size_type count )
+    nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept
         : data_( s )
         , size_( count )
     {}
 
-    nssv_constexpr basic_string_view( CharT const * s)
+    nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept
         : data_( s )
+#if nssv_CPP17_OR_GREATER
         , size_( Traits::length(s) )
+#elif nssv_CPP11_OR_GREATER
+        , size_( detail::length(s) )
+#else
+        , size_( Traits::length(s) )
+#endif
     {}
 
     // Assignment:
@@ -550,7 +580,7 @@
 #else
         if ( pos >= size() )
         {
-            throw std::out_of_range("nonst::string_view::at()");
+            throw std::out_of_range("nonstd::string_view::at()");
         }
 #endif
         return data_at( pos );
@@ -592,7 +622,7 @@
 #else
         if ( pos > size() )
         {
-            throw std::out_of_range("nonst::string_view::copy()");
+            throw std::out_of_range("nonstd::string_view::copy()");
         }
 #endif
         const size_type rlen = (std::min)( n, size() - pos );
@@ -609,7 +639,7 @@
 #else
         if ( pos > size() )
         {
-            throw std::out_of_range("nonst::string_view::substr()");
+            throw std::out_of_range("nonstd::string_view::substr()");
         }
 #endif
         return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
@@ -620,7 +650,9 @@
     nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1)
     {
         if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
+        {
             return result;
+        }
 
         return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
     }
@@ -716,10 +748,14 @@
     nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
     {
         if ( size() < v.size() )
+        {
             return npos;
+        }
 
         if ( v.empty() )
+        {
             return (std::min)( size(), pos );
+        }
 
         const_iterator last   = cbegin() + (std::min)( size() - v.size(), pos ) + v.size();
         const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq );
@@ -857,7 +893,7 @@
     {
         const basic_string_view v;
 
-        nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {}
+        nssv_constexpr explicit not_in_view( basic_string_view v ) : v( v ) {}
 
         nssv_constexpr bool operator()( CharT c ) const
         {
@@ -984,7 +1020,167 @@
 // constexpr and noexcept so that an object t with an implicit conversion
 // to S can be compared according to Table 67.
 
-#if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
+#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
+
+// accomodate for older compilers:
+
+// ==
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+// !=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) != 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) != 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() != rhs.size() && lhs.compare( rhs ) != 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return lhs.size() != rhs.size() || rhs.compare( lhs ) != 0; }
+
+// <
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) > 0; }
+
+// <=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) >= 0; }
+
+// >
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) < 0; }
+
+// >=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    basic_string_view<CharT, Traits> lhs,
+    char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    char const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) <= 0; }
+
+#else // newer compilers:
 
 #define nssv_BASIC_STRING_VIEW_I(T,U)  typename std::decay< basic_string_view<T,U> >::type
 
@@ -1081,7 +1277,7 @@
 #undef nssv_MSVC_ORDER
 #undef nssv_BASIC_STRING_VIEW_I
 
-#endif // nssv_CPP11_OR_GREATER
+#endif // compiler-dependent approach to comparisons
 
 // 24.4.4 Inserters and extractors:
 
@@ -1400,19 +1596,23 @@
   ElementNotation notation {ElementNotation::Dot};
 };
 
-}
+}  // namespace inja
 
-#endif // PANTOR_INJA_CONFIG_HPP
+#endif  // INCLUDE_INJA_CONFIG_HPP_
 
 // #include "function_storage.hpp"
-#ifndef PANTOR_INJA_FUNCTION_STORAGE_HPP
-#define PANTOR_INJA_FUNCTION_STORAGE_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
+#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
 
 #include <vector>
 
 // #include "bytecode.hpp"
-#ifndef PANTOR_INJA_BYTECODE_HPP
-#define PANTOR_INJA_BYTECODE_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_BYTECODE_HPP_
+#define INCLUDE_INJA_BYTECODE_HPP_
 
 #include <string>
 #include <utility>
@@ -1543,7 +1743,7 @@
 
 }  // namespace inja
 
-#endif  // PANTOR_INJA_BYTECODE_HPP
+#endif  // INCLUDE_INJA_BYTECODE_HPP_
 
 // #include "string_view.hpp"
 
@@ -1551,7 +1751,7 @@
 
 namespace inja {
 
-using namespace nlohmann;
+using json = nlohmann::json;
 
 using Arguments = std::vector<const json*>;
 using CallbackFunction = std::function<json(Arguments& args)>;
@@ -1616,11 +1816,13 @@
 
 }
 
-#endif // PANTOR_INJA_FUNCTION_STORAGE_HPP
+#endif  // INCLUDE_INJA_FUNCTION_STORAGE_HPP_
 
 // #include "parser.hpp"
-#ifndef PANTOR_INJA_PARSER_HPP
-#define PANTOR_INJA_PARSER_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_PARSER_HPP_
+#define INCLUDE_INJA_PARSER_HPP_
 
 #include <limits>
 #include <string>
@@ -1634,8 +1836,10 @@
 // #include "function_storage.hpp"
 
 // #include "lexer.hpp"
-#ifndef PANTOR_INJA_LEXER_HPP
-#define PANTOR_INJA_LEXER_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_LEXER_HPP_
+#define INCLUDE_INJA_LEXER_HPP_
 
 #include <cctype>
 #include <locale>
@@ -1643,8 +1847,10 @@
 // #include "config.hpp"
 
 // #include "token.hpp"
-#ifndef PANTOR_INJA_TOKEN_HPP
-#define PANTOR_INJA_TOKEN_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_TOKEN_HPP_
+#define INCLUDE_INJA_TOKEN_HPP_
 
 #include <string>
 
@@ -1710,11 +1916,13 @@
 
 }
 
-#endif  // PANTOR_INJA_TOKEN_HPP
+#endif  // INCLUDE_INJA_TOKEN_HPP_
 
 // #include "utils.hpp"
-#ifndef PANTOR_INJA_UTILS_HPP
-#define PANTOR_INJA_UTILS_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_UTILS_HPP_
+#define INCLUDE_INJA_UTILS_HPP_
 
 #include <algorithm>
 #include <fstream>
@@ -1761,11 +1969,11 @@
   inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) {
     return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
   }
-}  // namespace string
+}  // namespace string_view
 
 }  // namespace inja
 
-#endif  // PANTOR_INJA_UTILS_HPP
+#endif  // INCLUDE_INJA_UTILS_HPP_
 
 
 
@@ -1835,7 +2043,7 @@
                    inja::string_view::starts_with(open_str, m_config.line_statement)) {
           m_state = State::LineStart;
         } else {
-          m_pos += 1; // wasn't actually an opening sequence
+          m_pos += 1;  // wasn't actually an opening sequence
           goto again;
         }
 
@@ -2063,11 +2271,13 @@
 
 }
 
-#endif // PANTOR_INJA_LEXER_HPP
+#endif  // INCLUDE_INJA_LEXER_HPP_
 
 // #include "template.hpp"
-#ifndef PANTOR_INJA_TEMPLATE_HPP
-#define PANTOR_INJA_TEMPLATE_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_TEMPLATE_HPP_
+#define INCLUDE_INJA_TEMPLATE_HPP_
 
 #include <map>
 #include <string>
@@ -2089,9 +2299,9 @@
 
 using TemplateStorage = std::map<std::string, Template>;
 
-}
+}  // namespace inja
 
-#endif  // PANTOR_INJA_TEMPLATE_HPP
+#endif  // INCLUDE_INJA_TEMPLATE_HPP_
 
 // #include "token.hpp"
 
@@ -2644,11 +2854,13 @@
 
 }  // namespace inja
 
-#endif  // PANTOR_INJA_PARSER_HPP
+#endif  // INCLUDE_INJA_PARSER_HPP_
 
 // #include "polyfill.hpp"
-#ifndef PANTOR_INJA_POLYFILL_HPP
-#define PANTOR_INJA_POLYFILL_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_POLYFILL_HPP_
+#define INCLUDE_INJA_POLYFILL_HPP_
 
 
 #if __cplusplus < 201402L
@@ -2660,48 +2872,51 @@
 
 
 namespace stdinja {
-  template<class T> struct _Unique_if {
-    typedef std::unique_ptr<T> _Single_object;
-  };
 
-  template<class T> struct _Unique_if<T[]> {
-    typedef std::unique_ptr<T[]> _Unknown_bound;
-  };
+template<class T> struct _Unique_if {
+  typedef std::unique_ptr<T> _Single_object;
+};
 
-  template<class T, size_t N> struct _Unique_if<T[N]> {
-    typedef void _Known_bound;
-  };
+template<class T> struct _Unique_if<T[]> {
+  typedef std::unique_ptr<T[]> _Unknown_bound;
+};
 
-  template<class T, class... Args>
-  typename _Unique_if<T>::_Single_object
-  make_unique(Args&&... args) {
-    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-  }
+template<class T, size_t N> struct _Unique_if<T[N]> {
+  typedef void _Known_bound;
+};
 
-  template<class T>
-  typename _Unique_if<T>::_Unknown_bound
-  make_unique(size_t n) {
-    typedef typename std::remove_extent<T>::type U;
-    return std::unique_ptr<T>(new U[n]());
-  }
-
-  template<class T, class... Args>
-  typename _Unique_if<T>::_Known_bound
-  make_unique(Args&&...) = delete;
+template<class T, class... Args>
+typename _Unique_if<T>::_Single_object
+make_unique(Args&&... args) {
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
 }
 
+template<class T>
+typename _Unique_if<T>::_Unknown_bound
+make_unique(size_t n) {
+  typedef typename std::remove_extent<T>::type U;
+  return std::unique_ptr<T>(new U[n]());
+}
+
+template<class T, class... Args>
+typename _Unique_if<T>::_Known_bound
+make_unique(Args&&...) = delete;
+
+}  // namespace stdinja
+
 #else
 
 namespace stdinja = std;
 
-#endif // memory */
+#endif  // memory */
 
-
-#endif // PANTOR_INJA_POLYFILL_HPP
+#endif  // INCLUDE_INJA_POLYFILL_HPP_
 
 // #include "renderer.hpp"
-#ifndef PANTOR_INJA_RENDERER_HPP
-#define PANTOR_INJA_RENDERER_HPP
+// Copyright (c) 2019 Pantor. All rights reserved.
+
+#ifndef INCLUDE_INJA_RENDERER_HPP_
+#define INCLUDE_INJA_RENDERER_HPP_
 
 #include <algorithm>
 #include <numeric>
@@ -2843,22 +3058,21 @@
     enum class Type { Map, Array };
 
     Type loop_type;
-    nonstd::string_view key_name;   // variable name for keys
-    nonstd::string_view value_name; // variable name for values
-    json data;                      // data with loop info added
+    nonstd::string_view key_name;    // variable name for keys
+    nonstd::string_view value_name;  // variable name for values
+    json data;                       // data with loop info added
 
-    json values;                    // values to iterate over
+    json values;                     // values to iterate over
 
     // loop over list
-    size_t index;                   // current list index
-    size_t size;                    // length of list
+    size_t index;                    // current list index
+    size_t size;                     // length of list
 
     // loop over map
     using KeyValue = std::pair<nonstd::string_view, json*>;
     using MapValues = std::vector<KeyValue>;
-    MapValues map_values;           // values to iterate over
-    MapValues::iterator map_it;     // iterator over values
-
+    MapValues map_values;            // values to iterate over
+    MapValues::iterator map_it;      // iterator over values
   };
 
   std::vector<LoopLevel> m_loop_stack;
@@ -3213,7 +3427,8 @@
             for (auto it = level.values.begin(), end = level.values.end(); it != end; ++it) {
               level.map_values.emplace_back(it.key(), &it.value());
             }
-            std::sort(level.map_values.begin(), level.map_values.end(), [](const LoopLevel::KeyValue& a, const LoopLevel::KeyValue& b) { return a.first < b.first; });
+            auto sort_lambda = [](const LoopLevel::KeyValue& a, const LoopLevel::KeyValue& b) { return a.first < b.first; };
+            std::sort(level.map_values.begin(), level.map_values.end(), sort_lambda);
             level.map_it = level.map_values.begin();
           } else {
             if (!level.values.is_array()) {
@@ -3281,7 +3496,7 @@
 
 }  // namespace inja
 
-#endif  // PANTOR_INJA_RENDERER_HPP
+#endif  // INCLUDE_INJA_RENDERER_HPP_
 
 // #include "string_view.hpp"
 
@@ -3293,91 +3508,71 @@
 
 namespace inja {
 
-using namespace nlohmann;
+using json = nlohmann::json;
 
 /*!
  * \brief Class for changing the configuration.
  */
 class Environment {
-  class Impl {
-   public:
-    std::string input_path;
-    std::string output_path;
-
-    LexerConfig lexer_config;
-    ParserConfig parser_config;
-
-    FunctionStorage callbacks;
-    TemplateStorage included_templates;
-  };
-
-  std::unique_ptr<Impl> m_impl;
-
  public:
   Environment(): Environment("") { }
 
-  explicit Environment(const std::string& global_path): m_impl(stdinja::make_unique<Impl>()) {
-    m_impl->input_path = global_path;
-    m_impl->output_path = global_path;
-  }
+  explicit Environment(const std::string& global_path): m_input_path(global_path), m_output_path(global_path) {}
 
-  explicit Environment(const std::string& input_path, const std::string& output_path): m_impl(stdinja::make_unique<Impl>()) {
-    m_impl->input_path = input_path;
-    m_impl->output_path = output_path;
-  }
+  Environment(const std::string& input_path, const std::string& output_path): m_input_path(input_path), m_output_path(output_path) {}
 
   /// Sets the opener and closer for template statements
   void set_statement(const std::string& open, const std::string& close) {
-    m_impl->lexer_config.statement_open = open;
-    m_impl->lexer_config.statement_close = close;
-    m_impl->lexer_config.update_open_chars();
+    m_lexer_config.statement_open = open;
+    m_lexer_config.statement_close = close;
+    m_lexer_config.update_open_chars();
   }
 
   /// Sets the opener for template line statements
   void set_line_statement(const std::string& open) {
-    m_impl->lexer_config.line_statement = open;
-    m_impl->lexer_config.update_open_chars();
+    m_lexer_config.line_statement = open;
+    m_lexer_config.update_open_chars();
   }
 
   /// Sets the opener and closer for template expressions
   void set_expression(const std::string& open, const std::string& close) {
-    m_impl->lexer_config.expression_open = open;
-    m_impl->lexer_config.expression_close = close;
-    m_impl->lexer_config.update_open_chars();
+    m_lexer_config.expression_open = open;
+    m_lexer_config.expression_close = close;
+    m_lexer_config.update_open_chars();
   }
 
   /// Sets the opener and closer for template comments
   void set_comment(const std::string& open, const std::string& close) {
-    m_impl->lexer_config.comment_open = open;
-    m_impl->lexer_config.comment_close = close;
-    m_impl->lexer_config.update_open_chars();
+    m_lexer_config.comment_open = open;
+    m_lexer_config.comment_close = close;
+    m_lexer_config.update_open_chars();
   }
 
   /// Sets whether to remove the first newline after a block
   void set_trim_blocks(bool trim_blocks) {
-    m_impl->lexer_config.trim_blocks = trim_blocks;
+    m_lexer_config.trim_blocks = trim_blocks;
   }
 
   /// Sets whether to strip the spaces and tabs from the start of a line to a block
   void set_lstrip_blocks(bool lstrip_blocks) {
-    m_impl->lexer_config.lstrip_blocks = lstrip_blocks;
+    m_lexer_config.lstrip_blocks = lstrip_blocks;
   }
 
   /// Sets the element notation syntax
   void set_element_notation(ElementNotation notation) {
-    m_impl->parser_config.notation = notation;
+    m_parser_config.notation = notation;
   }
 
 
   Template parse(nonstd::string_view input) {
-    Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
+    Parser parser(m_parser_config, m_lexer_config, m_included_templates);
     return parser.parse(input);
   }
 
   Template parse_template(const std::string& filename) {
-    Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
-		return parser.parse_template(m_impl->input_path + static_cast<std::string>(filename));
-	}
+    Parser parser(m_parser_config, m_lexer_config, m_included_templates);
+    return parser.parse_template(m_input_path + static_cast<std::string>(filename));
+  }
 
   std::string render(nonstd::string_view input, const json& data) {
     return render(parse(input), data);
@@ -3390,55 +3585,55 @@
   }
 
   std::string render_file(const std::string& filename, const json& data) {
-		return render(parse_template(filename), data);
-	}
+    return render(parse_template(filename), data);
+  }
 
   std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) {
-		const json data = load_json(filename_data);
-		return render_file(filename, data);
-	}
+    const json data = load_json(filename_data);
+    return render_file(filename, data);
+  }
 
   void write(const std::string& filename, const json& data, const std::string& filename_out) {
-		std::ofstream file(m_impl->output_path + filename_out);
-		file << render_file(filename, data);
-		file.close();
-	}
+    std::ofstream file(m_output_path + filename_out);
+    file << render_file(filename, data);
+    file.close();
+  }
 
   void write(const Template& temp, const json& data, const std::string& filename_out) {
-		std::ofstream file(m_impl->output_path + filename_out);
-		file << render(temp, data);
-		file.close();
-	}
+    std::ofstream file(m_output_path + filename_out);
+    file << render(temp, data);
+    file.close();
+  }
 
-	void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
-		const json data = load_json(filename_data);
-		write(filename, data, filename_out);
-	}
+  void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
+    const json data = load_json(filename_data);
+    write(filename, data, filename_out);
+  }
 
-	void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
-		const json data = load_json(filename_data);
-		write(temp, data, filename_out);
-	}
+  void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
+    const json data = load_json(filename_data);
+    write(temp, data, filename_out);
+  }
 
   std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
-    Renderer(m_impl->included_templates, m_impl->callbacks).render_to(os, tmpl, data);
+    Renderer(m_included_templates, m_callbacks).render_to(os, tmpl, data);
     return os;
   }
 
   std::string load_file(const std::string& filename) {
-    Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
-		return parser.load_file(m_impl->input_path + filename);
-	}
+    Parser parser(m_parser_config, m_lexer_config, m_included_templates);
+    return parser.load_file(m_input_path + filename);
+  }
 
   json load_json(const std::string& filename) {
-		std::ifstream file = open_file_or_throw(m_impl->input_path + filename);
-		json j;
-		file >> j;
-		return j;
-	}
+    std::ifstream file = open_file_or_throw(m_input_path + filename);
+    json j;
+    file >> j;
+    return j;
+  }
 
   void add_callback(const std::string& name, unsigned int numArgs, const CallbackFunction& callback) {
-    m_impl->callbacks.add_callback(name, numArgs, callback);
+    m_callbacks.add_callback(name, numArgs, callback);
   }
 
   /** Includes a template with a given name into the environment.
@@ -3446,8 +3641,18 @@
    * include "<name>" syntax.
    */
   void include_template(const std::string& name, const Template& tmpl) {
-    m_impl->included_templates[name] = tmpl;
+    m_included_templates[name] = tmpl;
   }
+
+ private:
+  std::string m_input_path;
+  std::string m_output_path;
+
+  LexerConfig m_lexer_config;
+  ParserConfig m_parser_config;
+
+  FunctionStorage m_callbacks;
+  TemplateStorage m_included_templates;
 };
 
 /*!
@@ -3467,7 +3672,7 @@
 
 }
 
-#endif // PANTOR_INJA_ENVIRONMENT_HPP
+#endif  // INCLUDE_INJA_ENVIRONMENT_HPP_
 
 // #include "string_view.hpp"
 
@@ -3479,4 +3684,4 @@
 
 
 
-#endif  // PANTOR_INJA_HPP
+#endif  // INCLUDE_INJA_INJA_HPP_
diff --git a/test/unit.cpp b/test/unit.cpp
index 96c87b1..fa3e3d5 100644
--- a/test/unit.cpp
+++ b/test/unit.cpp
@@ -2,3 +2,33 @@
 
 #define CATCH_CONFIG_MAIN
 #include "catch/catch.hpp"
+#include "inja/inja.hpp"
+
+
+using json = nlohmann::json;
+
+
+TEST_CASE("copy-environment") {
+    inja::Environment env;
+    env.add_callback("double", 1, [](inja::Arguments& args) {
+        int number = args.at(0)->get<int>();
+        return 2 * number;
+    });
+
+    inja::Template t1 = env.parse("{{ double(2) }}");
+    env.include_template("tpl", t1);
+    std::string test_tpl = "{% include \"tpl\" %}";
+
+    REQUIRE(env.render(test_tpl, json()) == "4");
+
+    inja::Environment copy(env);
+    CHECK(copy.render(test_tpl, json()) == "4");
+
+    // overwrite template in source env
+    inja::Template t2 = env.parse("{{ double(4) }}");
+    env.include_template("tpl", t2);
+    REQUIRE(env.render(test_tpl, json()) == "8");
+
+    // template is unchanged in copy
+    CHECK(copy.render(test_tpl, json()) == "4");
+}