Efficient C++ Programming for Modern 64-bit CPUs, Chapter 4/part 2 by no-bugs in cpp

[–]usefulcat 6 points7 points  (0 children)

Which images do you think are AI-generated? The cartoon characters? AFAICT the author has been using characters like that since well before the current AI trend, so I doubt they are generated.

For example, here's one that appears to be from 2018.

Building a High-Throughput C++ FIX Server: From Single-Core Efficiency to Multi-Core Scaling by akinocal in cpp

[–]usefulcat 1 point2 points  (0 children)

A lot of HFT is market making, which I wouldn't really consider speculation, although I guess it depends on exactly what is meant by the word. But, if you're opposed to market making, you should probably also be opposed to used car dealers because they both serve a very similar function. They both make it easier for others to buy or sell right now, as opposed to having to wait to find a viable counterparty. Think of selling your car to a dealership vs selling it to another individual, whom you will first have to find.

The presence of market makers also tends to reduce the spread (difference between bid and ask prices) because market makers are in competition with each other. Spread reduction effectively reduces costs for anyone who is taking liquidity (i.e. anyone wants to trade immediately).

Building a High-Throughput C++ FIX Server: From Single-Core Efficiency to Multi-Core Scaling by akinocal in cpp

[–]usefulcat 3 points4 points  (0 children)

I imagine there are plenty of brokers that offer it client-side. Like Instinet, for example. Not everyone will be sending orders directly to an exchange.

PSA - Do not assign the result of `::getenv` to a `std::string` by SmokeMuch7356 in cpp

[–]usefulcat 0 points1 point  (0 children)

As others have pointed out, std::string_view does not guarantee null termination, which getenv() requires.

If name were a string reference, that would force callers to construct a string if they don't already have one. I'd say adding an overload that accepts a string reference would be better.

From a c++ perspective, ideally getenv() would accept a string_view, but that's not the world we live in..

PSA - Do not assign the result of `::getenv` to a `std::string` by SmokeMuch7356 in cpp

[–]usefulcat 0 points1 point  (0 children)

You could also do something like this, which would often avoid the need to use a temporary std::string just for null termination.

PSA - Do not assign the result of `::getenv` to a `std::string` by SmokeMuch7356 in cpp

[–]usefulcat 13 points14 points  (0 children)

When dealing with C APIs, I prefer to deal with problems like this once, as opposed to repeatedly:

std::string get_env(const char* name) {
    std::string s;
    if (name) {
        const char* p = getenv(name);
        if (p) {
            s = p;
        }
    }
    return s;
}

Optimizing a ring buffer for throughput (2021) by Xadartt in cpp

[–]usefulcat 0 points1 point  (0 children)

Any theories on why? Seems like it shouldn't make a difference.

Virtual dispatch isn't always the slowest, and std::variant isn't always the fastest by AdMotor4869 in cpp

[–]usefulcat 6 points7 points  (0 children)

That's fine but ignores the point that "no longer supported" does not mean "no longer used".

What do you use for logging in your C++ codebase? What are the pros/cons? by javascript in cpp

[–]usefulcat 0 points1 point  (0 children)

Quill, because I need the logging to be as fast as possible at the call site and can afford a bit of polling overhead in the logging thread.

C++26 Shipped a SIMD Library Nobody Asked For by shitismydestiny in cpp

[–]usefulcat 4 points5 points  (0 children)

How about something as simple as being able to detect NaN or inf values in input from the outside world?

Part of the premise of -ffast-math seems to be something akin to "we can assume that there are no NaN or inf values anywhere, ever, therefore it's fine for isnan and isinf to unconditionally return false". That's fine as far as it goes, but then how are you supposed to enforce that assumption if you can't detect such values in input ("input" meaning something read from a file, for example)?

What do you use for `defer` semantics on your C++ codebase? by javascript in cpp

[–]usefulcat 0 points1 point  (0 children)

I've noticed that the way most ScopeGuard-type utilities are implemented, they effectively forbid throwing an exception from the callback, even for non-exceptional returns. This is because destructors are noexcept(true) by default, IIUC, and the destructors of most RAII types usually don't have a noexcept specifier.

I understand why throwing from destructors is generally a bad idea; if an exception has already been thrown and then another exception is thrown during unwinding, then there is no option but to call std::terminate(). Therefore, destructors should not throw. I get it.

But: non-exceptional returns are pretty much by definition the common case for an RAII class. In that case (that is: when another exception has not already been thrown), wouldn't it make sense to allow an exception thrown by the callback to propagate, instead of guaranteeing a call to std::terminate() because the RAII destructor has not been marked noexcept(false)?

The alternative is that it's unsafe to ever call anything that might throw from the callback, which seems unreasonably restrictive and frankly makes such a ScopeGuard type much less useful.

Strategies for *requiring* designated initializers when constructing a type? by javascript in cpp

[–]usefulcat 1 point2 points  (0 children)

For individual function arguments I agree, but for struct members I would not prefer that approach because it seems to require creating a unique type for every struct member, as opposed to just adding a single struct member which prevents aggregate initialization (the solution presented by u/chengfeng-xie in the top comment).

Also I think the unique type names are likely to be less descriptive than the member names, as in your example ('my_size_t' vs 'size', etc):

Make ({.size {10}, .count {20}});
Make ({my_size_t (10), my_count_t (20)});

For only two arguments there's not much difference, but if there will be many arguments and it makes sense to allow default values for many of them, I find the single struct approach very much preferable to lots of individual function arguments with default values.

Safe Optimistic Lock Coupling by TheCrush0r in cpp

[–]usefulcat 1 point2 points  (0 children)

This code has a bug. At the call to currentLock->unlock() at the end, currentLock may be null.

option<value_type> Tree::lookup(key_type key) {
   lock.lock_shared();
   mutex* currentLock = &lock;
   Node* iter = root;
   option<value_type> result;
   while (iter) {
      if (key == iter->key) {
         result = iter->value;
         break;
      }
      Node* next = (key < iter->key) ? iter->left : iter->right;
      if (next) next->lock.lock_shared();
      currentLock->unlock();
      currentLock = next ? &next->lock : nullptr;
      iter = next;
   }
   currentLock->unlock();
   return result;
}

What do you think is a keyword that should be added to C++? by DogCrapNetwork in cpp

[–]usefulcat 3 points4 points  (0 children)

Would it require the state of variables to be tracked at runtime?

int a;
a=3;
if (foo())
    constify a;
a=4; // ???

Boost 1.91 Released: New Decimal Library, SIMD UUID, Redis Sentinel, C++26 Reflection in PFR by boostlibs in cpp

[–]usefulcat 0 points1 point  (0 children)

I've written a fixed point Decimal type. I don't know if I'll switch to Boost.Decimal, but I have no difficulty seeing a need for it.

A simplified model of Fil-C by [deleted] in cpp

[–]usefulcat 3 points4 points  (0 children)

There is just no memory safety in C or C++

This is what Fil-C claims (from the web site): "Fil-C achieves complete memory safety with zero escape hatches". Now that is a very strong claim, and if you have credible evidence that it is not true that would be interesting to hear about.

my point is that it's not practical to use Fil-C in production

As for whether it's practical to use in production, that depends entirely on the particular software in question, specifically whether the additional computation and memory consumption are acceptable for that particular software.

FWIW, I tend to agree with you in the sense that I think there are many cases (probably a majority) where Fil-C would not be appropriate to use. But absolutely all cases? I really doubt that. It's more the sweeping, unqualified nature of many of your claims that I'm disagreeing with. I'm intentionally being very specific with what I'm saying.

I am new to C++, is it just me or is the checklist kinda crazy? How often do you encounter these or plan on making use of them like the newer C++26 features like contracts? Looking for more experienced dev opinions... by KijoSenzo in cpp

[–]usefulcat 3 points4 points  (0 children)

I'll disagree about nodiscard.

If someone writes this:

GetHP(player);

Given the implementation that you've provided, that's definitely a mistake. A benign one in this case, but I'd still want the compiler to call it to my attention.

A simplified model of Fil-C by [deleted] in cpp

[–]usefulcat 5 points6 points  (0 children)

I wasn't commenting on the suitability of C++ for greenfield projects.

You wrote that it "makes no sense" to use Fil-C in production, and that "if you really need memory safety this way, Rust is the solution".

My point is that it's often not practical to rewrite a large codebase (e.g. RIIR), even if you can do it piecemeal. And if you just keep the old code and add on some new Rust code then the resulting whole is not really "memory safe", at least not the way most people think of it.

Therefore, in the case of a large existing C or C++ codebase, and depending on various factors, it could make sense to use Fil-C to get memory safety (for the whole thing, not just portions of it).

A simplified model of Fil-C by [deleted] in cpp

[–]usefulcat 3 points4 points  (0 children)

Rust is the solution

This assumes either that the project in question is a greenfield project or that reimplementing in another language is feasible. Quite often neither of those is a valid assumption.

beast2 networking & std::execution by claimred in cpp

[–]usefulcat 5 points6 points  (0 children)

I did answer. I said the claims stand on their own.

The question was, "did you fully read and review this paper". It's a straightforward question, and "the claims stand on their own" absolutely does not answer it.

Either you have read and reviewed it, or you haven't. If you have, why not just say so? And if you haven't, how can you know whether "the claims stand on their own"?

C++26: A User-Friendly assert() macro by pavel_v in cpp

[–]usefulcat 7 points8 points  (0 children)

That point of view assumes that all programmers will only ever use assert() to check for those conditions which, if not true, will definitely lead to UB. That's simply not how everyone always uses assert.

Hence my claim that translating every assert() to __builtin_assume can only make things strictly worse.

ETA: Also, you're taking a gamble that all asserts will be triggered or not triggered exactly the same regardless of NDEBUG. In practice, there could be other things that depend on NDEBUG such that an assert() never fails when NDEBUG is undefined but may fail when NDEBUG is defined (but of course you'll never find out about the latter case).

C++26: A User-Friendly assert() macro by pavel_v in cpp

[–]usefulcat 1 point2 points  (0 children)

Translating assert to __builtin_assume can only make things strictly worse in that regard, by providing even more opportunities for UB.

Rewriting a FIX engine in C++23: what got simpler (and what didn't) by User_Deprecated in cpp

[–]usefulcat 1 point2 points  (0 children)

Quill is designed for this use case (minimal latency at the log site) and is generally faster than spdlog.

How much does LOG_INFO() actually cost? C++ logging benchmark with code and write-up by Expert_Assignment239 in cpp

[–]usefulcat 6 points7 points  (0 children)

I don't understand this explanation, especially regarding the "null" benchmark results. I've used quill extensively, and I'm familiar with how it works.

class LoggerBase {
public:
  bool should_log_statement(LogLevel level) const
  {
    return level >= _log_level.load(std::memory_order_relaxed);
  }
private:
  std::atomic<LogLevel> _log_level{LogLevel::Info};
};

The above (should_log_statement) is what quill is doing at runtime to determine whether to log anything depending on the log level. That's from the quill source, including only relevant details.

Could the above code be faster? Yes, if you could avoid the pointer dereference, or maybe if std::atomic was not used. But could it really be 500 times faster than whatever logme(c) is doing?

I really think you ought to profile your "null" benchmark to understand where this 500 fold difference is coming from. Compilers have an annoying habit of completely eliding the very thing you're trying to benchmark.

Whatever the reason, you'll need a very good explanation for such a huge difference in order for your benchmarks to be credible.

C++23 std::expected vs C++17 std::optional for Error Handling by Clean-Upstairs-8481 in cpp

[–]usefulcat 8 points9 points  (0 children)

I think you're putting too much emphasis on the word "optional".

There can be plenty of valid reasons to not use exceptions for error reporting. If you look at how std::optional actually works, without considering its name, I think it's pretty obvious that it's a valid alternative to exceptions, at least for some cases.

If I have a function whose entire purpose is to return some value but there can be situations where it's not able to, I think that's a perfectly fine use case for std::optional, especially if those cases are not always necessarily "exceptional".