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

STEllAR-GROUP / hpx / #862

10 Jan 2023 05:30PM UTC coverage: 86.582% (-0.05%) from 86.634%
#862

push

StellarBot
Merge #6130

6130: Remove the mutex lock in the critical path of get_partitioner. r=hkaiser a=JiakunYan

Remove the mutex lock in the critical path of hpx::resource::detail::get_partitioner.

The protected variable `partitioner_ref` is only set once during initialization.

Co-authored-by: Jiakun Yan <jiakunyan1998@gmail.com>

6 of 6 new or added lines in 1 file covered. (100.0%)

174767 of 201851 relevant lines covered (86.58%)

2069816.07 hits per line

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

63.46
/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,027✔
49
        hpx::any_nonser& value_store,
50
        std::vector<std::string> const& new_tokens, bool utf8) const
51
    {
52
        if (utf8)
14,027✔
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.emplace_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,019✔
67
        }
68
    }
14,021✔
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.emplace_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.emplace_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,253✔
96

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

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

110
    unsigned untyped_value::max_tokens() const noexcept
221✔
111
    {
112
        return min_tokens();
221✔
113
    }
114

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

125
    typed_value<bool>* bool_switch()
14✔
126
    {
127
        return bool_switch(nullptr);
14✔
128
    }
129

130
    typed_value<bool>* bool_switch(bool* v)
16✔
131
    {
132
        typed_value<bool>* r = new typed_value<bool>(v);
16✔
133
        r->default_value(false);
16✔
134
        r->zero_tokens();
16✔
135

136
        return r;
16✔
137
    }
×
138

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

151
        for (char& i : s)
31✔
152
            i = char(std::tolower(i));
21✔
153

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

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

173
        for (wchar_t& i : s)
×
174
            i = wchar_t(tolower(i));
×
175

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

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

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

200
    namespace validators {
201

202
        HPX_CORE_EXPORT
203
        void check_first_occurrence(hpx::any_nonser const& value)
14,001✔
204
        {
205
            if (value.has_value())
14,001✔
206
                throw multiple_occurrences();
4✔
207
        }
13,997✔
208
    }    // namespace validators
209

210
    invalid_option_value::invalid_option_value(std::string const& bad_value)
1✔
211
      : validation_error(validation_error::invalid_option_value)
1✔
212
    {
2✔
213
        set_substitute("value", bad_value);
1✔
214
    }
1✔
215

216
    invalid_option_value::invalid_option_value(std::wstring const& bad_value)
×
217
      : validation_error(validation_error::invalid_option_value)
×
218
    {
×
219
        set_substitute("value", convert_value(bad_value));
×
220
    }
×
221

222
    invalid_bool_value::invalid_bool_value(std::string const& bad_value)
×
223
      : validation_error(validation_error::invalid_bool_value)
×
224
    {
×
225
        set_substitute("value", bad_value);
×
226
    }
×
227

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

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

250
        return m_message.c_str();
11✔
251
    }
252

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

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

287
    std::string error_with_option_name::get_canonical_option_name() const
20✔
288
    {
289
        auto option_it = m_substitutions.find("option");
20✔
290
        auto original_it = m_substitutions.find("original_token");
20✔
291
        if (option_it != m_substitutions.end() && !option_it->second.length())
20✔
292
        {
293
            return original_it != m_substitutions.end() ? original_it->second :
11✔
294
                                                          std::string();
×
295
        }
296

297
        std::string original_token;
9✔
298
        if (original_it != m_substitutions.end())
9✔
299
            original_token = strip_prefixes(original_it->second);
9✔
300

301
        std::string option_name;
9✔
302
        if (option_it != m_substitutions.end())
9✔
303
            option_name = strip_prefixes(option_it->second);
9✔
304

305
        //  For long options, use option name
306
        if (m_option_style == command_line_style::allow_long ||
9✔
307
            m_option_style == command_line_style::allow_long_disguise)
×
308
            return get_canonical_option_prefix() + option_name;
9✔
309

310
        //  For short options use first letter of original_token
311
        if (m_option_style && original_token.length())
×
312
            return get_canonical_option_prefix() + original_token[0];
×
313

314
        // no prefix
315
        return option_name;
×
316
    }
20✔
317

318
    void error_with_option_name::substitute_placeholders(
11✔
319
        std::string const& error_template) const
320
    {
321
        m_message = error_template;
11✔
322
        std::map<std::string, std::string> substitutions(m_substitutions);
11✔
323
        substitutions["canonical_option"] = get_canonical_option_name();
11✔
324
        substitutions["prefix"] = get_canonical_option_prefix();
11✔
325

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

340
        //
341
        //  replace placeholder with values
342
        //  placeholder are denoted by surrounding '%'
343
        //
344
        for (auto& substitution : substitutions)
55✔
345
            replace_token('%' + substitution.first + '%', substitution.second);
44✔
346
    }
11✔
347

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

362
        std::string error_template = original_error_template;
×
363
        // remove duplicates using std::set
364
        std::set<std::string> alternatives_set(
×
365
            m_alternatives.begin(), m_alternatives.end());
×
366
        std::vector<std::string> alternatives_vec(
×
367
            alternatives_set.begin(), alternatives_set.end());
×
368

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

378
        // there is a programming error if multiple options have the same name...
379
        if (m_alternatives.size() > 1 && alternatives_vec.size() == 1)
×
380
            error_template += "different versions of ";
×
381

382
        error_template += "'%prefix%" + alternatives_vec.back() + "'";
×
383

384
        // use inherited logic
385
        error_with_option_name::substitute_placeholders(error_template);
×
386
    }
×
387

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