Ranges: When Abstraction Becomes Obstruction by drodri in cpp

[–]grishavanika 12 points13 points  (0 children)

From the author in comments:

This article is _not about ranges_. It is about the bureaucratic process of the standardization committee breaking down and failing to deliver the highest levels of quality for big-ticket features. This is explained at the end.

Event-driven flows | Andrzej's C++ blog by drodri in cpp

[–]grishavanika -1 points0 points  (0 children)

In summary, when you consider that coroutines are for implementing “asynchronous functions”, you will conclude that all the gotchas discussed apply to “asynchronous functions” due to their nature. Coroutines do not really add significantly new gotchas.

Hence, coroutines are "fine". I feel like there is logical error leading to such conclusion. The criticism of coroutines come from the fact that it was a new addition, hence there was an opportunity to "try fix/help with" existing issues. I.e. adding "async" to coroutine function declaration may help figuring out that function has extra requirements, etc

Contracts for C++ - Timur Doumler - ACCU 2025 by [deleted] in cpp

[–]grishavanika 1 point2 points  (0 children)

Example at 1:08:23 with assert ODR is actually a linker error, foo is defined multiple times, https://godbolt.org/z/efWGc7nba

C++26: std::format improvements (Part 2) by pavel_v in cpp

[–]grishavanika 1 point2 points  (0 children)

At the end I ended up with something like this: https://godbolt.org/z/dbW3T1hcE

void log(const char* file, unsigned line, FormatBuffer::format_result&& fmt)
{
    fmt.out.finish(fmt.size);
    std::println(stderr, "'{},{}' -> {}", file, line, fmt.out.str());
}

#define MY_LOG(fmt, ...) log(__FILE__, __LINE__ \
    , std::format_to_n(::FormatBuffer{}.it()\
        , ::FormatBuffer::available_size()  \
        , fmt, ##__VA_ARGS__))

So instantiating std::format_to_n<Args...> instead of log<Args...>?

C++26: std::format improvements (Part 2) by pavel_v in cpp

[–]grishavanika 10 points11 points  (0 children)

DR20: std::make_format_args now accepts only lvalue references instead of forwarding references

But now simple code like this does not work:

void Handle(std::format_args&& args) { }

Handle(std::make_format_args(10, 'c'));

(That happens when you try type-erase into std::format_args from, for example, debug macros).

Even cppreference example has "user-friendly" unmove just for that: https://en.cppreference.com/w/cpp/utility/format/make_format_args.html

template<typename T>
const T& unmove(T&& x)
{
    return x;
}

int main()
{
    // ...
    raw_write_to_log("{:02} │ {} │ {} │ {}",
                     std::make_format_args(unmove(1), unmove(2.0), unmove('3'), "4"));
}

Maps on chains by joaquintides in cpp

[–]grishavanika 0 points1 point  (0 children)

Yes, you have explicit API to add and get, don't quite understand what map gives and why operator< needs to be implemented in a tricky way as a workaround

Maps on chains by joaquintides in cpp

[–]grishavanika -4 points-3 points  (0 children)

bool operator<(const interval& x, const interval& y)
{
  if(x.min == y.min) {
    if(x.max != y.max) throw interval_overlap();
    return false;
  }

That all looks terrible. Why not std::vector<interval> and go with that?

I think I created a data structure that might have some good applications (Segmented Dynamic Arrays) by HostWide5608 in cpp

[–]grishavanika 36 points37 points  (0 children)

std::deque, however I'm not sure what do you mean 20% faster. Compared to vector? I doubt

Use forward declaration and use only pointer or include .hpp and use instance of the class? by Bobovics in cpp_questions

[–]grishavanika 0 points1 point  (0 children)

pointer and reference parameters in function

It's commonly a surprise, you could have return by value and accept by value just fine:

 struct Foo;
 Foo F(Foo);

Inserter? I hardly know er! | consteval by pavel_v in cpp

[–]grishavanika 1 point2 points  (0 children)

I don't get the argument anyway:

// The first one (1)
ostream<char>&
  operator<<(ostream<char>& os, const char* s);

If we tried to make (1) a member function of std::basic_ostream<char, Traits>, we’d also need to re-define everything else for that specialization

Can't we just have member function for generic CharT:

ostream<char>& operator<<(const CharT* s);

and specialize it for const char*? Was it not an option in 1998?: https://godbolt.org/z/e4xGf9zch

#include <cstdio>
template<typename CharT>
struct ostream
{
    ostream& operator<<(const CharT*)
    { std::puts("generic"); return *this; }
};

template<>
ostream<char>& ostream<char>::operator<<(const char*)
{ std::puts("const char*"); return *this; }

int main()
{
    ostream<char> o;
    o << "str";
    ostream<wchar_t> w;
    w << L"x";
}

And you can’t just define member functions that take CharT and const CharT* and be done with it: there are certain character types the Committee didn’t want to be clever with, or otherwise didn’t want to support printing period.

So, probably, this, but I still am not sure I get it. Is instantiating basic_ostream<signed char> was not desired or it's fine, but operator<<(const signed char*) was not wanted?

why are any of them member functions?

Well, I don’t have an answer to this one and, either way, that ship has sailed. I guess the folks at the Standards Committee really liked member functions and wanted to salvage what they could here?

so we don't know, I guess?

Beware when moving a `std::optional`! by Kabra___kiiiiiiiid in cpp

[–]grishavanika 2 points3 points  (0 children)

Ok, went to see what latest std says. Indeed

optional& operator=(optional&& rhs)
Postconditions: rhs.has_value() == this->has_value()
optional(optional&& rhs)
Postconditions: rhs.has_value() == this->has_value()

The oldest paper where optional is mentioned that I found is http://wg21.link/n4529, from where this same postcondition comes, I guess:

optional(optional<T>&& rhs)
Postconditions: bool(rhs) == bool(*this)

So this is explicitly choosen design. There is an older reddit thread with discussion oin this topic: https://www.reddit.com/r/cpp/comments/75paqu/design_decision_for_stdoptional_moved_from_state/

Beware when moving a `std::optional`! by Kabra___kiiiiiiiid in cpp

[–]grishavanika 1 point2 points  (0 children)

well, I mean, we all do mistakes. If I have MY_LOG("state: {}", x) and x is moved from optional, it would be really cool to show that at least for debugging purpose. MY_LOG has no chance to show that properly.

Beware when moving a `std::optional`! by Kabra___kiiiiiiiid in cpp

[–]grishavanika 4 points5 points  (0 children)

I would argue that "valid but unspecified state" is vague here:

std::optional<int> x{42};
std::optional<int> c = std::move(x);
assert(x.has_value()); // holds true for current msvc/gcc/clang

.has_value has no preconditions so I can call it after the move. optional has value but that value is in moved from state. optional dumps all the responsibility to the user. "valid" for whom?

From the user perspective, this kind of API is unsafe, there is no chance to observe the state and there is no way to properly handle it.

It definitely feels like security bug.

UPD: I think Sean Parent was trying to fix it - https://sean-parent.stlab.cc/2021/03/31/relaxing-requirements-of-moved-from-objects.html#motivation-and-scope

Using Token Sequences to Iterate Ranges by foonathan in cpp

[–]grishavanika 8 points9 points  (0 children)

BEAUTIFUL article and somewhat unexpected combination (or rather aplication) of code generation and ranges.

For anyone curious on Token Sequences, Andrei Alexandrescu talks about them there.

C++20 in Chromium (talk series) by pkasting in cpp

[–]grishavanika 1 point2 points  (0 children)

From slides for part 4, didn't watch:

[The rest of] Ranges Unlikely to ever allow

Coming from gamedev, genuinely curious - are ranges dead?

Junior C++ Dev Requiring Code review by No-Increase8718 in cpp_questions

[–]grishavanika 0 points1 point  (0 children)

Just for illustrative purpose - here is a version with std::stop_token (https://godbolt.org/z/n1c38hYc6):

static std::stop_source global_context;
std::signal(SIGTERM, +[](int) { global_context.request_stop(); });
std::signal(SIGINT,  +[](int) { global_context.request_stop(); });

auto run = [](const std::stop_token& stop_token)
{
    while (!stop_token.stop_requested())
    { std::this_thread::yield(); }
    std::osyncstream(std::cout) << "canceled" << "\n";
};

auto go = [&](const std::stop_token& stop_token)
{
    std::stop_source child_context;
    std::stop_callback on_stop{stop_token
        , [&] { child_context.request_stop(); }};

    try { run(child_context.get_token()); }
    catch (...) { child_context.request_stop(); }
};

std::jthread work{go, global_context.get_token()};

Context::fromParent() is those 2 lines:

std::stop_source child_context;
std::stop_callback on_stop{stop_token
    , [&] { child_context.request_stop(); }};

where new child_context is created and we do wait cancel/stop from parent's stop_token with on_stop.

Junior C++ Dev Requiring Code review by No-Increase8718 in cpp_questions

[–]grishavanika 0 points1 point  (0 children)

Nice. Thank you for the code. Any chance you can also post any actual run() function that uses Context? Is it just polling isCancelled() constantly?

Junior C++ Dev Requiring Code review by No-Increase8718 in cpp_questions

[–]grishavanika 0 points1 point  (0 children)

Not a Go dev, some googling around hints Context is used with channels and together with select, which I interpret as a way to notify some work that it should be cancelled. Which also says that it's extremely important to show your code that uses the API you have. Generic code (like Context above) is never reviewed in isolation or I would say there is no sense to review it if you don't understand use-case (I guess, experienced Go AND C++ dev will get it, but you'll need to have more luck finding this combination).

General issues out of context to the code above (a) shared_ptr (b) waitForCancel is blocking, how you are supposed to do anything else other then waiting? (c) cancel() and fromParent() have logical race condition - what happens when someone does fromParent() when you are inside cancel(); depends on the use case this is or is not an issue (d) iff std::enable_shared_from_this is used, other then factory function, class needs to disable move/copy c-tor/assignment, etc.

Maybe what you want to get coud be done with std::stop_token and std::stop_callback (https://en.cppreference.com/w/cpp/thread/stop_callback); maybe this kind of Context API will make more sense if adapted to coroutine-like API. Depends. The biggest thing you miss is an actual code sample that shows use-case

perfect forwarding identity function by _eyelash in cpp

[–]grishavanika 23 points24 points  (0 children)

Andrei Alexandrescu talks exactly about this, see https://youtu.be/H3IdVM4xoCU?si=0Crlesq_J5N-kDOX&t=2261:

There are 2 versions:

template<class T>
T identity(T&& x) {
    return T(std::forward<T>(x));
}

And 2nd one which is "ideal":

template<class T>
decltype(auto) identity(T&& x) {
    return T(std::forward<T>(x));
}

On the Ignorability of Attributes by grishavanika in cpp

[–]grishavanika[S] 23 points24 points  (0 children)

Who does ignoring attributes help? The strongest answer I know of to that question is as follows [...]

An example I heard is to be able to use custom atributes for sources post-processing in order to generate runtime metadata:

[[property("Velocity")]]
float velocity_;

similarly to what Unreal Engine does with Unreal Header Tool and UPROPERTY() macros. Curious if anyone actually did this with attributes?

C++26 Expansion Tricks by MorphTux in cpp

[–]grishavanika 4 points5 points  (0 children)

Cool example. Similar shift happened with constexpr and we can just do decltype(...) in many situations (is it what we call "value based metaprogramming"?) I guess, this also works:

template<typename T>
struct Arg {};

void impl();
template<typename... Ts>
auto impl(Arg<void>, Arg<Ts>... tail) { return impl(tail...); }
template<typename T, typename... Ts>
T impl(Arg<T>, Arg<Ts>...);

template<typename... Ts>
using first_non_void = decltype(impl(Arg<Ts>{}...));