std::latch
en.cppreference.com
en.cppreference.com
In short (copied from that answer):
* Barriers are useful when you have a bunch of threads and you want to synchronise across of them at once, for example to do something that operates on all of their data at once.
* Latches are useful if you have a bunch of work items and you want to know when they've all been handled, and aren't necessarily interested in which thread(s) handled them.
I wasn't following the C++20 development like I usually do. A combination of no longer working with C++ and feeling a bit disillusioned by the long wait for Concepts and Modules. I did read a summary of all language proposals which made it in, but this made me realize I didn't read up on the changes to the standard library.
std::barrier has extra knobs that I didn't discover a good use for, or would have used them in the sample. Enlightenment welcome.
This might be a good place to mention: cppreference.com is curated by ISO Standard C++ committee participants. (Cplusplus.com, by contrast, is very poorly maintained.) If cppreference disagrees from the Standard, it is likely that the Standard will soon be amended to match.
Speaking of Godbolt: it enforces a limit of three spawned threads, but the Gcc and Clang thread sanitizers use up a thread. So if you want to try the samples on Godbolt under the thread sanitizer, you have to reduce the problem sizes to two. Learned that the hard way.
Are you sure you've not changed the dropdown? I just made some changes to the example, hit "run this code", hit "run", and the output updated accordingly.
EDIT: Answer from the developers here https://stackoverflow.com/a/45873222 it's about compatibility with other types used for related purposes.
In general, the C integer types cannot be relied upon; typedefs like uint32_t, size_t, ptrdiff_t should always be preferred.
Answered by someone on the WG here.
std::string product{"not worked"};
Searching for "curly braces C++ string" is not really productive.
I'm also curious what
[&](job& my_job) { }
means.
The second one is a lambda function / closure over my_job.
The second one is a lambda (which, if you're not familiar with the term, are anonymous functions/functors); the entries in the initial brackets define the captured variables (if any), and whether they're captured by reference (with ampersand) or by value (without).
[1] https://en.cppreference.com/w/cpp/language/list_initializati...
Uniform initialization is great, except for the unfortunate interaction with initialized_lists (yes, we can't have nice things), but if you do not have an initializer_list constructor in your class you do not have to worry.
Why do you say this when you can explicitly see I specifically made sure to avoid claiming it is in fact a forced cast, and instead I said it is kind of like a forced cast? Clearly I meant something other than that it was actually a forced cast, right?
> if you do not have an initializer_list constructor in your class you do not have to worry.
Which is precisely an example of my point about it being problematic. How would you go about this when you don't know everything about a class? Like, say, in a template? And how do you prevent your code from silently breaking if a class you use later adds an initializer_list constructor?
> Uniform initialization is great
I disagree. It's awful. It's just a minor cosmetic change (which we can call an "improvement" for the sake of argument, though I think that's also dubious and the syntax is just ugly) that introduces pitfalls in the actual semantics of your program. That's not a great trade-off; it's a terrible one.
std::int64_t i64 { 44 * 44 * 44 }; std::int8_t i8 = i64; // perfectly fine std::int8_t i8_2 { i64 }; // refuses to narrow the int
uniform initialization resolves lots of headaches, like T() being ambiguous at times with functions, narrowing, etc.
With T() being ambiguous, you can already syntactically disambiguate. With extra parentheses or whatever other construct the case may warrant. As people have been doing all these years. Again, it's just a minor syntactic inconvenience.
>[&](job& my_job) { } A lambda function that takes in a reference to job!
C++ has definitely evolved but the package management is still difficult unlike cargo!
For personal projects, vcpkg with manifests is a breeze to use.
It's the same as std::string product; product = "not worked";
[&](job& my_job) { } is a lambda expression. & is capturing the variable by reference. my_job is the parameter being passed which is a pointer of type job.
Please check https://en.cppreference.com/w/cpp/language/lambda and https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-... for more.
Not a C++ person, so please forgive my ignorance, but what is the difference between the above and sth. like:
std::string product = "not worked";
or std::string product("not worked");
(Are these even legal C++ statements and, if not, why not? ;-))From the POV of the standard, those are different kinds of initializations. C++ has like 12 different ways of initializing variables, and the differences are quite confusing, but in practical terms in my experience I never had to care too much beside making sure native types are initialized to some specified value.
You can probably find some talks on YouTube talking about the initializations of c++, and 1h30m is probably not enought to cover all the details :')
Brace initialization has an advantage in that it allows you to initialize compound values, like containers and structs, even if they're nested:
std::map<int, std::string> m = { // nested list-initialization
{1, "a"},
{2, {'a', 'b', 'c'} },
{3, s1}
};
Ref: https://en.cppreference.com/w/cpp/language/list_initializati... https://en.cppreference.com/w/cpp/language/aggregate_initial...The second one is a direct initialization, invoking corresponding constructor.
The first one first invokes default constructor and then copy or move assignment depending on rvalueness of the arg.
std::string product;
product = "not worked";
i.e., separate declaration and initialization, as suggested by the grand-parent?https://en.cppreference.com/w/cpp/language/direct_initializa... https://en.cppreference.com/w/cpp/language/move_assignment
What are examples of situations where the other pattern would be preferable?
otherwise you should really avoid it.
It's not exactly the same. The original called the converting constructor std::string::string(const char*). Your example calls the std::string default constructor, then the assignment std::string::operator=(const char*). Maybe you didn't mean literally the same, but where trying to illustrate the rough meaning, but the parent commenter said they were familiar with older versions of C++ so I think they'd already be familiar with converting constructors.
It might be more enlightening to say that all of the following are equivalent:
std::string product{"not worked"};
std::string product("not worked");
std::string product = "not worked";
std::string product = std::string("not worked");
(I'm 90% sure about the last one but can't find documentation for it at the moment.) None of them call the copy constructor std::string::string(const std::string&) or copy assignment operator std::string::operator=(const std::string&), although in older versions of the C++ standard the last two required that the relevant assignment operators (std::string::operator=(const char*) and std::string::operator=(const std::string&) respectively) to be accessible even though it wasn't called.For other combinations of types, these different syntaxes are not equivalent. For example, uniform initialisation (with the braces) won't allow narrowing conversions, such as short to int or double to float.
If you make a point to always use the newest features, where there is a choice, and really put the type system to work for you, programs generally run right the first time, once the compiler is satisfied. In the past ten years I have spent more time filing compiler bug reports than debugging C++ memory usage errors.
It is amazing how many people are all up-to-date on what is new. I think people who complain about C++ on HN must be complaining about a much older version of the language.
Even Android NDK and some subsystems are good examples of this second C++ flavour.
I am looking forward to c++23 and beyond. Hopefully they will eventuall add fixed-point arithmetic support.
I too would love to know the specific reasoning though.
I still miss usint Ctrl-P when working with VS Code.
The C++ stdlib terminology is always so weird.
EitherOwnedOrBorrowed and AtomicReferenceCountedPointer, but that'd be annoying to type once you know them.
code is read more than it is written, so having those longer terms helps with reducing mental load and providing self-documenting codeand with most modern ide or even text editors providing autocompletion, (imho) i think abbreviations not really neccessary (though they can be fun in the case of kotlin ^^)
/// ----------------------------------------------------------------------------
/// latches are great for multi-threaded tests with following sequence of steps
/// 1. setup test data
/// 2. create a latch
/// 3. create test threads each decrementing the latch-count
/// 4. when all threads have reached the latch, they are unblocked.
///
/// sligtly verbose commented code to illustrate this
void foo()
{
/// --------------------------------------------------------------------
/// create a latch with non-zero-count
unsigned const thread_count=...;
std::latch done(thread_count);
my_data data[thread_count];
std::vector<std::jthread> threads;
/// --------------------------------------------------------------------
/// threads decrement count as they are created
for(unsigned i=0;i<thread_count;++i)
threads.push_back(std::jthread([&,i]{
data[i]=make_data(i);
done.count_down();
do_more_stuff();
}));
/// --------------------------------------------------------------------
/// others wait for latch to be signalled, when the count reaches zero,
/// latch is permanently signalled, and threads are woken
done.wait();
process_data();
}
have fun ? var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
}()
wg.Wait()
The middle part of the block can be done freely, you don't have to count the threads beforehand. ...
wg.Add(1)
...
for every thread that you are about to create ?The phrasing is somewhat confusing: it is the waiting thread, not "they", that gets unblocked (count_down does not block).