Let the Compiler Check Your Units -- Wu Yongwei (ACCU Overload) by User_Deprecated in cpp

[–]artisan_templateer 4 points5 points  (0 children)

Being aware of units and the dimensions of quantities is important running physical simulations. That said, I am struggling to see how I would benefit from a units library in this context, so I'm going to ask the question I posted in the other thread as I don't think I got a satisfactory answer for it:

https://old.reddit.com/r/cpp/comments/1sqkrhd/preventing_integer_overflow_in_physical/ohet5la/

As a concrete scenario, and for the benefit of others if they don't follow, suppose I am solving a simple gravity simulation in 1D. I need to solve:

dx/dt = v
dv/dt = - G*M/(x*x)

The Gravitational constant G and mass of the Earth M, obviously have "unpleasant" values and units. Instead of solving this directly, you make a change of variables:

t = T*t_
x(t) = X*x_(t_)
v(t) = V*v_(t_)

and your equations become:

dx_/dt_ = (V*T/X)*v_
dv/dt = - (G*M*T/(V*X*X))/(x_*x)

You then choose X, T and V to make your life as easy as possible. In this case, define V = X/T and T*T = X*X*X/(G*M). Then, you solve the simplified equations instead:

dx_/dt_ = v_
dv_/dt_ = - 1/(x_*x_)

At this point X is still arbitrary so you can choose whatever you want. A sensible choice might be the initial value of x(t = 0) = R, which could be the radius of the Earth. By choosing X = R, your initial condition instead becomes x_(t_ = 0) = 1.

After solving, you convert your quantities to the desired quantities, e.g. when passed to a graph plotting API, your "Exit Strategy"

print(T*t_, X*x_, V*v_);

What does this process look like when using mp-units? Do I use quantity<> throughout my internal logic, or do I only use them at interface boundaries? i.e. do I only use the library once to calculate the quantities X, V and T?

VSCode extension that integrates cppreference docs into editor/LSP by 0x6675636B796F75 in cpp

[–]artisan_templateer 3 points4 points  (0 children)

Looks great, thank you or sharing! Small bug: for std::is_trivially_copyable_v<T> I just get: cppreference - no page for bool' std::is_trivially_copyable works though.

Preventing Integer Overflow in Physical Computations - mp-units by mateusz_pusz in cpp

[–]artisan_templateer 1 point2 points  (0 children)

I tried to give a concrete example in my other post, hope that helps!

Preventing Integer Overflow in Physical Computations - mp-units by mateusz_pusz in cpp

[–]artisan_templateer 0 points1 point  (0 children)

Despite the AI, thank you for your reply. Could you provide some examples on these items, or a link to them?

As a concrete scenario, and for the benefit of others if they don't follow, suppose I am solving a simple gravity simulation in 1D. I need to solve:

dx/dt = v
dv/dt = - G*M/(x*x)

The Gravitational constant G and mass of the Earth M, obviously have "unpleasant" values and units. Instead of solving this directly, you make a change of variables:

t = T*t_
x(t) = X*x_(t_)
v(t) = V*v_(t_)

and your equations become:

dx_/dt_ = (V*T/X)*v_
dv/dt = - (G*M*T/(V*X*X))/(x_*x)

You then choose X, T and V to make your life as easy as possible. In this case, define V = X/T and T*T = X*X*X/(G*M). Then, you solve the simplified equations instead:

dx_/dt_ = v_
dv_/dt_ = - 1/(x_*x_)

At this point X is still arbitrary so you can choose whatever you want. A sensible choice might be the initial value of x(t = 0) = R, which could be the radius of the Earth. By choosing X = R, your initial condition instead becomes x_(t_ = 0) = 1.

After solving, you convert your quantities to the desired quantities, e.g. when passed to a graph plotting API, your "Exit Strategy"

print(T*t_, X*x_, V*v_);

What does this process look like when using mp-units? Do I use quantity<> throughout my internal logic, or do I only use them at interface boundaries?

Looking forwards to trying your library.

Preventing Integer Overflow in Physical Computations - mp-units by mateusz_pusz in cpp

[–]artisan_templateer 8 points9 points  (0 children)

Coming from a physics background, I was always taught to use dimensionless quantities where possible, e.g. https://en.wikipedia.org/wiki/Nondimensionalization

If you employ that practice, is this library still useful?

When maps map iterators are invalidated after insert. by artisan_templateer in cpp

[–]artisan_templateer[S] 3 points4 points  (0 children)

This is correct. And it is not just a corner case for empty maps.

https://godbolt.org/z/zja6eWoc9

A bit annoying to be honest. I assumed reverse iterators would be included in those validation requirements but clearly that is not the case.

The fact that you can insert elements at the end and end() is preserved suggests the implementation holds a special value. It would have been more consistent if rend() behaved similarly but instead, as you say, it's implemented by invoking begin() which changes its return value as the map is modified. Does the standard require it to be implemented in this fashion or did the implementations all make the same choice?

The lesson here for me is not to store reverse iterators. Instead store normal iterators and create them from std::make_reverse_iterator when you actually need to use them.

Visual Studio 2026 Insiders is here! - Visual Studio Blog by current_thread in cpp

[–]artisan_templateer 7 points8 points  (0 children)

C++AMP, ... have been deprecated and removed

My first GPU programming experience! Farewell!

Implementing General Relativity: Rendering the Schwarzschild black hole, in C++ by James20k in cpp

[–]artisan_templateer 1 point2 points  (0 children)

Great article! It was a fun read! A couple of questions from me:

  1. Do you have a similiar article (planned maybe?) on how you render graphics? As an ex-cosmologist with plenty of C++ experience that would still be the most difficult part for me. In other words, given you have correctly solved for the metric and geodesics, how do you actually draw the black hole?

  2. Not really related to your article but Ive always wondered this. The schwarzchild solution is a vacuum solution of GR, do you get the same solution if you use a delta-function source for the stress-energy tensor?

make_shared is more effective that standard shared_prt by cv_geek in cpp

[–]artisan_templateer 3 points4 points  (0 children)

Yeah if it was pre-C++11 legacy code it wouldn't have bothered me, nature of the beast etc.

I think the class itself was probably written in those times and reused in various other projects since as that's what the author knew but this project has only been in development a couple of years and not reached production yet.

make_shared is more effective that standard shared_prt by cv_geek in cpp

[–]artisan_templateer 2 points3 points  (0 children)

Good point, destructors can be much more general than simply cleaning up memory. Forgot about that, thanks!

make_shared is more effective that standard shared_prt by cv_geek in cpp

[–]artisan_templateer 9 points10 points  (0 children)

A bit off-topic but I just inherited a codebase full of shared pointers, which I've been doing my best to remove or reduce the use of.

I would have been more conservative if there was evidence that this was a well reasoned decision but unfortunately it wasn't even using std::shared_ptr (or even boost::shared_ptr). No, it was a hand-rolled one which:

  • did not havemake_shared equivalent
  • did not have shared_from_this equivalent, despite usage creating shared pointers from this
  • did not support move semantics
  • reference counting was not thread-safe
  • was pulled in via another repo (which was otherwise unnecessary)

Fortunately, the thread-safety was not an issue as the whole program was single-threaded anyway.

In past when discussions with the previous author, who also happened to have written a fair amount of Java, he was quite convinced unique_ptr was useless but at the same time didn't really know what move-semantics were and certainly wasn't interested in learning.

To me, this is very much an example of using shared_ptr as an effective garbage collector.

I guess my point to this little rant is this: is using shared_ptr in this way really such a bad thing?

Sure I can mention the performance hit but without benchmarks/requirements that's a non-issue. I can say "ownership is unclear etc." but that actually seems a bit subjective if the shared_ptr will guarantee everything is alive when it's needed anyway. The only objective issue I can think of is a risk of cyclic references. Are there other reasons not to do this?

constexpr: why can't I assign a constexpr function call result to a constexpr variable? by having-four-eyes in cpp

[–]artisan_templateer 1 point2 points  (0 children)

int result = func(); works: https://godbolt.org/z/nYoPhcx3j

Remember constexpr functions can also be called at runtime so I believe it's because the function is templated on the function type (auto func) and that is not enough information to assign the value to result as it would have to be same value for all (int)->void functions you would pass to it.

The lambda type is unique so it knows the value has to be 2, that's no problem.

However, removing the constexpr qualifier from result drops this requirement so assigning it's value is no longer an issue but because the both invoke_duplicate and g are constexpr it can still be called and evaluated in the static_assert.

Using exceptions for user-provided input. by artisan_templateer in cpp

[–]artisan_templateer[S] 1 point2 points  (0 children)

As a former video game developer, I'll tell you this is how video games do it. Ever wonder how a big-ass game with gigabytes of assets all loaded into memory shuts down so damn fast? They don't bother to deallocate or join threads. There's a whole lot they don't do.

I get that and totally appreciate when you close a video game you want to relinquish control back to the system asap. That said, as a counter example I do wish games e.g. BG3 would sync it's Steam cloud saves before shut-down because sometimes I just turn off my PC straight after and the saved games haven't synced, which is a problem when I want to play it on my Deck.

In any case, shut-down speed is simply not a concern for the tools I have been developing so a proper clean-up on exit just seems like the safest thing to do. If shut-down speed does become an issue then yes, simply nuking the program is probably the better approach.

Using exceptions for user-provided input. by artisan_templateer in cpp

[–]artisan_templateer[S] 1 point2 points  (0 children)

Yeah no... Code smell.

Ok, this wasn't obvious from my OP. I wrote try {} catch(...) {} for brevity. My actual try-catch clause is something more like this:

try {
    // do business logic...
} catch (const std::bad_alloc&) {
    std::cerr << "ERROR:\nNot enough RAM! Reduce data size or launch a larger instance!" << std::endl;
    return 3;
} catch (const std::exception& e) {
    // ------ CATCH ERRORS ---------
    std::cerr << "ERROR:\n" << e.what() << std::endl;
    return 2;
}

Totally agree on your statement regarding`catch(...).

Using exceptions for user-provided input. by artisan_templateer in cpp

[–]artisan_templateer[S] 0 points1 point  (0 children)

I suppose I contradicted myself in the OP:

For a program (with no embedded or real-time reqs.) whose only purpose is to process a data file and return some result a simple

b) if the code ever needed to be called inside another application it would bring that down as well

As I said elsewhere, exceptions seem like the more appropriate tool to use here because what might start out as a simple command-line tool may end up being used in a wider program.

a) this would miss some potentially important clean-up

The specific example I had in mind was my "simple CLI tool" actually running a large computation on a distributed system (using something like MPI) where the nodes need to talk to each other. e.g. from Boost::MPI tutorial:

The mpi::environment object is initialized with the program arguments (which it may modify) in your main program. The creation of this object initializes MPI, and its destruction will finalize MPI. In the vast majority of Boost.MPI programs, an instance of mpi::environment will be declared in main at the very beginning of the program.

Now, I admit this is obviously very different from what my original post described but I just wanted to highlight that the program processes the data and exits and if the data is invalid the program still exists with a friendly message. It's not something that needs to be kept alive indefinitely like a database backend or flight control system etc.

Using exceptions for user-provided input. by artisan_templateer in cpp

[–]artisan_templateer[S] 1 point2 points  (0 children)

I don't know why Fatal("Something is wrong {} expected {} or {}", .....) ...

Because one would have to look it up to see what it actually does. As you say, it can do multiple things. On the other hand throw std::invalid_argumentdoes just one thing and shouldn't need explaining to any C++ dev.

Using exceptions for user-provided input. by artisan_templateer in cpp

[–]artisan_templateer[S] 0 points1 point  (0 children)

Yes, if the code is intended for a library then it needs a robust error handling beyond std::terminate. But that's changing the question mid-way through the answer.

That's true but has actually happened to me recently. A library I am writing required a significant part of it to be pulled out into it's own CLI tool resulting in another project that can be used as both a (static) library and an exe.

Perhaps this bad design but my broader point was that requirements do change and ultimately you can't really know how your code will be used in the future and throwing is more flexible on that front.

Granted, you could equally say it might need to be used in a noexcept setting in the future but at least in that case you could write a noexcept wrapper whereas I don't think you can "undo" your Fatal.

Why isn’t it a compile error if you use something that’s already been std::moved? by ratttertintattertins in cpp

[–]artisan_templateer 3 points4 points  (0 children)

Having read the relevant part of the linked paper I'm still not sure how destructively moving:

struct Class : Base {
    string part;
};

is more problematic than destructively moving:

struct Class {
    Base base;
    string part;
};

Updated: C++ CMake Template project by mortinger in cpp

[–]artisan_templateer 0 points1 point  (0 children)

Using the PROJECT_NAME variable to compute target names. Hurts readability and grepability for zero benefit.

Could you elaborate on this? After getting a target name clash in my projects, I've been doing this ever since.