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

STEllAR-GROUP / hpx / #856

28 Dec 2022 02:00AM UTC coverage: 86.602% (+0.05%) from 86.55%
#856

push

StellarBot
Merge #6119

6119: Update CMakeLists.txt r=hkaiser a=khuck

updating the default APEX version


Co-authored-by: Kevin Huck <khuck@cs.uoregon.edu>

174566 of 201573 relevant lines covered (86.6%)

1876093.78 hits per line

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

63.24
/libs/core/program_options/src/value_semantic.cpp
1
//  Copyright Vladimir Prus 2004.
2
//  SPDX-License-Identifier: BSL-1.0
3
//  Distributed under the Boost Software License, Version 1.0.
4
//  (See accompanying file LICENSE_1_0.txt
5
//  or copy at http://www.boost.org/LICENSE_1_0.txt)
6

7
#include <hpx/program_options/config.hpp>
8
#include <hpx/datastructures/any.hpp>
9
#include <hpx/program_options/detail/cmdline.hpp>
10
#include <hpx/program_options/detail/convert.hpp>
11
#include <hpx/program_options/value_semantic.hpp>
12

13
#include <cctype>
14
#include <cstddef>
15
#include <map>
16
#include <set>
17
#include <string>
18
#include <vector>
19

20
namespace hpx::program_options {
21

22
    inline std::string strip_prefixes(std::string const& text)
18✔
23
    {
24
        // "--foo-bar" -> "foo-bar"
25
        std::string::size_type i = text.find_first_not_of("-/");
18✔
26
        if (i == std::string::npos)
18✔
27
        {
28
            return text;
×
29
        }
30
        return text.substr(i);
18✔
31
    }
18✔
32

33
    namespace {
34

35
        std::string convert_value(std::wstring const& s)
×
36
        {
37
            try
38
            {
39
                return to_local_8_bit(s);
×
40
            }
×
41
            catch (std::exception const&)
42
            {
43
                return "<unrepresentable unicode string>";
×
44
            }
×
45
        }
×
46
    }    // namespace
47

48
    void value_semantic_codecvt_helper<char>::parse(
14,013✔
49
        hpx::any_nonser& value_store,
50
        std::vector<std::string> const& new_tokens, bool utf8) const
51
    {
52
        if (utf8)
14,013✔
53
        {
54
            // Need to convert to local encoding.
55
            std::vector<std::string> local_tokens;
2✔
56
            for (auto const& new_token : new_tokens)
4✔
57
            {
58
                std::wstring w = from_utf8(new_token);
2✔
59
                local_tokens.push_back(to_local_8_bit(w));
2✔
60
            }
2✔
61
            xparse(value_store, local_tokens);
2✔
62
        }
2✔
63
        else
64
        {
65
            // Already in local encoding, pass unmodified
66
            xparse(value_store, new_tokens);
14,005✔
67
        }
68
    }
14,007✔
69

70
    void value_semantic_codecvt_helper<wchar_t>::parse(
2✔
71
        hpx::any_nonser& value_store,
72
        std::vector<std::string> const& new_tokens, bool utf8) const
73
    {
74
        std::vector<std::wstring> tokens;
2✔
75
        if (utf8)
2✔
76
        {
77
            // Convert from utf8
78
            for (auto const& new_token : new_tokens)
2✔
79
            {
80
                tokens.push_back(from_utf8(new_token));
1✔
81
            }
82
        }
1✔
83
        else
84
        {
85
            // Convert from local encoding
86
            for (auto const& new_token : new_tokens)
2✔
87
            {
88
                tokens.push_back(from_local_8_bit(new_token));
1✔
89
            }
90
        }
91

92
        xparse(value_store, tokens);
2✔
93
    }
2✔
94

95
    std::string arg("arg");
1,251✔
96

97
    std::string untyped_value::name() const
14✔
98
    {
99
        return arg;
14✔
100
    }
101

102
    unsigned untyped_value::min_tokens() const noexcept
119✔
103
    {
104
        if (m_zero_tokens)
119✔
105
            return 0;
87✔
106
        else
107
            return 1;
32✔
108
    }
119✔
109

110
    unsigned untyped_value::max_tokens() const noexcept
221✔
111
    {
112
        if (m_zero_tokens)
221✔
113
            return 0;
171✔
114
        else
115
            return 1;
50✔
116
    }
221✔
117

118
    void untyped_value::xparse(hpx::any_nonser& value_store,
27✔
119
        std::vector<std::string> const& new_tokens) const
120
    {
121
        if (value_store.has_value())
27✔
122
            throw multiple_occurrences();
×
123
        if (new_tokens.size() > 1)
27✔
124
            throw multiple_values();
×
125
        value_store = new_tokens.empty() ? std::string("") : new_tokens.front();
27✔
126
    }
27✔
127

128
    typed_value<bool>* bool_switch()
14✔
129
    {
130
        return bool_switch(nullptr);
14✔
131
    }
132

133
    typed_value<bool>* bool_switch(bool* v)
16✔
134
    {
135
        typed_value<bool>* r = new typed_value<bool>(v);
16✔
136
        r->default_value(false);
16✔
137
        r->zero_tokens();
16✔
138

139
        return r;
16✔
140
    }
×
141

142
    /* Validates bool value.
143
        hpx::any_nonser of "1", "true", "yes", "on" will be converted to "1".
144
        hpx::any_nonser of "0", "false", "no", "off" will be converted to "0".
145
        Case is ignored. The 'xs' vector can either be empty, in which
146
        case the value is 'true', or can contain explicit value.
147
    */
148
    void validate(
10✔
149
        hpx::any_nonser& v, std::vector<std::string> const& xs, bool*, int)
150
    {
151
        check_first_occurrence(v);
10✔
152
        std::string s(get_single_string(xs, true));
10✔
153

154
        for (char& i : s)
31✔
155
            i = char(std::tolower(i));
21✔
156

157
        if (s.empty() || s == "on" || s == "yes" || s == "1" || s == "true")
10✔
158
            v = hpx::any_nonser(true);
6✔
159
        else if (s == "off" || s == "no" || s == "0" || s == "false")
4✔
160
            v = hpx::any_nonser(false);
4✔
161
        else
162
            throw invalid_bool_value(s);
×
163
    }
10✔
164

165
    // This is blatant copy-paste. However, templating this will cause a problem,
166
    // since wstring can't be constructed/compared with char*. We'd need to
167
    // create auxiliary 'widen' routine to convert from char* into
168
    // needed string type, and that's more work.
169
    HPX_CORE_EXPORT
170
    void validate(
×
171
        hpx::any_nonser& v, std::vector<std::wstring> const& xs, bool*, int)
172
    {
173
        check_first_occurrence(v);
×
174
        std::wstring s(get_single_string(xs, true));
×
175

176
        for (wchar_t& i : s)
×
177
            i = wchar_t(tolower(i));
×
178

179
        if (s.empty() || s == L"on" || s == L"yes" || s == L"1" || s == L"true")
×
180
            v = hpx::any_nonser(true);
×
181
        else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
×
182
            v = hpx::any_nonser(false);
×
183
        else
184
            throw invalid_bool_value(convert_value(s));
×
185
    }
×
186

187
    HPX_CORE_EXPORT
188
    void validate(hpx::any_nonser& v, std::vector<std::string> const& xs,
12,240✔
189
        std::string*, int)
190
    {
191
        check_first_occurrence(v);
12,240✔
192
        v = hpx::any_nonser(get_single_string(xs));
12,240✔
193
    }
12,240✔
194

195
    HPX_CORE_EXPORT
196
    void validate(hpx::any_nonser& v, std::vector<std::wstring> const& xs,
×
197
        std::string*, int)
198
    {
199
        check_first_occurrence(v);
×
200
        v = hpx::any_nonser(get_single_string(xs));
×
201
    }
×
202

203
    namespace validators {
204

205
        HPX_CORE_EXPORT
206
        void check_first_occurrence(hpx::any_nonser const& value)
13,987✔
207
        {
208
            if (value.has_value())
13,987✔
209
                throw multiple_occurrences();
4✔
210
        }
13,983✔
211
    }    // namespace validators
212

213
    invalid_option_value::invalid_option_value(std::string const& bad_value)
1✔
214
      : validation_error(validation_error::invalid_option_value)
1✔
215
    {
2✔
216
        set_substitute("value", bad_value);
1✔
217
    }
1✔
218

219
    invalid_option_value::invalid_option_value(std::wstring const& bad_value)
×
220
      : validation_error(validation_error::invalid_option_value)
×
221
    {
×
222
        set_substitute("value", convert_value(bad_value));
×
223
    }
×
224

225
    invalid_bool_value::invalid_bool_value(std::string const& bad_value)
×
226
      : validation_error(validation_error::invalid_bool_value)
×
227
    {
×
228
        set_substitute("value", bad_value);
×
229
    }
×
230

231
    error_with_option_name::error_with_option_name(std::string const& template_,
37✔
232
        std::string const& option_name, std::string const& original_token,
233
        int option_style)
234
      : error(template_)
37✔
235
      , m_option_style(option_style)
37✔
236
      , m_error_template(template_)
37✔
237
    {
74✔
238
        //     parameter            |     placeholder               |   value
239
        //     ---------            |     -----------               |   -----
240
        set_substitute_default(
37✔
241
            "canonical_option", "option '%canonical_option%'", "option");
37✔
242
        set_substitute_default("value", "argument ('%value%')", "argument");
37✔
243
        set_substitute_default("prefix", "%prefix%", "");
37✔
244
        m_substitutions["option"] = option_name;
37✔
245
        m_substitutions["original_token"] = original_token;
37✔
246
    }
37✔
247

248
    char const* error_with_option_name::what() const noexcept
11✔
249
    {
250
        // will substitute tokens each time what is run()
251
        substitute_placeholders(m_error_template);
11✔
252

253
        return m_message.c_str();
11✔
254
    }
255

256
    void error_with_option_name::replace_token(
56✔
257
        std::string const& from, std::string const& to) const
258
    {
259
        for (;;)
67✔
260
        {
261
            std::size_t pos = m_message.find(from);
67✔
262
            // not found: all replaced
263
            if (pos == std::string::npos)
67✔
264
                return;
56✔
265
            m_message.replace(pos, from.length(), to);
11✔
266
        }
267
    }
268

269
    std::string error_with_option_name::get_canonical_option_prefix() const
20✔
270
    {
271
        switch (m_option_style)
20✔
272
        {
273
        case command_line_style::allow_dash_for_short:
274
            return "-";
×
275
        case command_line_style::allow_slash_for_short:
276
            return "/";
×
277
        case command_line_style::allow_long_disguise:
278
            return "-";
×
279
        case command_line_style::allow_long:
280
            return "--";
19✔
281
        case 0:
282
            return "";
1✔
283
        }
284
        throw std::logic_error(
×
285
            "error_with_option_name::m_option_style can only be "
286
            "one of [0, allow_dash_for_short, allow_slash_for_short, "
287
            "allow_long_disguise or allow_long]");
288
    }
20✔
289

290
    std::string error_with_option_name::get_canonical_option_name() const
20✔
291
    {
292
        if (!m_substitutions.find("option")->second.length())
20✔
293
            return m_substitutions.find("original_token")->second;
11✔
294

295
        std::string original_token =
296
            strip_prefixes(m_substitutions.find("original_token")->second);
9✔
297
        std::string option_name =
298
            strip_prefixes(m_substitutions.find("option")->second);
9✔
299

300
        //  For long options, use option name
301
        if (m_option_style == command_line_style::allow_long ||
9✔
302
            m_option_style == command_line_style::allow_long_disguise)
×
303
            return get_canonical_option_prefix() + option_name;
9✔
304

305
        //  For short options use first letter of original_token
306
        if (m_option_style && original_token.length())
×
307
            return get_canonical_option_prefix() + original_token[0];
×
308

309
        // no prefix
310
        return option_name;
×
311
    }
20✔
312

313
    void error_with_option_name::substitute_placeholders(
11✔
314
        std::string const& error_template) const
315
    {
316
        m_message = error_template;
11✔
317
        std::map<std::string, std::string> substitutions(m_substitutions);
11✔
318
        substitutions["canonical_option"] = get_canonical_option_name();
11✔
319
        substitutions["prefix"] = get_canonical_option_prefix();
11✔
320

321
        //
322
        //  replace placeholder with defaults if values are missing
323
        //
324
        for (auto const& substitution_default : m_substitution_defaults)
44✔
325
        {
326
            // missing parameter: use default
327
            if (substitutions.count(substitution_default.first) == 0 ||
55✔
328
                substitutions[substitution_default.first].length() == 0)
22✔
329
            {
330
                replace_token(substitution_default.second.first,
24✔
331
                    substitution_default.second.second);
12✔
332
            }
12✔
333
        }
334

335
        //
336
        //  replace placeholder with values
337
        //  placeholder are denoted by surrounding '%'
338
        //
339
        for (auto& substitution : substitutions)
55✔
340
            replace_token('%' + substitution.first + '%', substitution.second);
44✔
341
    }
11✔
342

343
    void ambiguous_option::substitute_placeholders(
×
344
        std::string const& original_error_template) const
345
    {
346
        // For short forms, all alternatives must be identical, by
347
        //      definition, to the specified option, so we don't need to
348
        //      display alternatives
349
        if (m_option_style == command_line_style::allow_dash_for_short ||
×
350
            m_option_style == command_line_style::allow_slash_for_short)
×
351
        {
352
            error_with_option_name::substitute_placeholders(
×
353
                original_error_template);
×
354
            return;
×
355
        }
356

357
        std::string error_template = original_error_template;
×
358
        // remove duplicates using std::set
359
        std::set<std::string> alternatives_set(
×
360
            m_alternatives.begin(), m_alternatives.end());
×
361
        std::vector<std::string> alternatives_vec(
×
362
            alternatives_set.begin(), alternatives_set.end());
×
363

364
        error_template += " and matches ";
×
365
        // Being very cautious: should be > 1 alternative!
366
        if (alternatives_vec.size() > 1)
×
367
        {
368
            for (std::size_t i = 0; i < alternatives_vec.size() - 1; ++i)
×
369
                error_template += "'%prefix%" + alternatives_vec[i] + "', ";
×
370
            error_template += "and ";
×
371
        }
×
372

373
        // there is a programming error if multiple options have the same name...
374
        if (m_alternatives.size() > 1 && alternatives_vec.size() == 1)
×
375
            error_template += "different versions of ";
×
376

377
        error_template += "'%prefix%" + alternatives_vec.back() + "'";
×
378

379
        // use inherited logic
380
        error_with_option_name::substitute_placeholders(error_template);
×
381
    }
×
382

383
    std::string validation_error::get_template(kind_t kind)
2✔
384
    {
385
        // Initially, store the message in 'const char*' variable,
386
        // to avoid conversion to std::string in all cases.
387
        char const* msg;
388
        switch (kind)
2✔
389
        {
390
        case invalid_bool_value:
391
            msg = "the argument ('%value%') for option '%canonical_option%' is "
×
392
                  "invalid. Valid choices are 'on|off', 'yes|no', '1|0' and "
393
                  "'true|false'";
394
            break;
×
395
        case invalid_option_value:
396
            msg = "the argument ('%value%') for option '%canonical_option%' is "
1✔
397
                  "invalid";
398
            break;
1✔
399
        case multiple_values_not_allowed:
400
            msg = "option '%canonical_option%' only takes a single argument";
1✔
401
            break;
1✔
402
        case at_least_one_value_required:
403
            msg = "option '%canonical_option%' requires at least one argument";
×
404
            break;
×
405
        // currently unused
406
        case invalid_option:
407
            msg = "option '%canonical_option%' is not valid";
×
408
            break;
×
409
        default:
410
            msg = "unknown error";
×
411
        }
×
412
        return msg;
2✔
413
    }
×
414
}    // namespace hpx::program_options
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