#ifndef JINJA2CPP_SRC_VALUE_VISITORS_H #define JINJA2CPP_SRC_VALUE_VISITORS_H #include "expression_evaluator.h" #include "helpers.h" #include "jinja2cpp/value.h" #include <boost/algorithm/string/predicate.hpp> #include <boost/optional.hpp> #include <fmt/format.h> #include <fmt/xchar.h> #include <iostream> #include <cmath> #include <limits> #include <utility> #include <typeinfo> namespace jinja2 { namespace detail { template<typename V> struct RecursiveUnwrapper { V* m_visitor{}; RecursiveUnwrapper(V* v) : m_visitor(v) {} template<typename T> static const auto& UnwrapRecursive(const T& arg) { return arg; // std::forward<T>(arg); } template<typename T> static auto& UnwrapRecursive(const RecursiveWrapper<T>& arg) { return arg.GetValue(); } // template<typename T> // static auto& UnwrapRecursive(RecursiveWrapper<T>& arg) // { // return arg.GetValue(); // } template<typename ... Args> auto operator()(const Args& ... args) const { assert(m_visitor != nullptr); return (*m_visitor)(UnwrapRecursive(args)...); } }; template<typename Fn> auto ApplyUnwrapped(const InternalValueData& val, Fn&& fn) { auto valueRef = GetIf<ValueRef>(&val); auto targetString = GetIf<TargetString>(&val); auto targetSV = GetIf<TargetStringView>(&val); // auto internalValueRef = GetIf<InternalValueRef>(&val); if (valueRef != nullptr) return fn(valueRef->get().data()); else if (targetString != nullptr) return fn(*targetString); else if (targetSV != nullptr) return fn(*targetSV); // else if (internalValueRef != nullptr) // return fn(internalValueRef->get()); return fn(val); } } // namespace detail template<typename V, typename ... Args> auto Apply(const InternalValue& val, Args&& ... args) { return detail::ApplyUnwrapped(val.GetData(), [&args...](auto& val) { auto v = V(args...); return std::visit(detail::RecursiveUnwrapper<V>(&v), val); }); } template<typename V, typename ... Args> auto Apply2(const InternalValue& val1, const InternalValue& val2, Args&& ... args) { return detail::ApplyUnwrapped(val1.GetData(), [&val2, &args...](auto& uwVal1) { return detail::ApplyUnwrapped(val2.GetData(), [&uwVal1, &args...](auto& uwVal2) { auto v = V(args...); return std::visit(detail::RecursiveUnwrapper<V>(&v), uwVal1, uwVal2); }); }); } bool ConvertToBool(const InternalValue& val); namespace visitors { template<typename R = InternalValue> struct BaseVisitor { R operator() (const GenericMap&) const { assert(false); return R(); } R operator() (const GenericList&) const { assert(false); return R(); } R operator() (const ValueRef&) const { assert(false); return R(); } R operator() (const TargetString&) const { assert(false); return R(); } template<typename T> R operator() (T&&) const { return R(); } template<typename T, typename U> R operator() (T&&, U&&) const { return R(); } }; template<typename CharT> struct ValueRendererBase { ValueRendererBase(std::basic_string<CharT>& os) : m_os(&os) { } template<typename T> void operator()(const T& val) const; void operator()(double val) const; void operator()(const std::basic_string_view<CharT>& val) const { m_os->append(val.begin(), val.end()); } void operator()(const std::basic_string<CharT>& val) const { m_os->append(val.begin(), val.end()); } void operator()(const EmptyValue&) const {} void operator()(const ValuesList&) const {} void operator()(const ValuesMap&) const {} void operator()(const GenericMap&) const {} void operator()(const GenericList&) const {} void operator()(const MapAdapter&) const {} void operator()(const ListAdapter&) const {} void operator()(const ValueRef&) const {} void operator()(const TargetString&) const {} void operator()(const TargetStringView&) const {} void operator()(const KeyValuePair&) const {} void operator()(const Callable&) const {} void operator()(const UserCallable&) const {} void operator()(const std::shared_ptr<IRendererBase>) const {} template<typename T> void operator()(const boost::recursive_wrapper<T>&) const {} template<typename T> void operator()(const RecWrapper<T>&) const {} auto GetOs() const { return std::back_inserter(*m_os); } std::basic_string<CharT>* m_os; }; template<> template<typename T> void ValueRendererBase<char>::operator()(const T& val) const { fmt::format_to(GetOs(), "{}", val); } template<> template<typename T> void ValueRendererBase<wchar_t>::operator()(const T& val) const { fmt::format_to(GetOs(), L"{}", val); } template<> inline void ValueRendererBase<char>::operator()(double val) const { fmt::format_to(GetOs(), "{:.8g}", val); } template<> inline void ValueRendererBase<wchar_t>::operator()(double val) const { fmt::format_to(GetOs(), L"{:.8g}", val); } struct InputValueConvertor { using result_t = boost::optional<InternalValue>; InputValueConvertor(bool byValue, bool allowStringRef) : m_byValue(byValue) , m_allowStringRef(allowStringRef) { } template<typename ChT> result_t operator()(const std::basic_string<ChT>& val) const { if (m_allowStringRef) return result_t(TargetStringView(std::basic_string_view<ChT>(val))); return result_t(TargetString(val)); } template<typename ChT> result_t operator()(std::basic_string<ChT>& val) const { return result_t(TargetString(std::move(val))); } result_t operator()(const ValuesList& vals) const { if (m_byValue) { ValuesList newVals(vals); return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(newVals)))); } return result_t(InternalValue(ListAdapter::CreateAdapter(vals))); } result_t operator() (ValuesList& vals) const { return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(vals)))); } result_t operator() (const GenericList& vals) const { if (m_byValue) { GenericList newVals(vals); return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(newVals)))); } return result_t(InternalValue(ListAdapter::CreateAdapter(vals))); } result_t operator() (GenericList& vals) const { return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(vals)))); } result_t operator()(const ValuesMap& vals) const { if (m_byValue) { ValuesMap newVals(vals); return result_t(CreateMapAdapter(std::move(newVals))); } return result_t(CreateMapAdapter(vals)); } result_t operator()(ValuesMap& vals) const { return result_t(CreateMapAdapter(std::move(vals))); } result_t operator()(const GenericMap& vals) const { if (m_byValue) { GenericMap newVals(vals); return result_t(CreateMapAdapter(std::move(newVals))); } return result_t(CreateMapAdapter(vals)); } result_t operator()(GenericMap& vals) const { return result_t(CreateMapAdapter(std::move(vals))); } result_t operator()(const UserCallable& val) const { return ConvertUserCallable(val); } result_t operator()(UserCallable& val) const { return ConvertUserCallable(std::move(val)); } template<typename T> result_t operator()(const RecWrapper<T>& val) const { return this->operator()(const_cast<const T&>(*val)); } template<typename T> result_t operator()(RecWrapper<T>& val) const { return this->operator()(*val); } template<typename T> result_t operator()(const T& val) const { return result_t(InternalValue(val)); } static result_t ConvertUserCallable(const UserCallable& val); bool m_byValue{}; bool m_allowStringRef{}; }; template<typename CharT> struct ValueRenderer; template<> struct ValueRenderer<char> : ValueRendererBase<char> { ValueRenderer(std::string& os) : ValueRendererBase<char>::ValueRendererBase<char>(os) { } using ValueRendererBase<char>::operator (); void operator()(const std::wstring& str) const { (*m_os) += ConvertString<std::string>(str); } void operator()(const std::wstring_view& str) const { (*m_os) += ConvertString<std::string>(str); } void operator() (bool val) const { m_os->append(val ? "true" : "false"); } }; template<> struct ValueRenderer<wchar_t> : ValueRendererBase<wchar_t> { ValueRenderer(std::wstring& os) : ValueRendererBase<wchar_t>::ValueRendererBase<wchar_t>(os) { } using ValueRendererBase<wchar_t>::operator (); void operator()(const std::string& str) const { (*m_os) += ConvertString<std::wstring>(str); } void operator()(const std::string_view& str) const { (*m_os) += ConvertString<std::wstring>(str); } void operator() (bool val) const { // fmt::format_to(GetOs(), L"{}", (const wchar_t*)(val ? "true" : "false")); m_os->append(val ? L"true" : L"false"); } }; struct UnaryOperation : BaseVisitor<InternalValue> { using BaseVisitor::operator (); UnaryOperation(UnaryExpression::Operation oper) : m_oper(oper) { } InternalValue operator() (int64_t val) const { InternalValue result; switch (m_oper) { case jinja2::UnaryExpression::LogicalNot: result = val ? false : true; break; case jinja2::UnaryExpression::UnaryPlus: result = +val; break; case jinja2::UnaryExpression::UnaryMinus: result = -val; break; } return result; } InternalValue operator() (double val) const { InternalValue result; switch (m_oper) { case jinja2::UnaryExpression::LogicalNot: result = fabs(val) > std::numeric_limits<double>::epsilon() ? false : true; break; case jinja2::UnaryExpression::UnaryPlus: result = +val; break; case jinja2::UnaryExpression::UnaryMinus: result = -val; break; } return result; } InternalValue operator() (bool val) const { InternalValue result; switch (m_oper) { case jinja2::UnaryExpression::LogicalNot: result = !val; break; default: break; } return result; } InternalValue operator() (const MapAdapter&) const { InternalValue result; switch (m_oper) { case jinja2::UnaryExpression::LogicalNot: result = true; break; default: break; } return result; } InternalValue operator() (const ListAdapter&) const { InternalValue result; switch (m_oper) { case jinja2::UnaryExpression::LogicalNot: result = true; break; default: break; } return result; } template<typename CharT> InternalValue operator() (const std::basic_string<CharT>& val) const { InternalValue result; switch (m_oper) { case jinja2::UnaryExpression::LogicalNot: result = val.empty(); break; default: break; } return result; } template<typename CharT> InternalValue operator() (const std::basic_string_view<CharT>& val) const { InternalValue result; switch (m_oper) { case jinja2::UnaryExpression::LogicalNot: result = val.empty(); break; default: break; } return result; } InternalValue operator() (const EmptyValue&) const { InternalValue result; switch (m_oper) { case jinja2::UnaryExpression::LogicalNot: result = true; break; default: break; } return result; } UnaryExpression::Operation m_oper; }; struct BinaryMathOperation : BaseVisitor<> { using BaseVisitor::operator (); using ResultType = InternalValue; // InternalValue operator() (int, int) const {return InternalValue();} bool AlmostEqual(double x, double y) const { return std::abs(x - y) <= std::numeric_limits<double>::epsilon() * std::abs(x + y) * 6 || std::abs(x - y) < std::numeric_limits<double>::min(); } BinaryMathOperation(BinaryExpression::Operation oper, BinaryExpression::CompareType compType = BinaryExpression::CaseSensitive) : m_oper(oper) , m_compType(compType) { } ResultType operator() (double left, double right) const { ResultType result = 0.0; switch (m_oper) { case jinja2::BinaryExpression::Plus: result = left + right; break; case jinja2::BinaryExpression::Minus: result = left - right; break; case jinja2::BinaryExpression::Mul: result = left * right; break; case jinja2::BinaryExpression::Div: result = left / right; break; case jinja2::BinaryExpression::DivReminder: result = std::remainder(left, right); break; case jinja2::BinaryExpression::DivInteger: { double val = left / right; result = val < 0 ? ceil(val) : floor(val); break; } case jinja2::BinaryExpression::Pow: result = pow(left, right); break; case jinja2::BinaryExpression::LogicalEq: result = AlmostEqual(left, right); break; case jinja2::BinaryExpression::LogicalNe: result = !AlmostEqual(left, right); break; case jinja2::BinaryExpression::LogicalGt: result = left > right; break; case jinja2::BinaryExpression::LogicalLt: result = left < right; break; case jinja2::BinaryExpression::LogicalGe: result = left > right || AlmostEqual(left, right); break; case jinja2::BinaryExpression::LogicalLe: result = left < right || AlmostEqual(left, right); break; default: break; } return result; } ResultType operator() (int64_t left, int64_t right) const { ResultType result; switch (m_oper) { case jinja2::BinaryExpression::Plus: result = left + right; break; case jinja2::BinaryExpression::Minus: result = left - right; break; case jinja2::BinaryExpression::Mul: result = left * right; break; case jinja2::BinaryExpression::DivInteger: result = left / right; break; case jinja2::BinaryExpression::Div: case jinja2::BinaryExpression::DivReminder: case jinja2::BinaryExpression::Pow: result = this->operator ()(static_cast<double>(left), static_cast<double>(right)); break; case jinja2::BinaryExpression::LogicalEq: result = left == right; break; case jinja2::BinaryExpression::LogicalNe: result = left != right; break; case jinja2::BinaryExpression::LogicalGt: result = left > right; break; case jinja2::BinaryExpression::LogicalLt: result = left < right; break; case jinja2::BinaryExpression::LogicalGe: result = left >= right; break; case jinja2::BinaryExpression::LogicalLe: result = left <= right; break; default: break; } return result; } ResultType operator() (int64_t left, double right) const { return this->operator ()(static_cast<double>(left), static_cast<double>(right)); } ResultType operator() (double left, int64_t right) const { return this->operator ()(static_cast<double>(left), static_cast<double>(right)); } template<typename CharT> ResultType operator() (const std::basic_string<CharT> &left, const std::basic_string<CharT> &right) const { return ProcessStrings(std::basic_string_view<CharT>(left), std::basic_string_view<CharT>(right)); } template<typename CharT1, typename CharT2> std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string<CharT1>& left, const std::basic_string<CharT2>& right) const { auto rightStr = ConvertString<std::basic_string<CharT1>>(right); return ProcessStrings(std::basic_string_view<CharT1>(left), std::basic_string_view<CharT1>(rightStr)); } template<typename CharT> ResultType operator() (const std::basic_string_view<CharT> &left, const std::basic_string<CharT> &right) const { return ProcessStrings(left, std::basic_string_view<CharT>(right)); } template<typename CharT1, typename CharT2> std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string_view<CharT1>& left, const std::basic_string<CharT2>& right) const { auto rightStr = ConvertString<std::basic_string<CharT1>>(right); return ProcessStrings(left, std::basic_string_view<CharT1>(rightStr)); } template<typename CharT> ResultType operator() (const std::basic_string<CharT> &left, const std::basic_string_view<CharT> &right) const { return ProcessStrings(std::basic_string_view<CharT>(left), right); } template<typename CharT1, typename CharT2> std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string<CharT1>& left, const std::basic_string_view<CharT2>& right) const { auto rightStr = ConvertString<std::basic_string<CharT1>>(right); return ProcessStrings(std::basic_string_view<CharT1>(left), std::basic_string_view<CharT1>(rightStr)); } template<typename CharT> ResultType operator() (const std::basic_string_view<CharT> &left, const std::basic_string_view<CharT> &right) const { return ProcessStrings(left, right); } template<typename CharT1, typename CharT2> std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string_view<CharT1>& left, const std::basic_string_view<CharT2>& right) const { auto rightStr = ConvertString<std::basic_string<CharT1>>(right); return ProcessStrings(left, std::basic_string_view<CharT1>(rightStr)); } template<typename CharT> ResultType operator() (const std::basic_string<CharT> &left, int64_t right) const { return RepeatString(std::basic_string_view<CharT>(left), right); } template<typename CharT> ResultType operator() (const std::basic_string_view<CharT> &left, int64_t right) const { return RepeatString(left, right); } template<typename CharT> ResultType RepeatString(const std::basic_string_view<CharT>& left, const int64_t right) const { using string = std::basic_string<CharT>; ResultType result; if(m_oper == jinja2::BinaryExpression::Mul) { string str; for (int i = 0; i < right; ++i) str.append(left.begin(), left.end()); result = TargetString(std::move(str)); } return result; } template<typename CharT> ResultType ProcessStrings(const std::basic_string_view<CharT>& left, const std::basic_string_view<CharT>& right) const { using string = std::basic_string<CharT>; ResultType result; switch (m_oper) { case jinja2::BinaryExpression::Plus: { auto str = string(left.begin(), left.end()); str.append(right.begin(), right.end()); result = TargetString(std::move(str)); break; } case jinja2::BinaryExpression::LogicalEq: result = m_compType == BinaryExpression::CaseSensitive ? left == right : boost::iequals(left, right); break; case jinja2::BinaryExpression::LogicalNe: result = m_compType == BinaryExpression::CaseSensitive ? left != right : !boost::iequals(left, right); break; case jinja2::BinaryExpression::LogicalGt: result = m_compType == BinaryExpression::CaseSensitive ? left > right : boost::lexicographical_compare(right, left, boost::algorithm::is_iless()); break; case jinja2::BinaryExpression::LogicalLt: result = m_compType == BinaryExpression::CaseSensitive ? left < right : boost::lexicographical_compare(left, right, boost::algorithm::is_iless()); break; case jinja2::BinaryExpression::LogicalGe: if (m_compType == BinaryExpression::CaseSensitive) { result = left >= right; } else { result = boost::iequals(left, right) ? true : boost::lexicographical_compare(right, left, boost::algorithm::is_iless()); } break; case jinja2::BinaryExpression::LogicalLe: if (m_compType == BinaryExpression::CaseSensitive) { result = left <= right; } else { result = boost::iequals(left, right) ? true : boost::lexicographical_compare(left, right, boost::algorithm::is_iless()); } break; default: break; } return result; } ResultType operator() (const KeyValuePair& left, const KeyValuePair& right) const { ResultType result; switch (m_oper) { case jinja2::BinaryExpression::LogicalEq: result = ConvertToBool(this->operator ()(left.key, right.key)) && ConvertToBool(Apply2<BinaryMathOperation>(left.value, right.value, BinaryExpression::LogicalEq, m_compType)); break; case jinja2::BinaryExpression::LogicalNe: result = ConvertToBool(this->operator ()(left.key, right.key)) || ConvertToBool(Apply2<BinaryMathOperation>(left.value, right.value, BinaryExpression::LogicalNe, m_compType)); break; default: break; } return result; } ResultType operator() (const ListAdapter& left, const ListAdapter& right) const { ResultType result; if (m_oper == jinja2::BinaryExpression::Plus) { InternalValueList values; values.reserve(left.GetSize().value_or(0) + right.GetSize().value_or(0)); for (auto& v : left) values.push_back(v); for (auto& v : right) values.push_back(v); result = ListAdapter::CreateAdapter(std::move(values)); } return result; } ResultType operator() (const ListAdapter& left, int64_t right) const { ResultType result; if (right >= 0 && m_oper == jinja2::BinaryExpression::Mul) { InternalValueList values; values.reserve(left.GetSize().value_or(0)); for (auto& v : left) values.push_back(v); auto listSize = values.size() * right; result = ListAdapter::CreateAdapter(static_cast<size_t>(listSize), [size = values.size(), values = std::move(values)](size_t idx) { return values[idx % size]; }); } return result; } ResultType operator() (bool left, bool right) const { ResultType result; switch (m_oper) { case jinja2::BinaryExpression::LogicalEq: result = left == right; break; case jinja2::BinaryExpression::LogicalNe: result = left != right; break; case jinja2::BinaryExpression::LogicalLt: result = (left ? 1 : 0) < (right ? 1 : 0); break; default: break; } return result; } ResultType operator() (EmptyValue, EmptyValue) const { ResultType result; switch (m_oper) { case jinja2::BinaryExpression::LogicalEq: result = true; break; case jinja2::BinaryExpression::LogicalNe: result = false; break; default: break; } return result; } template<typename T> ResultType operator() (EmptyValue, T&&) const { ResultType result; switch (m_oper) { case jinja2::BinaryExpression::LogicalEq: result = false; break; case jinja2::BinaryExpression::LogicalNe: result = true; break; default: break; } return result; } template<typename T> ResultType operator() (T&&, EmptyValue) const { ResultType result; switch (m_oper) { case jinja2::BinaryExpression::LogicalEq: result = false; break; case jinja2::BinaryExpression::LogicalNe: result = true; break; default: break; } return result; } BinaryExpression::Operation m_oper; BinaryExpression::CompareType m_compType; }; struct BooleanEvaluator : BaseVisitor<bool> { using BaseVisitor::operator (); bool operator() (int64_t val) const { return val != 0; } bool operator() (double val) const { return fabs(val) < std::numeric_limits<double>::epsilon(); } bool operator() (bool val) const { return val; } template<typename CharT> bool operator()(const std::basic_string<CharT>& str) const { return !str.empty(); } template<typename CharT> bool operator()(const std::basic_string_view<CharT>& str) const { return !str.empty(); } bool operator() (const MapAdapter& val) const { return val.GetSize() != 0ULL; } bool operator() (const ListAdapter& val) const { return val.GetSize() != 0ULL; } bool operator() (const EmptyValue&) const { return false; } }; template<typename TargetType> struct NumberEvaluator { NumberEvaluator(TargetType def = 0) : m_def(def) {} TargetType operator ()(int64_t val) const { return static_cast<TargetType>(val); } TargetType operator ()(double val) const { return static_cast<TargetType>(val); } TargetType operator ()(bool val) const { return static_cast<TargetType>(val); } template<typename U> TargetType operator()(U&&) const { return m_def; } TargetType m_def; }; using IntegerEvaluator = NumberEvaluator<int64_t>; using DoubleEvaluator = NumberEvaluator<double>; struct StringJoiner : BaseVisitor<TargetString> { using BaseVisitor::operator (); template<typename CharT> TargetString operator() (EmptyValue, const std::basic_string<CharT>& str) const { return str; } template<typename CharT> TargetString operator() (EmptyValue, const std::basic_string_view<CharT>& str) const { return std::basic_string<CharT>(str.begin(), str.end()); } template<typename CharT> TargetString operator() (const std::basic_string<CharT>& left, const std::basic_string<CharT>& right) const { return left + right; } template<typename CharT1, typename CharT2> std::enable_if_t<!std::is_same<CharT1, CharT2>::value, TargetString> operator() (const std::basic_string<CharT1>& left, const std::basic_string<CharT2>& right) const { return left + ConvertString<std::basic_string<CharT1>>(right); } template<typename CharT> TargetString operator() (std::basic_string<CharT> left, const std::basic_string_view<CharT>& right) const { left.append(right.begin(), right.end()); return std::move(left); } template<typename CharT1, typename CharT2> std::enable_if_t<!std::is_same<CharT1, CharT2>::value, TargetString> operator() (std::basic_string<CharT1> left, const std::basic_string_view<CharT2>& right) const { auto r = ConvertString<std::basic_string<CharT1>>(right); left.append(r.begin(), r.end()); return std::move(left); } }; template<typename Fn> struct StringConverterImpl : public BaseVisitor<decltype(std::declval<Fn>()(std::declval<std::string_view>()))> { using R = decltype(std::declval<Fn>()(std::string_view())); using BaseVisitor<R>::operator (); StringConverterImpl(const Fn& fn) : m_fn(fn) {} template<typename CharT> R operator()(const std::basic_string<CharT>& str) const { return m_fn(std::basic_string_view<CharT>(str)); } template<typename CharT> R operator()(const std::basic_string_view<CharT>& str) const { return m_fn(str); } const Fn& m_fn; }; template<typename CharT> struct SameStringGetter : public visitors::BaseVisitor<nonstd::expected<void, std::basic_string<CharT>>> { using ResultString = std::basic_string<CharT>; using OtherString = std::conditional_t<std::is_same<CharT, char>::value, std::wstring, std::string>; using ResultStringView = std::basic_string_view<CharT>; using OtherStringView = std::conditional_t<std::is_same<CharT, char>::value, std::wstring_view, std::string_view>; using Result = nonstd::expected<void, ResultString>; using BaseVisitor<Result>::operator (); Result operator()(const ResultString& str) const { return nonstd::make_unexpected(str); } Result operator()(const ResultStringView& str) const { return nonstd::make_unexpected(ResultString(str.begin(), str.end())); } Result operator()(const OtherString& str) const { return nonstd::make_unexpected(ConvertString<ResultString>(str)); } Result operator()(const OtherStringView& str) const { return nonstd::make_unexpected(ConvertString<ResultString>(str)); } }; } // namespace visitors inline bool ConvertToBool(const InternalValue& val) { return Apply<visitors::BooleanEvaluator>(val); } inline int64_t ConvertToInt(const InternalValue& val, int64_t def = 0) { return Apply<visitors::IntegerEvaluator>(val, def); } inline double ConvertToDouble(const InternalValue& val, double def = 0) { return Apply<visitors::DoubleEvaluator>(val, def); } template<template<typename> class Cvt = visitors::StringConverterImpl, typename Fn> auto ApplyStringConverter(const InternalValue& str, Fn&& fn) { return Apply<Cvt<Fn>>(str, std::forward<Fn>(fn)); } template<typename CharT> auto GetAsSameString(const std::basic_string<CharT>&, const InternalValue& val) { using Result = std::optional<std::basic_string<CharT>>; auto result = Apply<visitors::SameStringGetter<CharT>>(val); if (!result) return Result(result.error()); return Result(); } template<typename CharT> auto GetAsSameString(const std::basic_string_view<CharT>&, const InternalValue& val) { using Result = std::optional<std::basic_string<CharT>>; auto result = Apply<visitors::SameStringGetter<CharT>>(val); if (!result) return Result(result.error()); return Result(); } inline bool operator==(const InternalValueData& lhs, const InternalValueData& rhs) { InternalValue cmpRes; cmpRes = Apply2<visitors::BinaryMathOperation>(lhs, rhs, BinaryExpression::LogicalEq); return ConvertToBool(cmpRes); } inline bool operator!=(const InternalValueData& lhs, const InternalValueData& rhs) { return !(lhs == rhs); } } // namespace jinja2 #endif // JINJA2CPP_SRC_VALUE_VISITORS_H