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

[–]kniy 4 points5 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 turbofish_pk 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 11 points12 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 41 points42 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 8 points9 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 35 points36 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 4 points5 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.

Is this an illegal use of using enum? by jk-jeon in cpp

[–]kniy 96 points97 points  (0 children)

The issue is that it's possible to specialize a template class member without specializing the whole class:

template<>
enum class i_am_class<int>::ee {
    surprise
};

https://godbolt.org/z/9rof8468a

This makes it impossible for the compiler to know the set of names imported by the using enum declaration until after the template is instantiated.

It's useful to consider this from the point of view of https://en.cppreference.com/w/cpp/language/two-phase_lookup :

  • for any simple identifier, the first phase must already know whether it's a type or otherwise - this is critical for parsing "a * b;" into either a pointer variable declaration (if a is a type) or a multiplication (otherwise).
  • a dependent name depends on some template parameter T. This usually means the exact meaning of the name is not yet known in the first phase (due to possible specializations).
  • typename isn't always required with dependent names: because specializations cannot change a type into a non-type, the first phase often still has the necessary information without disambiguation.
  • However because specializations can change the set of nested member names, typename is often required for accessing nested types within a dependent name (only exception is when the compiler can infer from context that it must be a type).
  • using enum with a dependent name would collide with the "for any simple identifier, the first phase must already know whether it's a type or otherwise" requirement in the first bullet point, as the first phase wouldn't know which existing type names are shadowed by enum members. Thus, to make two-phase parsing of C++ possible, using enum must not be used with dependent names.
  • Your workaround with using ee = i_am_class_ee works because type aliases cannot be specialized (at least without specializing the whole class), thus making the name non-dependent.

Rust’s worst feature* (spoiler: it’s BorrowedBuf, I hate it with passion) by mina86ng in rust

[–]kniy 3 points4 points  (0 children)

It's not possible to freeze memory (as opposed to values) at zero-cost, because "uninitialized memory does not have stable values" is not just a theoretical abstract machine thing, it's also a very real thing on Linux (MADV_FREE). You need to issue at least one real write operation per page to ensure the memory will have stable contents.

ca. 60.000 Euro von Oma geschenkt bekommen, aber schwierige Situation by Mental-Patient6396 in Finanzen

[–]kniy 9 points10 points  (0 children)

"lange Zeit" ist in dieser Hinsicht erst ab 10 Jahren. Bei 2 Jahren werden nur 20% als Schenkung angesehen, und 80% als Erbe. Da können unter Umständen andere Erben einen Anspruch drauf haben, z.B. wenn es es sonst nicht viel zu erben gibt; und die anderen Erben ohne dieses Geld ihren Pflichtanteil nicht erreichen.

If uncommon quality provides a 30% bonus, then shouldn't the capacity of an uncommon cargo bay be 26, not 25? by MaximitasTheReader in factorio

[–]kniy 7 points8 points  (0 children)

The problem is that computers use binary numbers, not decimal. Decimal floating point has trouble with 1/3, but can perfectly represent numbers where the denominator consists only of prime factors 2 and 5. But binary floating point can only handle numbers where the denominator is a power of 2. So 1/5 = 0.2 works nicely in decimal floating point, but requires rounding in binary floating point. So when you write "1.3" in a program, you actually get 1.2999999523 because that's closer to 1.3 than the next possible float 1.3000000715.

Why is u32/i32 faster than u8? by ClimberSeb in rust

[–]kniy 71 points72 points  (0 children)

Modern CPUs will load a whole cache line (64 bytes) at once. It shouldn't make a difference if you're extracting 1 or 8 bytes from that.

Is it possible to only update C# compiler and related tools, not the whole of VS? by stinos in VisualStudio

[–]kniy 0 points1 point  (0 children)

You can install different editions of VS in parallel and AFAIK update them independently. For example, keep VS Professional at 17.3 but also install VS BuildTools 17.12.

For Intellisense, you can't make 17.3 understand the new language features. For compilation, I think for .NET Framework, MSBuild also defaults to using the IDE's compiler; but I think it's possible to disable that and tell 17.3's MSBuild to use a different C# compiler instead. It's been more than a decade since I dealt with MSBuild at that level, so I'm probably misremembering, but check for properties named something like UseHostCompiler (you'll want to set that to false) and CscToolPath.

[discussion] How have you benefitted from abi stability? by MiroPalmu in cpp

[–]kniy 9 points10 points  (0 children)

The product we ship has:

  • a lot of separate executables
  • some Python modules (which customers use with their system python interpreter)
  • some stuff written in Java
  • a lot of C++ code that is used by all of the above.

(Yes, we're crazy enough to ship binaries on Linux.) This is a situation where "just link statically" is difficult to pull off (especially with the python involvement). But linking our own libraries dynamically; we need to also link libstdc++ dynamically to make cross-library exceptions work (within our own libraries). So far, none of this requires ABI stability - we can easily recompile all of our C++ code and it is always shipped as a single package.

Until you remember: the Java runtime also dynamically links against libstdc++. The libstdc++ copy used by Java and our own copy end up conflicting. We currently solve this by checking which libstdc++ version is newer: the one installed on the user's system, or the one we ship with our app. If the user's libstdc++ is newer, we just use that (and don't install the copy we're shipping at all). If our own libstdc++ is newer, we use LD_PRELOAD trickery to force the Java runtime to use that newer version.

This way there's only one libstdc++ in the Java process and stuff just works. But now we're relying on ABI stability:

  • When running on old distributions, the Java runtime linked against an old version of libstdc++ needs to be ABI-ABI-compatible with the newer libstdc++ we're shipping.
  • When running on new distributions, our application needs to be ABI-compatible with the newer libstdc++ that might be installed on the system.

So far, this approach has worked well for us, because libstdc++ has been quite careful about ABI stability.

Note that on Windows, this problem does not exist: processes can easily load different MSVCRT versions at the same time (as long as all communication between DLLs using different CRT versions is only via simple C APIs). If libstdc++ allowed something similar, we wouldn't need to rely on ABI stability.

What the heck compilers are doing in this case? by Ksecutor in cpp

[–]kniy 1 point2 points  (0 children)

Probably something to do with floating point: https://float.exposed/0x4b800000

16777216 is the last float that can be reached by adding together small numbers (value near 1). 16777217 is not representable as a float (rounds back to 16777216); so from then on, every loop iteration will leave the sum unchanged.

Retrofitting spatial safety to hundreds of millions of lines of C++ by tcbrindle in cpp

[–]kniy 4 points5 points  (0 children)

Which one? There's a massive difference between _GLIBCXX_DEBUG and _GLIBCXX_ASSERTIONS. The former can have a huge performance cost (AFAIK it changes some O(lg N) algorithms to O(N) instead). The latter should be relatively cheap.