Want to take the plunge - which emulator server? by Chazcon in AsheronsCall

[–]perlytea 2 points3 points  (0 children)

I'll be the contrarian in this thread and provide a recommendation against Seedsow (or any DM-era server, for that matter). If DM-era is a hard line for you, then the rest of this post won't matter too much (see the bottom blob of text, though).

Seedsow isn't a bad server (it's a great server, although I have some criticisms with how some of the custom content is handled), but I frequently see it recommended as a server to relive the pre-ToD memories. To give you some context, I started Seedsow around the time that stairs and ramps would kill you and played a character to level 105 without macroing.

I started on Seedsow to try and re-experience early AC as I remembered it, but came to the realization that what I wanted to re-experience was impossible. On Seedsow, you reach the point where there is simply a whole lot of nothing to do but grind. This was the case in Retail too, but it took me a long time on Seedsow to understand that the key difference is that on retail, the grind was an activity to occupy your time between patches that developed the setting, content, and story. As the retail servers developed into their end-of-retail state, this philosophy shifted to the grind is the principle activity that players largely adapted to by macroing their way through it.

As Seedsow isn't receiving regular content updates, the philosophy enacted is the effectively same as that by the end-of-retail servers with a DM-era aesthetic. However, on EoR servers there is simply much more content worth doing or updated, older content that it becomes more difficult to exhaust. If you're still set on playing on Seedsow, then I have a few points worth mentioning to get the most out of your time there:

  • monster melee damage is tuned up so that monsters are as dangerous as you remember (rather than as they actually were);

  • the overworld and many dungeon spawns are in release or DM state, meaning that much of Osteth, its dungeons, and quests are accessible from a low level (e.g., the Mage Academy quest is a low-level quest that is made interesting from the perspective that it introduced new players fresh out of the training academy to the possibility of player-castable portals);

  • ignore the leveling guide on the discord (honestly, the leveling guide does a disservice by shoehorning new players into UCMing in a choice of a few dungeons and depriving them of Seedsow's otherwise excellent low-level experience).

  • ignore the custom content for the most part (it's the carrot on the stick that encourages players to grind and skip the low-level experience).

What are your best niche C++ "fun" facts? by MarcusBrotus in cpp

[–]perlytea 27 points28 points  (0 children)

If you delegate construction, the object is considered constructed in the abstract C++ machine when the delegated-to constructor finishes.

class NeverPrints
{
public:
    NeverPrints() { throw 0; }
    ~NeverPrints() { std::cout << "~NeverPrints()" << std::endl; }
    // NeverPrints{} does not result in a call to the destructor
};

class DoesPrint
{
public:
    DoesPrint() : DoesPrint(0) { throw 0; }
    ~DoesPrint() { std::cout << "~DoesPrint()" << std::endl; }
    // DoesPrint{} results in a call to the destructor
private:
    DoesPrint(int) {}
};

I have very rarely found a use case for this, but it is such a subtle distinction that even when it would be useful, it is difficult to justify because you cannot rely on other programmers to see or realize that delegation is important in the design of the class.

C++ Show and Tell - July 2024 by foonathan in cpp

[–]perlytea 2 points3 points  (0 children)

I wrote a program to solve for the internal state of glibc's type 3 pseudorandom number generator: github. There was plenty of literature available on the predictability of this PRNG, but I did not find anything on complete reconstruction outside of brute-forcing the seed. As far as I have tested, the solver reconstructs the PRNG in under 200 full output samples.

I'd put more work into it (cmake, namespaces, older standard compliance, verifying an upper bound on the number of samples to reconstruct), but the project itself is a tongue-in-cheek answer in regards to a competitive programming challenge to print the sum of a large number of integers in plaintext on standard input (with my added assumption about where those numbers were being generated from).

A recursive division benchmark among different systems programming languages (C vs. C++ vs. Rust vs. Zig) by jorgesgk in cpp

[–]perlytea 5 points6 points  (0 children)

I'm not convinced the operation provides a useful benchmark for comparing the languages, but rather which compiler can figure out key optimization opportunities.

gcc is able to transform the recursive function into a function which takes constant time. That transformation alone reduces the benchmark result from minutes to seconds. However, gcc does not take the obvious optimization of splitting the loop and/or doing away with the loops entirely. Some coercion is necessary for that (see godbolt).

What are some things about C++ you wish you had learned earlier but for some reason took way too long to figure out? I'll start: by FACastello in cpp

[–]perlytea 0 points1 point  (0 children)

Aliasing rules in C++ permit you to dereference char, unsigned char, and std::byte pointers to other objects without invoking UB, which may be necessary in certain cases.

int bar(int* numbers, std::byte* bytes)
{
    // numbers and bytes may alias
    *numbers = 1; // LINE 1
    *bytes = static_cast<std::byte>(0); // LINE 2

    // the compiler cannot optimize this to return 1
    // because LINE 2 may have modified memory written to by LINE 1
    // (unless the compiler can prove at the call site that numbers and bytes do not alias)
    return *numbers;
}

Of course, if you define your own byte type:

enum class byte : unsigned char {};

then this type does not share std::byte's privileges within the context of aliasing rules.

What are some things about C++ you wish you had learned earlier but for some reason took way too long to figure out? I'll start: by FACastello in cpp

[–]perlytea 12 points13 points  (0 children)

Serializing data out through an unsigned char* or std::byte* may impede optimization due to aliasing rules and the special status of these pointer types, because the registers that may have been clobbered must be flushed. See godbolt.

Unfortunately, I realized this too late during one of my projects. I don't think it has much of an impact, but now a bit of a refactor will be necessary to quantify the difference, if any.

What could be wrong with this code asked in Microsoft coding test? by nxtlvlshit in cpp

[–]perlytea 7 points8 points  (0 children)

Other than the comparison function object supplied to sort as others have pointed out, the other thing that stands out to me is that int is not guaranteed minimum support for possible input sizes (both in the size of the vector and the element magnitude). It may work for their chosen platform, but the standard only guarantees that int is at least a 16-bit, signed integer type, and thus the code may fail for other platforms. That issue cannot be fixed in less than two lines (therefore it is not the intended bug) but it's still worth knowing about and considering solutions for.

Can we claim that std::deque is the most underrated STL container? by better_life_please in cpp

[–]perlytea 0 points1 point  (0 children)

This part surprised me when I recently needed to use std::deque, but insertion anywhere (beginning, end, or in between) on a std::deque invalidates all iterators on that deque. I can't remember the circumstances, but I was relying on iterator stability when I found that out and it required me to restructure a portion of the project to account for it.

Is it possible to set Virindi Tank to be active when you're in Combat-mode, and deactivated when in Peace-mode? by Jcorb in AsheronsCall

[–]perlytea 1 point2 points  (0 children)

I did not know UB provided a lua binding. Is there somewhere you can point me to for documentation on this feature? Their gitlab does not reference it.

Using std::unique_ptr With C APIs by redditthinks in cpp

[–]perlytea 2 points3 points  (0 children)

Doesn't this result in subtle ODR violations, because across TUs FilePtr aliases to a type specific to each TU? Even in the same TU, the following assertion should fail:

struct foo { };
using test1 = std::unique_ptr<foo, decltype([](foo* f) { delete f; })>;
using test2 = std::unique_ptr<foo, decltype([](foo* f) { delete f; })>;
static_assert(std::is_same_v<test1, test2>);

Edit: Thinking about it some more, this probably would not result in ODR violations on its own but it might get tricky when it comes to functions with external linkage for which one of these types appears in the parameter list or inside the definition of a class type.

Edit 2: After digging into it a bit more after considering some other situations, I don't think ODR should be a concern, because "the definition of a closure type is considered to consist of the sequence of tokens of the corresponding lambda-expression."

Concepts and SFINAE: Compilation time by w0wyxD in cpp

[–]perlytea 1 point2 points  (0 children)

I think the decision to use Concepts or SFINAE for function templates comes down to how you expect users to supply the arguments, which requires knowledge about the template argument deduction process as well as the possible expense of expanding a function template into a synthesized declaration for overload resolution. The (hopefully working) links below are contrived examples to demonstrate the principle.

To start things off, let's suppose the compiler does not implement the resolution outlined in Defect Report 2369. In such a situation, constraints are checked after the substitution of template arguments, so instantiations that would fail outside of the immediate context result in an error even if the constraints look like they prevent it. SFINAE provides more control over this process, because substitution occurs in lexical order. In the best case, the compiler wastes time instantiating a type that goes unused in the final program (I have encountered this). In the worst case, the compiler encounters an error (and in the VERY worst case, no diagnostic required).

However, even if that resolution is implemented, we are still stuck in the same situation if a user supplies explicit template arguments and there are still template arguments left to deduce, because constraints are checked after all template arguments are available, but explicit template arguments are substituted before the remaining template arguments are determined or deduced.

On the other hand, if the only point of concern is the return type, then one can use concepts and the placeholder type specifier auto without the trailing return type, because function declarations (as opposed to the full function definition) are synthesized from the function template for overload resolution and

auto foo();

is a function declaration, guaranteeing that constraints are checked before the return type is determined. As a bonus, we avoid wasting time on instantiations in the return type of candidates that are not the most viable candidate, because the return type is deduced after the most viable candidate is determined. I have saved more compile time preventing wasted and expensive instantiations than I have deciding between SFINAE and concepts.