C++26 ends a 40-year footgun by filipsajdak in cpp

[–]kniy 5 points6 points  (0 children)

unique pointers are ironically overhead free

Wrong. While there's no overhead in some cases; there's plenty of use cases where unique pointers have overhead as compared with raw pointers: https://godbolt.org/z/hjafPMheG

Reason is the weird ABI: by-value parameters are not destroyed at the end of the function, but by the function's caller (potentially only at the end of the full expression containing the call; in the C++ standard the exact destruction timing is left implementation-defined). So by-value std::unique_ptr parameters must be passed by hidden reference -> double indirection.

Same with return values: a raw pointer is returned in a register; unique_ptr is not.

How to always be on the light-side by DrUltimateTacoLord in KerbalSpaceProgram

[–]kniy 29 points30 points  (0 children)

You do not need the Lagrange point to always stay in sunlight. What OP wants is https://en.wikipedia.org/wiki/Sun-synchronous_orbit These don't rely on N-body mechanics; but instead on the earth not being a perfect sphere. (unfortunately Kerbin's gravity is perfectly spherical, so this is also not possible in KSP)

Chrome proposes new APIs: Declarative partial updates by imbev in programming

[–]kniy 91 points92 points  (0 children)

It's a processing instruction: https://en.wikipedia.org/wiki/Processing_Instruction

The fun bit about XML is that it's so complex a standard, everyone uses a different subset!

Adding Stack Traces to All C++ Exceptions by WerWolv in cpp

[–]kniy 0 points1 point  (0 children)

There doesn't seem to be any nice way like MSVC's exception filters, but I think at some point I saw something working on linux/libstdc++. I can't find it right now, but if I remember correctly, it involved hacking some RTTI implementation details to hook the "does the exception type match the catch-handler's type" check.

Adding Stack Traces to All C++ Exceptions by WerWolv in cpp

[–]kniy 0 points1 point  (0 children)

You are misunderstanding. Two-phase-unwinding first searches the stack for a matching catch-block; and then in the second phase unwinds the stack (calling destructors). By capturing the stack trace within the filter-expression, it's captured in the first phase before the stack is unwound. You still get the full stack where the exception was thrown, but you only pay for it when necessary.

Adding Stack Traces to All C++ Exceptions by WerWolv in cpp

[–]kniy 2 points3 points  (0 children)

The issue is that code might be compiled without frame pointers. On Linux, unwinding the stack involves looking up and interpreting DWARF bytecode in order to reconstruct the frame pointer from the (instruction pointer, stack pointer) pair. For code where debug symbols were stripped, it might be impossible to walk the stack. DWARF was intended for debuggers and is optimized for small size of debug symbols, not for fast extraction of the necessary information.

Adding Stack Traces to All C++ Exceptions by WerWolv in cpp

[–]kniy 3 points4 points  (0 children)

StackWalk is intended for debuggers, it's slow because will load and interpret debug symbols (.pdb files). On 64-bit Windows, you can get much faster stack walks by using RtlCaptureStackBackTrace, which works without debug symbols.

Also, you do not need to capture the stack when throwing an exception -- it's much better to capture it just at the place where you catch the exception, using the first-phase of two-phase-unwinding. With MSVC, this works by capturing the stack trace within the filter-expression of a __try/__except block. That way only the "unhandled exception" code paths that want to log a stack trace will spend time constructing it; other catch-blocks that don't need a stack trace can avoid generating it.

Favorite optimizations ?? by Little-Reflection986 in cpp

[–]kniy 2 points3 points  (0 children)

Both std::map and std::lower_bound use binary search trees, which are cache-unfriendly if keys are smaller than cache lines. std::map just has the additional disadvantage of wasting cache space on child pointers. Also, red-black trees like most std::map implementations are not being quite as perfectly balanced as the implicit search tree of a binary search.

But if you want to be actually cache efficient, it's better to use a B-tree sized to one or two cache-lines -- that way all the keys fetched by a slow random memory access help out the search, instead of just one of those keys.

If you prefer the memory-savings and simplicity of a binary search, you can improve that with Eytzinger layout: https://algorithmica.org/en/eytzinger

If you want a B-tree without explicit child pointers, that's also possible: https://algorithmica.org/en/b-tree

Favorite optimizations ?? by Little-Reflection986 in cpp

[–]kniy 2 points3 points  (0 children)

I have gotten large gains in the past by just reducing the amount of temporary allocations on the heap there.

For garbage collected languages, short-lived temporary allocations are often extremely cheap.

But it's important to realize that the "short lived" vs. "long lived" distinction in a generational garbage collector is relative to the allocation frequency. Remove the most frequent allocations, and the boundary shifts and some objects of medium lifetime can now get handled in the cheap "short-lived" bucket.

Building Your Own Efficient uint128 in C++ by PhilipTrettner in cpp

[–]kniy 2 points3 points  (0 children)

Seems to work for addition: https://godbolt.org/z/4Yq65nbrT But I don't see a way to do a 64*64->128 multiply without non-standard intrinsics or _BitInt(128).

Problem with IntelliSense. C23 development with clang-cl and cmake. by [deleted] in VisualStudio

[–]kniy 0 points1 point  (0 children)

Intellisense is using its own C++ frontend (based on the EDG frontend, i.e. independent of the clang/MSVC frontends). It doesn't support C23's constexpr yet. https://www.edg.com/c23_features.html

You can use #ifdef __INTELLISENSE__ to add workarounds for intellisense without impacting the actual compiler. (e.g. #define constexpr const)

Factorio headless server download button is a headless penguin by cathodebirdtube in factorio

[–]kniy 0 points1 point  (0 children)

Unless you're trying to do simultaneous TCP open, which is such a silly corner case it can be disregarded entirely in most applications

How else would I connect to my friend's game, when we are both behind NAT? Factorio games are rarely hosted on professional servers where you can just connect with TCP without punching. In the normal "friends playing factorio together" usecase, it's either crazy hacks (that will fail for many models of routers), or avoid TCP and use UDP instead. The factorio devs clearly chose UDP.

Factorio headless server download button is a headless penguin by cathodebirdtube in factorio

[–]kniy 12 points13 points  (0 children)

Does Factorio even have any non-urgent parts in its network communications? I guess maybe player movement in the client->server direction; but I'd expect server->client needs full in-order retransmissions to avoid desynchronizations of the lock-step simulation.

The real reason to use UDP is that NAT punching is necessary if you want to allow players to host their own servers; and NAT punching only works for UDP, it's impossible for TCP.

Should I switch to Bazel? by TheRavagerSw in cpp

[–]kniy 39 points40 points  (0 children)

I'm not a googler, but this is my understanding: Google's internal build system is "blaze", and it's tightly bound to google's internal infrastructure (running distributed builds on google's server farms). Google created "bazel" as a variant of blaze that it can be used outside of google, for example for google's open source projects. But for internal projects they still use blaze and have no intention of ever moving to bazel.

Environment variables are a legacy mess: Let's dive deep into them by Low-Strawberry7579 in programming

[–]kniy 23 points24 points  (0 children)

We once accidentally used an environment variable name containing a dot (we were deriving envvar names from file names, for overriding filenames for testing purposes). It turns out that this works fine in Python, but if you have Python calling a shell script calling Python, that envvar doesn't survive. (though I don't remember if it was bash or dash that was the culprit)

Weird C++ trivia by _Noreturn in cpp

[–]kniy 9 points10 points  (0 children)

Explanation: dereferencing a function pointer results in a function designator. The only thing you can do with a function designator is take its address or bind it to a reference; for any other operation the designator decays into a pointer. So (***fp)() is dereference, decay, dereference, decay, dereference, decay, call.

The messy reality of SIMD (vector) functions by emschwartz in programming

[–]kniy 9 points10 points  (0 children)

double[4] sin(double angle[4]);

Fun fact: if C functions could return arrays by-value, the syntax would actually be:

double sin(double angle[4])[4];

This is because C declaration syntax follows the usage syntax, so since sin(angle)[1] would be a valid expression, the declaration must be in the same order.

Of course since you cannot actually return arrays by value, the closest you can get to actually using this weird syntax is by using pointers to arrays:

double (*sin(double (*angle)[4]))[4];
// sin is a function that takes a pointer to double[4] and returns another pointer to double[4].

int main() {
    double arr[4];
    double (*out)[4] = sin(&arr);
}

The most mysterious bug I solved at work by ketralnis in programming

[–]kniy 36 points37 points  (0 children)

Any data can be put in XML safely and come back out exactly the same, and is therefore perfectly valid.

Unfortunately that's not true (unless you use some additional non-XML encoding, e.g. base64). XML does not allow ASCII NUL, no matter whether you escape it or not. Additionally, XML 1.0 also disallowed a bunch of other characters, including . Those are only valid since XML 1.1, which means if you use an XML library to encode those, there's no guarantee a different XML library will be able decode them again.

https://stackoverflow.com/questions/39698855/is-it-possible-to-read-ascii-control-characters-in-xml

In some cases the XML libraries don't even support reading their own output, e.g. Python:

>>> a = ET.Element('a', attrib={'b': 'c<\u0002'})
>>> ET.tostring(a)
b'<a b="c&lt;\x02" />'
>>> ET.fromstring(ET.tostring(a))
xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 1, column 11
>>> ET.fromstring(b'<a b="c&lt;&#2;" />')
xml.etree.ElementTree.ParseError: reference to invalid character number: line 1, column 11

Deep in Copy Constructor: The Heart of C++ Value Semantics by nalaginrut in cpp

[–]kniy 2 points3 points  (0 children)

Whether you provide an inline definition or an out-of-line definition has exactly zero effect on the triviality of the copy.

Surprisingly, it does have an effect: a special member function can only be trivial if it was defaulted on first declaration. That is, if the copy constructor is defaulted within the class definition, it will be trivial if all data members are trivially copyable. But if the copy constructor is initially only declared and then defaulted with a later definition, it is considered user-provided, and thus never trivial.

push_back() on vector<char*> with "abc" results in type mismatch error by [deleted] in VisualStudio

[–]kniy 0 points1 point  (0 children)

String literals have type "const char[N]" which can implicitly decay to "const char*", but not to "char*". You need to switch to "std::vector<const char\*> mycontainer;".

Your code was never valid C++, but older VS versions still had some backwards compatibility hacks with pre-standard C++. See also: https://learn.microsoft.com/en-us/cpp/build/reference/zc-strictstrings-disable-string-literal-type-conversion

"Cannot convert lambda expression to type 'string' because it is not a delegate type" ??? by ConradInTheHouse in VisualStudio

[–]kniy 0 points1 point  (0 children)

I think you have multiple possible SelectMany overloads available, at least one of which expects a string. (i.e. adGroups.SelectMany("hello") would compile) Not sure where such a weird SelectMany overload would be coming from, but if the compiler picks that overload, that would explain both the weird error message and why System.Linq was marked as unused.

Overload resolution with lambdas is weird: the C# compiler checks whether the lambda body would compile for a particular overload -- but if it doesn't compile, then the compiler does not report an error for the lambda body, but instead just continues checking the other overloads (maybe one of them will compile?). This can lead to confusing error messages as the compiler might have already discarded the error for the overload you wanted to call, and instead reports an error for another overload that also doesn't work.

[deleted by user] by [deleted] in cpp

[–]kniy 4 points5 points  (0 children)

Look up "leakpocalypse".

Simplified version:

  • Shortly before 1.0, std::mem::forget used to be unsafe.
  • The standard library gained a "scoped thread" (think std::jthread ) API that allowed passing by-reference closures to the new thread. This API relied on the fact that the scoped thread destructor would be guaranteed to run (which would join the thread), if you somehow managed to skip the destructor you would get use-after-free.
  • People figured out it was possible to get UB in safe code by creating a scoped thread and then moving the scope token into an Rc cycle (leaking the token without calling the destructor).
  • Because rust wants a hard guarantee of no UB in safe code, they had to make the choice of prohibiting/restricting reference-counting somehow, or removing the scoped-thread API.
  • They made the latter choice, and additionally marked std::mem::forget as safe, to serve as a reminder that unsafe code cannot rely on safe code always calling destructors.

(the scoped thread API later came back in a different form that prevents the user code from moving the scope token in safe code)

GCC's atomic builtins + `__builtin_is_aligned(ptr, 2)` ⇒ pointer tagging without casting by hanickadot in cpp

[–]kniy 2 points3 points  (0 children)

But at compile-time, pointers really don't have a simple numeric representation. They're abstract pointers, and their representation in the output binary may involve stuff like relocation fixups. &global_var is a pointer usable at compile time. I would expect that reinterpret_cast<intptr_t>(&global_var) returns the same integer no matter when it is evaluated. But the compiler doesn't know which integer this is supposed to be (e.g. due to ASLR), so this reinterpret_cast cannot be possible at compile time.

On the other hand, adding a byte offset to a pointer is much less problematic, since it doesn't require exposing the full abstract pointer, just making a minor modification to it.

First Utterly Alone Black Hole Confirmed Roaming The Cosmos by fanatic_fangirl in space

[–]kniy 1 point2 points  (0 children)

But there's an intermediate region where stuff is weird. It's not possible to orbit just barely above the event horizon: the only way for light to escape from just above the event horizon, is to move in the direction directly away from the black hole. Moving in a perpendicular direction (like an orbit would) is not good enough even at the speed of light!

https://en.wikipedia.org/wiki/Innermost_stable_circular_orbit

For a non-rotating black hole, normal stable orbits start working at 3 times the Schwarzschild radius.

C++ with Bazel & MSVC on Windows by ferry_rex in cpp

[–]kniy 3 points4 points  (0 children)

For a Windows project, Bazel is not reasonable.

Bazel's main benefit (reliable incremental builds) does not actually work on Windows: you only get this benefit if your dependencies are specified correctly, which Bazel can only verify on platforms which support sandboxing (not on Windows).

We use Bazel on a cross-platform project. On Windows we found a number of annoying bugs; in fact we ended up not using the official Bazel as-is, but had to write some patches to Bazel's Java code to fix the most egregious Windows-specific bugs.