Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: Virtual GDAL Datasets 4 : * Purpose: Implementation of MathExpression 5 : * Author: Daniel Baston 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2024, ISciences LLC 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #pragma once 14 : 15 : #include "cpl_error.h" 16 : 17 : #include <string_view> 18 : #include <vector> 19 : 20 : namespace gdal 21 : { 22 : 23 : /** 24 : * Class to support evaluation of a mathematical expression 25 : */ 26 : class MathExpression 27 : { 28 : public: 29 19 : virtual ~MathExpression() = default; 30 : 31 : /** 32 : * Create a MathExpression using a specified dialect. 33 : * @param pszExpression The body of the expression, e.g. "X + 3" 34 : * @param pszDialect The expression dialect, e.g. "muparser" 35 : * @return a MathExpression using the specified dialect, or nullptr on error. 36 : */ 37 : static std::unique_ptr<MathExpression> Create(const char *pszExpression, 38 : const char *pszDialect); 39 : 40 : /** 41 : * Register a variable to be used in the expression. 42 : * 43 : * The value of the variable may be changed during repeated evaluations of 44 : * the expression, but its location in memory may not. 45 : * 46 : * @param osVariable The name of the variable 47 : * @param pdfLocation The location of the variable's value 48 : * 49 : * @since 3.11 50 : */ 51 : virtual void RegisterVariable(std::string_view osVariable, 52 : double *pdfLocation) = 0; 53 : 54 : /** 55 : * Register a vector to be used in the expression. 56 : * 57 : * The values and size of the vector may be changed during repeated evaluations 58 : * of the expression, but its location in memory may not. 59 : * 60 : * @param osVariable The name of the vector 61 : * @param padfLocation The location of the vector 62 : * 63 : * @since 3.11 64 : */ 65 : virtual void RegisterVector(std::string_view osVariable, 66 : std::vector<double> *padfLocation) = 0; 67 : 68 : /** 69 : * Compile the expression. 70 : * 71 : * If not called explicitly, the expression will be compiled the first time 72 : * the expression is evaluated. 73 : * 74 : * @return CE_None if the expression can be successfully parsed and all 75 : * symbols have been registe vrtexpression_exprtk.cpp 76 : vrtexpression_muparser.cppred, CE_Failure otherwise. 77 : * 78 : * @since 3.11 79 : */ 80 : virtual CPLErr Compile() = 0; 81 : 82 : /** 83 : * Evaluate the expression. 84 : * 85 : * @return CE_None if the expression was successfully evaluated, CE_Failure otherwise. 86 : * 87 : * @since 3.11 88 : */ 89 : virtual CPLErr Evaluate() = 0; 90 : 91 : /** 92 : * Access the results from the last time the expression was evaluated. 93 : * 94 : * The returned vector may be reused on subsequent evaluations of the expression. 95 : * 96 : * @return a reference to the vector in which results are stored. 97 : * 98 : * @since 3.11 99 : */ 100 : virtual const std::vector<double> &Results() const = 0; 101 : }; 102 : 103 : /*! @cond Doxygen_Suppress */ 104 : 105 : #if GDAL_VRT_ENABLE_EXPRTK 106 : 107 : /** 108 : * Class to support evaluation of an expression using the exprtk library. 109 : */ 110 : class ExprtkExpression : public MathExpression 111 : { 112 : public: 113 : explicit ExprtkExpression(std::string_view osExpression); 114 : 115 : virtual ~ExprtkExpression(); 116 : 117 : void RegisterVariable(std::string_view osVariable, 118 : double *pdfLocation) override; 119 : 120 : void RegisterVector(std::string_view osVariable, 121 : std::vector<double> *padfLocation) override; 122 : 123 : CPLErr Compile() override; 124 : 125 : CPLErr Evaluate() override; 126 : 127 : const std::vector<double> &Results() const override; 128 : 129 : private: 130 : class Impl; 131 : 132 : std::unique_ptr<Impl> m_pImpl; 133 : }; 134 : 135 : #endif 136 : 137 : #if GDAL_VRT_ENABLE_MUPARSER 138 : 139 : /** 140 : * Class to support evaluation of an expression using the muparser library. 141 : */ 142 : class MuParserExpression : public MathExpression 143 : { 144 : public: 145 : explicit MuParserExpression(std::string_view osExpression); 146 : 147 : virtual ~MuParserExpression(); 148 : 149 : void RegisterVariable(std::string_view osVariable, 150 : double *pdfLocation) override; 151 : 152 : void RegisterVector(std::string_view osVariable, 153 : std::vector<double> *padfLocation) override; 154 : 155 : CPLErr Compile() override; 156 : 157 : CPLErr Evaluate() override; 158 : 159 : const std::vector<double> &Results() const override; 160 : 161 : private: 162 : class Impl; 163 : 164 : std::unique_ptr<Impl> m_pImpl; 165 : }; 166 : 167 : #endif 168 : 169 : inline std::unique_ptr<MathExpression> 170 19 : MathExpression::Create([[maybe_unused]] const char *pszExpression, 171 : const char *pszDialect) 172 : { 173 19 : if (EQUAL(pszDialect, "exprtk")) 174 : { 175 : #if GDAL_VRT_ENABLE_EXPRTK 176 : return std::make_unique<gdal::ExprtkExpression>(pszExpression); 177 : #else 178 0 : CPLError(CE_Failure, CPLE_IllegalArg, 179 : "Dialect '%s' is not supported by this GDAL build. A GDAL " 180 : "build with ExprTk is be needed.", 181 : pszDialect); 182 : #endif 183 : } 184 19 : else if (EQUAL(pszDialect, "muparser")) 185 : { 186 : #if GDAL_VRT_ENABLE_MUPARSER 187 19 : return std::make_unique<gdal::MuParserExpression>(pszExpression); 188 : #else 189 : CPLError(CE_Failure, CPLE_IllegalArg, 190 : "Dialect '%s' is not supported by this GDAL build. A GDAL " 191 : "build with muparser is be needed.", 192 : pszDialect); 193 : #endif 194 : } 195 : else 196 : { 197 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Unknown expression dialect: %s", 198 : pszDialect); 199 : } 200 0 : return nullptr; 201 : } 202 : 203 : /*! @endcond */ 204 : 205 : } // namespace gdal