• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.58
/frmts/vrt/vrtexpression_muparser.cpp
1
/******************************************************************************
2
 *
3
 * Project:  Virtual GDAL Datasets
4
 * Purpose:  Implementation of GDALExpressionEvaluator.
5
 * Author:   Daniel Baston
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2024, ISciences LLC
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12

13
#include "vrtexpression.h"
14
#include "cpl_string.h"
15

16
#include <cmath>
17
#include <limits>
18
#include <map>
19
#include <optional>
20
#include "muparser_header.h"
21

22
namespace gdal
23
{
24

25
/*! @cond Doxygen_Suppress */
26

27
static mu::value_type isnan(mu::value_type x)
1✔
28
{
29
    return std::isnan(x);
1✔
30
}
31

32
static mu::value_type isnodata(void *userdata, mu::value_type x)
×
33
{
34
    double noData = *static_cast<double *>(userdata);
×
35
    return x == noData || (std::isnan(x) && std::isnan(noData));
×
36
}
37

38
static mu::value_type always_false(mu::value_type)
×
39
{
40
    return 0;
×
41
}
42

43
// Only newer versions of muparser have the DefineFunUserData method that we
44
// need to register the isnodata() function above. Since it's not clear what
45
// version this was introduced or how to check the version, we test for the
46
// method directly.
47
namespace
48
{
49

50
template <typename, typename = void>
51
struct HasDefineFunUserData : std::false_type
52
{
53
};
54

55
template <typename Parser>
56
struct HasDefineFunUserData<
57
    Parser, std::void_t<decltype(std::declval<Parser>().DefineFunUserData(
58
                _T("x"), isnodata, nullptr))>> : std::true_type
59
{
60
};
61

62
template <typename T> void DefineIsNoDataFunction(T &parser)
63
{
64
    const auto &varmap = parser.GetVar();
65
    if (auto it = varmap.find("NODATA"); it != varmap.end())
66
    {
67
        parser.DefineFunUserData(_T("isnodata"), isnodata, it->second);
68
    }
69
    else
70
    {
71
        // muparser doesn't allow userData to be null, so we bind isnodata
72
        // to a dummy function instead
73
        parser.DefineFun(_T("isnodata"), always_false);
74
    }
75
}
76

77
}  // namespace
78

79
static std::optional<std::string> Sanitize(const std::string &osVariable)
659✔
80
{
81
    // muparser does not allow characters '[' or ']' which we use to emulate
82
    // vectors. Replace these with a combination of underscores
83
    auto from = osVariable.find('[');
659✔
84
    if (from != std::string::npos)
659✔
85
    {
86
        auto to = osVariable.find(']');
237✔
87
        if (to != std::string::npos)
237✔
88
        {
89
            auto sanitized = std::string("__") + osVariable.substr(0, from) +
474✔
90
                             +"__" +
474✔
91
                             osVariable.substr(from + 1, to - from - 1) + "__";
711✔
92
            return sanitized;
237✔
93
        }
94
    }
95

96
    return std::nullopt;
422✔
97
}
98

99
static void ReplaceVariable(std::string &expression,
220✔
100
                            const std::string &variable,
101
                            const std::string &sanitized)
102
{
103
    std::string::size_type seekPos = 0;
220✔
104
    auto pos = expression.find(variable, seekPos);
220✔
105
    while (pos != std::string::npos)
499✔
106
    {
107
        auto end = pos + variable.size();
279✔
108

109
        if (pos == 0 ||
483✔
110
            (!std::isalnum(expression[pos - 1]) && expression[pos - 1] != '_'))
204✔
111
        {
112
            expression =
113
                expression.substr(0, pos) + sanitized + expression.substr(end);
236✔
114
        }
115

116
        seekPos = end;
279✔
117
        pos = expression.find(variable, seekPos);
279✔
118
    }
119
}
220✔
120

121
class MuParserExpression::Impl
122
{
123
  public:
124
    explicit Impl(std::string_view osExpression)
347✔
125
        : m_osExpression(std::string(osExpression))
347✔
126
    {
127
    }
347✔
128

129
    void Register(std::string_view osVariable, double *pdfValue)
630✔
130
    {
131
        try
132
        {
133
            m_oParser.DefineVar(std::string(osVariable), pdfValue);
632✔
134
        }
135
        catch (const mu::Parser::exception_type &)
1✔
136
        {
137
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid variable name: %s",
1✔
138
                     std::string(osVariable).c_str());
2✔
139
            m_bCompileFailed = true;
1✔
140
        }
141
    }
630✔
142

143
    CPLErr Compile()
353✔
144
    {
145
        if (m_bCompileFailed)
353✔
146
        {
147
            return CE_Failure;
1✔
148
        }
149

150
        // On some platforms muparser does not seem to parse "nan" as a floating
151
        // point literal.
152
        try
153
        {
154
            m_oParser.DefineConst("nan",
352✔
155
                                  std::numeric_limits<double>::quiet_NaN());
156
            m_oParser.DefineConst("NaN",
352✔
157
                                  std::numeric_limits<double>::quiet_NaN());
158
        }
159
        catch (const mu::Parser::exception_type &)
×
160
        {
161
        }
162

163
        try
164
        {
165
            m_oParser.DefineFun(_T("isnan"), isnan);
352✔
166

167
            // Check to see if a NODATA variable has been defined and, if so,
168
            // bind it to the isnodata() function
169
            if constexpr (HasDefineFunUserData<mu::Parser>::value)
170
            {
171
                // gcc 9.4 still requires the code disabled by if constexpr to
172
                // compile, so we hide it in a templated function
173
                DefineIsNoDataFunction(m_oParser);
174
            }
175

176
            // Edit the expression ot replace variable names such as X[1] with
177
            // their sanitized versions
178
            std::string tmpExpression(m_osExpression);
704✔
179

180
            for (const auto &[osFrom, osTo] : m_oSubstitutions)
572✔
181
            {
182
                ReplaceVariable(tmpExpression, osFrom, osTo);
220✔
183
            }
184

185
            m_oParser.SetExpr(tmpExpression);
352✔
186
        }
187
        catch (const mu::Parser::exception_type &e)
×
188
        {
189
            CPLError(CE_Failure, CPLE_AppDefined, "%s", e.GetMsg().c_str());
×
190
            return CE_Failure;
×
191
        }
192
        catch (const std::exception &e)
×
193
        {
194
            CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
195
            return CE_Failure;
×
196
        }
197

198
        return CE_None;
352✔
199
    }
200

201
    CPLErr Evaluate()
12,991,500✔
202
    {
203
        if (!m_bIsCompiled)
12,991,500✔
204
        {
205
            if (auto eErr = Compile(); eErr != CE_None)
345✔
206
            {
207
                return eErr;
1✔
208
            }
209

210
            m_bIsCompiled = true;
344✔
211
        }
212

213
        try
214
        {
215
            int nResults;
216
            const double *dfResults = m_oParser.Eval(nResults);
12,991,500✔
217
            m_adfResults.resize(nResults);
12,991,500✔
218
            std::copy(dfResults, dfResults + nResults, m_adfResults.begin());
12,991,500✔
219
        }
220
        catch (const mu::Parser::exception_type &e)
1✔
221
        {
222
            CPLError(CE_Failure, CPLE_AppDefined, "%s", e.GetMsg().c_str());
1✔
223
            return CE_Failure;
1✔
224
        }
225
        catch (const std::exception &e)
×
226
        {
227
            CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
228
            return CE_Failure;
×
229
        }
230

231
        return CE_None;
12,991,500✔
232
    }
233

234
    const CPLString m_osExpression;
235
    std::map<CPLString, CPLString> m_oSubstitutions{};
236
    mu::Parser m_oParser{};
237
    std::vector<double> m_adfResults{1};
238
    bool m_bIsCompiled = false;
239
    bool m_bCompileFailed = false;
240
};
241

242
MuParserExpression::MuParserExpression(std::string_view osExpression)
347✔
243
    : m_pImpl{std::make_unique<Impl>(osExpression)}
347✔
244

245
{
246
}
347✔
247

248
MuParserExpression::~MuParserExpression()
694✔
249
{
250
}
694✔
251

252
CPLErr MuParserExpression::Compile()
8✔
253
{
254
    return m_pImpl->Compile();
8✔
255
}
256

257
void MuParserExpression::RegisterVariable(std::string_view osVariable,
630✔
258
                                          double *pdfValue)
259
{
260
    auto sanitized = Sanitize(std::string(osVariable));
1,260✔
261
    if (sanitized.has_value())
630✔
262
    {
263
        m_pImpl->m_oSubstitutions[std::string(osVariable)] = sanitized.value();
208✔
264
    }
265
    m_pImpl->Register(sanitized.value_or(std::string(osVariable)), pdfValue);
630✔
266
}
630✔
267

268
void MuParserExpression::RegisterVector(std::string_view osVariable,
7✔
269
                                        std::vector<double> *padfValues)
270
{
271
    // muparser does not support vector variables, so we simulate them
272
    // by creating a scalar variable for each element, and then replacing
273
    // the name of the vector by a list of its elements before compiling
274
    // the expression.
275
    CPLString osElementVarName;
14✔
276
    CPLString osElementsList;
14✔
277
    std::string osVectorVarName(osVariable);
7✔
278

279
    int nElementVarNameLength = static_cast<int>(
280
        4 + osVectorVarName.size() + std::log10(padfValues->size()));
7✔
281
    osElementsList.reserve(padfValues->size() *
7✔
282
                           (1 + nElementVarNameLength));  // +1 for commas
7✔
283

284
    for (std::size_t i = 0; i < padfValues->size(); i++)
36✔
285
    {
286
        osElementVarName.Printf("%s[%d]", osVectorVarName.c_str(),
287
                                static_cast<int>(i));
29✔
288
        osElementVarName = Sanitize(osElementVarName).value();
29✔
289
        RegisterVariable(osElementVarName, padfValues->data() + i);
29✔
290

291
        if (i > 0)
29✔
292
        {
293
            osElementsList += ",";
22✔
294
        }
295
        osElementsList += osElementVarName;
29✔
296
    }
297

298
    m_pImpl->m_oSubstitutions[osVectorVarName] = std::move(osElementsList);
7✔
299
}
7✔
300

301
CPLErr MuParserExpression::Evaluate()
12,991,500✔
302
{
303
    return m_pImpl->Evaluate();
12,991,500✔
304
}
305

306
const std::vector<double> &MuParserExpression::Results() const
12,991,600✔
307
{
308
    return m_pImpl->m_adfResults;
12,991,600✔
309
}
310

311
/*! @endcond Doxygen_Suppress */
312

313
}  // namespace gdal
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc