• 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

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

8
#include <hpx/program_options/config.hpp>
9
#include <hpx/assert.hpp>
10
#include <hpx/program_options/options_description.hpp>
11
// FIXME: this is only to get multiple_occurrences class
12
// should move that to a separate headers.
13
#include <hpx/program_options/parsers.hpp>
14
#include <hpx/string_util/tokenizer.hpp>
15

16
#include <climits>
17
#include <cstdarg>
18
#include <cstddef>
19
#include <cstring>
20
#include <iterator>
21
#include <memory>
22
#include <sstream>
23
#include <string>
24
#include <utility>
25
#include <vector>
26

27
namespace hpx::program_options {
28

29
    namespace {
30

31
        template <typename Char>
32
        std::basic_string<Char> tolower_(std::basic_string<Char> const& str)
110✔
33
        {
34
            std::basic_string<Char> result;
110✔
35
            for (typename std::basic_string<Char>::size_type i = 0;
350✔
36
                 i < str.size(); ++i)
350✔
37
            {
38
                result.append(1, static_cast<Char>(std::tolower(str[i])));
240✔
39
            }
240✔
40
            return result;
110✔
41
        }
110✔
42

43
    }    // unnamed namespace
44

45
    option_description::option_description() {}
×
46

47
    option_description::option_description(
95✔
48
        char const* names, value_semantic const* s)
49
      : m_value_semantic(s)
95✔
50
    {
95✔
51
        this->set_names(names);
95✔
52
    }
95✔
53

54
    option_description::option_description(
184,298✔
55
        char const* names, value_semantic const* s, char const* description)
56
      : m_description(description)
184,298✔
57
      , m_value_semantic(s)
184,298✔
58
    {
184,298✔
59
        this->set_names(names);
184,298✔
60
    }
184,298✔
61

62
    option_description::~option_description() {}
184,393✔
63

64
    option_description::match_result option_description::match(
1,889,116✔
65
        std::string const& option, bool approx, bool long_ignore_case,
66
        bool short_ignore_case) const
67
    {
68
        match_result result = no_match;
1,889,116✔
69
        std::string local_option =
70
            (long_ignore_case ? tolower_(option) : option);
1,889,116✔
71

72
        for (auto const& long_name : m_long_names)
3,738,999✔
73
        {
74
            std::string local_long_name(
75
                (long_ignore_case ? tolower_(long_name) : long_name));
1,889,021✔
76

77
            if (!local_long_name.empty())
1,889,021✔
78
            {
79
                if ((result == no_match) && (*local_long_name.rbegin() == '*'))
1,889,021✔
80
                {
81
                    // The name ends with '*'. Any specified name with the given
82
                    // prefix is OK.
83
                    if (local_option.find(local_long_name.substr(
47✔
84
                            0, local_long_name.length() - 1)) == 0)
47✔
85
                        result = approximate_match;
7✔
86
                }
47✔
87

88
                if (local_long_name == local_option)
1,889,021✔
89
                {
90
                    result = full_match;
39,138✔
91
                    break;
39,138✔
92
                }
93
                else if (approx)
1,849,883✔
94
                {
95
                    if (local_long_name.find(local_option) == 0)
1,206,137✔
96
                    {
97
                        result = approximate_match;
3,403✔
98
                    }
3,403✔
99
                }
1,206,137✔
100
            }
1,849,883✔
101
        }
1,889,021✔
102

103
        if (result != full_match)
1,889,116✔
104
        {
105
            std::string local_short_name(
106
                short_ignore_case ? tolower_(m_short_name) : m_short_name);
1,849,978✔
107

108
            if (local_short_name == local_option)
1,849,978✔
109
            {
110
                result = full_match;
163✔
111
            }
163✔
112
        }
1,849,978✔
113

114
        return result;
1,889,116✔
115
    }
1,889,116✔
116

117
    std::string const& option_description::key(std::string const& option) const
240,597✔
118
    {
119
        // We make the arbitrary choice of using the first long
120
        // name as the key, regardless of anything else
121
        if (!m_long_names.empty())
240,597✔
122
        {
123
            std::string const& first_long_name = *m_long_names.begin();
240,467✔
124
            if (first_long_name.find('*') != std::string::npos)
240,467✔
125
                // The '*' character means we're long_name
126
                // matches only part of the input. So, returning
127
                // long name will remove some of the information,
128
                // and we have to return the option as specified
129
                // in the source.
130
                return option;
9✔
131
            else
132
                return first_long_name;
240,458✔
133
        }
134
        else
135
            return m_short_name;
130✔
136
    }
240,597✔
137

138
    std::string option_description::canonical_display_name(
15✔
139
        int prefix_style) const
140
    {
141
        // We prefer the first long name over any others
142
        if (!m_long_names.empty())
15✔
143
        {
144
            if (prefix_style == command_line_style::allow_long)
15✔
145
                return "--" + *m_long_names.begin();
13✔
146
            if (prefix_style == command_line_style::allow_long_disguise)
2✔
147
                return "-" + *m_long_names.begin();
×
148
        }
2✔
149
        // sanity check: m_short_name[0] should be '-' or '/'
150
        if (m_short_name.length() == 2)
2✔
151
        {
152
            if (prefix_style == command_line_style::allow_slash_for_short)
2✔
153
                return std::string("/") + m_short_name[1];
×
154
            if (prefix_style == command_line_style::allow_dash_for_short)
2✔
155
                return std::string("-") + m_short_name[1];
×
156
        }
2✔
157
        if (!m_long_names.empty())
2✔
158
            return *m_long_names.begin();
2✔
159
        else
160
            return m_short_name;
×
161
    }
15✔
162

163
    std::string const& option_description::long_name() const
105✔
164
    {
165
        static std::string empty_string("");
105✔
166
        return m_long_names.empty() ? empty_string : *m_long_names.begin();
105✔
167
    }
×
168

169
    std::pair<std::string const*, std::size_t> option_description::long_names()
8✔
170
        const
171
    {
172
        // reinterpret_cast is to please msvc 10.
173
        return (m_long_names.empty()) ?
8✔
174
            std::pair<std::string const*, size_t>(
×
175
                reinterpret_cast<std::string const*>(0), 0) :
×
176
            std::pair<std::string const*, size_t>(
8✔
177
                &(*m_long_names.begin()), m_long_names.size());
8✔
178
    }
179

180
    option_description& option_description::set_names(char const* _names)
184,393✔
181
    {
182
        m_long_names.clear();
184,393✔
183
        std::istringstream iss(_names);
184,393✔
184
        std::string name;
184,393✔
185

186
        while (std::getline(iss, name, ','))
369,283✔
187
        {
188
            m_long_names.push_back(name);
184,890✔
189
        }
190
        HPX_ASSERT(!m_long_names.empty() && "No option names were specified");
184,393✔
191

192
        bool try_interpreting_last_name_as_a_switch = m_long_names.size() > 1;
184,393✔
193
        if (try_interpreting_last_name_as_a_switch)
184,393✔
194
        {
195
            std::string const& last_name = *m_long_names.rbegin();
489✔
196
            if (last_name.length() == 1)
489✔
197
            {
198
                m_short_name = '-' + last_name;
478✔
199
                m_long_names.pop_back();
478✔
200
                // The following caters to the (valid) input of ",c" for some
201
                // character c, where the caller only wants this option to have
202
                // a short name.
203
                if (m_long_names.size() == 1 && (*m_long_names.begin()).empty())
478✔
204
                {
205
                    m_long_names.clear();
74✔
206
                }
74✔
207
            }
478✔
208
        }
489✔
209
        // We could theoretically also ensure no remaining long names
210
        // are empty, or that none of them have length 1
211
        return *this;
212
    }
184,393✔
213

214
    std::string const& option_description::description() const
192✔
215
    {
216
        return m_description;
192✔
217
    }
218

219
    std::shared_ptr<value_semantic const> option_description::semantic() const
427,756✔
220
    {
221
        return m_value_semantic;
427,756✔
222
    }
223

224
    std::string option_description::format_name() const
273✔
225
    {
226
        if (!m_short_name.empty())
273✔
227
        {
228
            return m_long_names.empty() ? m_short_name :
4✔
229
                                          std::string(m_short_name)
2✔
230
                                              .append(" [ --")
2✔
231
                                              .append(*m_long_names.begin())
2✔
232
                                              .append(" ]");
2✔
233
        }
234
        return std::string("--").append(*m_long_names.begin());
271✔
235
    }
273✔
236

237
    std::string option_description::format_parameter() const
273✔
238
    {
239
        if (m_value_semantic->max_tokens() != 0)
273✔
240
            return m_value_semantic->name();
193✔
241
        else
242
            return "";
80✔
243
    }
273✔
244

245
    options_description_easy_init::options_description_easy_init(
34,467✔
246
        options_description* owner)
247
      : owner(owner)
34,467✔
248
    {
249
    }
34,467✔
250

251
    options_description_easy_init& options_description_easy_init::operator()(
69,015✔
252
        char const* name, char const* description)
253
    {
254
        // Create untyped semantic which accepts zero tokens: i.e.
255
        // no value can be specified on command line.
256
        // FIXME: does not look exception-safe
257
        std::shared_ptr<option_description> d =
258
            std::make_shared<option_description>(
69,015✔
259
                name, new untyped_value(true), description);
69,015✔
260

261
        owner->add(HPX_MOVE(d));
69,015✔
262
        return *this;
263
    }
69,015✔
264

265
    options_description_easy_init& options_description_easy_init::operator()(
95✔
266
        char const* name, value_semantic const* s)
267
    {
268
        std::shared_ptr<option_description> d =
269
            std::make_shared<option_description>(name, s);
95✔
270
        owner->add(HPX_MOVE(d));
95✔
271
        return *this;
272
    }
95✔
273

274
    options_description_easy_init& options_description_easy_init::operator()(
115,283✔
275
        char const* name, value_semantic const* s, char const* description)
276
    {
277
        std::shared_ptr<option_description> d =
278
            std::make_shared<option_description>(name, s, description);
115,283✔
279

280
        owner->add(HPX_MOVE(d));
115,283✔
281
        return *this;
282
    }
115,283✔
283

284
    options_description::options_description(
9,026✔
285
        unsigned line_length, unsigned min_description_length)
286
      : m_line_length(line_length)
9,026✔
287
      , m_min_description_length(min_description_length)
9,026✔
288
    {
289
        // we require a space between the option and description parts, so add 1.
290
        HPX_ASSERT(m_min_description_length < m_line_length - 1);
9,026✔
291
    }
9,026✔
292

293
    options_description::options_description(std::string const& caption,
31,569✔
294
        unsigned line_length, unsigned min_description_length)
295
      : m_caption(caption)
31,569✔
296
      , m_line_length(line_length)
31,569✔
297
      , m_min_description_length(min_description_length)
31,569✔
298
    {
299
        // we require a space between the option and description parts, so add 1.
300
        HPX_ASSERT(m_min_description_length < m_line_length - 1);
31,569✔
301
    }
31,569✔
302

303
    void options_description::add(std::shared_ptr<option_description> desc)
545,445✔
304
    {
305
        m_options.push_back(desc);
545,445✔
306
        belong_to_group.push_back(0);
545,445✔
307
    }
545,445✔
308

309
    options_description& options_description::add(
58,622✔
310
        options_description const& desc)
311
    {
312
        std::shared_ptr<options_description> d =
313
            std::make_shared<options_description>(desc);
58,622✔
314
        groups.push_back(HPX_MOVE(d));
58,622✔
315

316
        for (auto const& option : desc.m_options)
419,674✔
317
        {
318
            add(option);
361,052✔
319
            belong_to_group.back() = 1;
361,052✔
320
        }
321

322
        return *this;
323
    }
58,622✔
324

325
    options_description_easy_init options_description::add_options()
34,467✔
326
    {
327
        return options_description_easy_init(this);
34,467✔
328
    }
329

330
    option_description const& options_description::find(std::string const& name,
14,044✔
331
        bool approx, bool long_ignore_case, bool short_ignore_case) const
332
    {
333
        option_description const* d =
14,044✔
334
            find_nothrow(name, approx, long_ignore_case, short_ignore_case);
14,044✔
335
        if (!d)
14,044✔
336
            throw unknown_option();
×
337
        return *d;
14,044✔
338
    }
×
339

340
    std::vector<std::shared_ptr<option_description>> const&
341
    options_description::options() const
4,318✔
342
    {
343
        return m_options;
4,318✔
344
    }
345

346
    option_description const* options_description::find_nothrow(
40,594✔
347
        std::string const& name, bool approx, bool long_ignore_case,
348
        bool short_ignore_case) const
349
    {
350
        std::shared_ptr<option_description> found;
40,594✔
351
        bool had_full_match = false;
40,594✔
352
        std::vector<std::string> approximate_matches;
40,594✔
353
        std::vector<std::string> full_matches;
40,594✔
354

355
        // We use linear search because matching specified option
356
        // name with the declared option name need to take care about
357
        // case sensitivity and trailing '*' and so we can't use simple map.
358
        for (auto const& option : m_options)
1,929,710✔
359
        {
360
            option_description::match_result r = option->match(
3,778,232✔
361
                name, approx, long_ignore_case, short_ignore_case);
1,889,116✔
362

363
            if (r == option_description::no_match)
1,889,116✔
364
                continue;
1,846,405✔
365

366
            if (r == option_description::full_match)
42,711✔
367
            {
368
                full_matches.push_back(option->key(name));
39,301✔
369
                found = option;
39,301✔
370
                had_full_match = true;
39,301✔
371
            }
39,301✔
372
            else
373
            {
374
                // FIXME: the use of 'key' here might not
375
                // be the best approach.
376
                approximate_matches.push_back(option->key(name));
3,410✔
377
                if (!had_full_match)
3,410✔
378
                    found = option;
3,407✔
379
            }
380
        }
381
        if (full_matches.size() > 1)
40,594✔
382
            throw ambiguous_option(full_matches);
3✔
383

384
        // If we have a full match, and an approximate match,
385
        // ignore approximate match instead of reporting error.
386
        // Say, if we have options "all" and "all-chroots", then
387
        // "--all" on the command line should select the first one,
388
        // without ambiguity.
389
        if (full_matches.empty() && approximate_matches.size() > 1)
40,591✔
390
            throw ambiguous_option(approximate_matches);
4✔
391

392
        return found.get();
40,587✔
393
    }
40,594✔
394

395
    HPX_CORE_EXPORT
396
    std::ostream& operator<<(std::ostream& os, options_description const& desc)
11✔
397
    {
398
        desc.print(os);
11✔
399
        return os;
11✔
400
    }
401

402
    namespace {
403

404
        /* Given a string 'par', that contains no newline characters
405
           outputs it to 'os' with wordwrapping, that is, as several
406
           line.
407

408
           Each output line starts with 'indent' space characters,
409
           following by characters from 'par'. The total length of
410
           line is no longer than 'line_length'.
411

412
        */
413
        void format_paragraph(std::ostream& os, std::string par,
105✔
414
            std::size_t indent, std::size_t line_length)
415
        {
416
            // Through reminder of this function, 'line_length' will
417
            // be the length available for characters, not including
418
            // indent.
419
            HPX_ASSERT(indent < line_length);
105✔
420
            line_length -= indent;
105✔
421

422
            // index of tab (if present) is used as additional indent relative
423
            // to first_column_width if paragrapth is spanned over multiple
424
            // lines if tab is not on first line it is ignored
425
            std::string::size_type par_indent = par.find('\t');
105✔
426

427
            if (par_indent == std::string::npos)
105✔
428
            {
429
                par_indent = 0;
101✔
430
            }
101✔
431
            else
432
            {
433
                // only one tab per paragraph allowed
434
                if (count(par.begin(), par.end(), '\t') > 1)
4✔
435
                {
436
                    throw program_options::error(
×
437
                        "Only one tab per paragraph is allowed in the options "
×
438
                        "description");
439
                }
440

441
                // erase tab from string
442
                par.erase(par_indent, 1);
4✔
443

444
                // this HPX_ASSERT may fail due to user error or
445
                // environment conditions!
446
                HPX_ASSERT(par_indent < line_length);
4✔
447

448
                // ignore tab if not on first line
449
                if (par_indent >= line_length)
4✔
450
                {
451
                    par_indent = 0;
×
452
                }
×
453
            }
454

455
            if (par.size() < line_length)
105✔
456
            {
457
                os << par;
22✔
458
            }
22✔
459
            else
460
            {
461
                std::string::const_iterator line_begin = par.begin();
83✔
462
                std::string::const_iterator const par_end = par.end();
83✔
463

464
                bool first_line = true;    // of current paragraph!
83✔
465

466
                while (line_begin < par_end)    // paragraph lines
400✔
467
                {
468
                    if (!first_line)
317✔
469
                    {
470
                        // If line starts with space, but second character
471
                        // is not space, remove the leading space.
472
                        // We don't remove double spaces because those
473
                        // might be intentianal.
474
                        if ((*line_begin == ' ') &&
262✔
475
                            ((line_begin + 1 < par_end) &&
28✔
476
                                (*(line_begin + 1) != ' ')))
28✔
477
                        {
478
                            line_begin += 1;    // line_begin != line_end
28✔
479
                        }
28✔
480
                    }
234✔
481

482
                    // Take care to never increment the iterator past
483
                    // the end, since MSVC 8.0 (brokenly), assumes that
484
                    // doing that, even if no access happens, is a bug.
485
                    auto remaining = static_cast<std::size_t>(
317✔
486
                        std::distance(line_begin, par_end));
317✔
487
                    std::string::const_iterator line_end = line_begin +
317✔
488
                        ((remaining < line_length) ? remaining : line_length);
317✔
489

490
                    // prevent chopped words
491
                    // Is line_end between two non-space characters?
492
                    if ((*(line_end - 1) != ' ') &&
595✔
493
                        ((line_end < par_end) && (*line_end != ' ')))
278✔
494
                    {
495
                        // find last ' ' in the second half of the current paragraph line
496
                        std::string::const_iterator last_space = find(
167✔
497
                            std::reverse_iterator<std::string::const_iterator>(
167✔
498
                                line_end),
167✔
499
                            std::reverse_iterator<std::string::const_iterator>(
167✔
500
                                line_begin),
167✔
501
                            ' ')
167✔
502
                                                                     .base();
167✔
503

504
                        if (last_space != line_begin)
167✔
505
                        {
506
                            // is last_space within the second half ot the
507
                            // current line
508
                            if (static_cast<std::size_t>(std::distance(
324✔
509
                                    last_space, line_end)) < (line_length / 2))
324✔
510
                            {
511
                                line_end = last_space;
155✔
512
                            }
155✔
513
                        }
162✔
514
                    }    // prevent chopped words
167✔
515

516
                    // write line to stream
517
                    copy(line_begin, line_end, std::ostream_iterator<char>(os));
317✔
518

519
                    if (first_line)
317✔
520
                    {
521
                        indent += par_indent;    //-V101
83✔
522

523
                        // there's less to work with now
524
                        line_length -= par_indent;
83✔
525
                        first_line = false;
83✔
526
                    }
83✔
527

528
                    // more lines to follow?
529
                    if (line_end != par_end)
317✔
530
                    {
531
                        os << '\n';
234✔
532

533
                        for (std::size_t pad = indent; pad > 0; --pad)
9,439✔
534
                        {
535
                            os.put(' ');
9,205✔
536
                        }
9,205✔
537
                    }
234✔
538

539
                    // next line starts after of this line
540
                    line_begin = line_end;
317✔
541
                }    // paragraph lines
542
            }
543
        }
105✔
544

545
        void format_description(std::ostream& os, std::string const& desc,
96✔
546
            std::size_t first_column_width, std::size_t line_length)
547
        {
548
            // we need to use one char less per line to work correctly if actual
549
            // console has longer lines
550
            HPX_ASSERT(line_length > 1);
96✔
551
            if (line_length > 1)
96✔
552
            {
553
                --line_length;
96✔
554
            }
96✔
555

556
            // line_length must be larger than first_column_width
557
            // this HPX_ASSERT may fail due to user error or environment
558
            // conditions!
559
            HPX_ASSERT(line_length > first_column_width);
96✔
560

561
            hpx::string_util::tokenizer paragraphs(desc,
192✔
562
                hpx::string_util::char_separator(
96✔
563
                    "\n", "", hpx::string_util::empty_token_policy::keep));
564

565
            auto par_iter = paragraphs.begin();
96✔
566
            auto const par_end = paragraphs.end();
96✔
567

568
            while (par_iter != par_end)    // paragraphs
201✔
569
            {
570
                format_paragraph(
105✔
571
                    os, *par_iter, first_column_width, line_length);
105✔
572

573
                ++par_iter;
105✔
574

575
                // prepare next line if any
576
                if (par_iter != par_end)
105✔
577
                {
578
                    os << '\n';
9✔
579

580
                    for (std::size_t pad = first_column_width; pad > 0; --pad)
241✔
581
                    {
582
                        os.put(' ');
232✔
583
                    }
232✔
584
                }
9✔
585
            }    // paragraphs
586
        }
96✔
587

588
        void format_one(std::ostream& os, option_description const& opt,
96✔
589
            std::size_t first_column_width, std::size_t line_length)
590
        {
591
            std::stringstream ss;
96✔
592
            ss << "  " << opt.format_name() << ' ' << opt.format_parameter();
96✔
593

594
            // Don't use ss.rdbuf() since g++ 2.96 is buggy on it.
595
            os << ss.str();
96✔
596

597
            if (!opt.description().empty())
96✔
598
            {
599
                if (ss.str().size() >= first_column_width)
96✔
600
                {
601
                    // first column is too long, lets put description in new line
602
                    os.put('\n');
6✔
603
                    for (std::size_t pad = first_column_width; pad > 0; --pad)
216✔
604
                    {
605
                        os.put(' ');
210✔
606
                    }
210✔
607
                }
6✔
608
                else
609
                {
610
                    for (std::size_t pad = first_column_width - ss.str().size();
1,640✔
611
                         pad > 0; --pad)
1,640✔
612
                    {
613
                        os.put(' ');
1,550✔
614
                    }
1,550✔
615
                }
616

617
                format_description(
96✔
618
                    os, opt.description(), first_column_width, line_length);
96✔
619
            }
96✔
620
        }
96✔
621
    }    // namespace
622

623
    std::size_t options_description::get_option_column_width() const
26✔
624
    {
625
        /* Find the maximum width of the option column */
626
        std::size_t width(23);
26✔
627
        std::size_t i;    // vc6 has broken for loop scoping
628
        for (i = 0; i < m_options.size(); ++i)
203✔
629
        {
630
            option_description const& opt = *m_options[i];
177✔
631
            std::stringstream ss;
177✔
632
            ss << "  " << opt.format_name() << ' ' << opt.format_parameter();
177✔
633
            width = (std::max)(width, ss.str().size());
177✔
634
        }
177✔
635

636
        /* Get width of groups as well*/
637
        for (auto const& group : groups)
41✔
638
            width = (std::max)(width, group->get_option_column_width());
15✔
639

640
        /* this is the column were description should start, if first
641
           column is longer, we go to a new line */
642
        std::size_t const start_of_description_column =
26✔
643
            m_line_length - m_min_description_length;
26✔
644

645
        width = (std::min)(width, start_of_description_column - 1);
26✔
646

647
        /* add an additional space to improve readability */
648
        ++width;
26✔
649
        return width;
26✔
650
    }
×
651

652
    void options_description::print(std::ostream& os, std::size_t width) const
26✔
653
    {
654
        if (!m_caption.empty())
26✔
655
            os << m_caption << ":\n";
18✔
656

657
        if (!width)
26✔
658
            width = get_option_column_width();
11✔
659

660
        /* The options formatting style is stolen from Subversion. */
661
        for (std::size_t i = 0; i < m_options.size(); ++i)
203✔
662
        {
663
            if (belong_to_group[i])
177✔
664
                continue;
81✔
665

666
            option_description const& opt = *m_options[i];
96✔
667

668
            format_one(os, opt, width, m_line_length);
96✔
669

670
            os << "\n";
96✔
671
        }
96✔
672

673
        for (auto const& group : groups)
41✔
674
        {
675
            os << "\n";
15✔
676
            group->print(os, width);
15✔
677
        }
678
    }
26✔
679
}    // 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