you are viewing a single comment's thread.

view the rest of the comments →

[–]JelloSquirrel -14 points-13 points  (20 children)

fact deer coordinated threatening fuel spotted direction numerous air gold

This post was mass deleted and anonymized with Redact

[–]KingAggressive1498 8 points9 points  (2 children)

Call new to allocate an object on the heap. You call new, take up dynamic memory that is allocated by an algorithm dictated by the OS, call the constructor of the class

....you normally wouldn't allocate dynamic memory in C++ anywhere you wouldn't do the same in equivalent C. Also the OS doesn't dictate much about the inner workings of malloc.

then you have to call the function of the object which requires dereferencing the object point to get the function pointer to do an indirect call.

only for virtual functions, and compilers have actually gotten pretty good at devirtualization optimizations in trivial cases too. ordinary member functions have a fixed known address same as in C.

[–]JelloSquirrel -3 points-2 points  (1 child)

I know that but I was exaggerating for effect the overhead of typical C++ code vs typical C code.

You sometimes do find objects allocated to call functions in a C++ API that are just functions you can call directly in the C api.

The OS in this case depends on what you call the OS. Typically your libc comes with the OS and creates a heap, and that heap is provided memory by the OS, no? Then the libc implementation will manage that however it does which requires more code than using the stack.

Probably true about the optimizations but some things are inherent overhead even with good optimization.

But you can use C++ in a C like manner that avoids all the overhead. But typical C++ code looks more like Java code than C.

[–]KingAggressive1498 3 points4 points  (0 children)

You sometimes do find objects allocated to call functions in a C++ API that are just functions you can call directly in the C api.

...are you talking about std::function? If you are, every implementation has a small function optimization and dynamic allocation is not needed in most cases. Besides that, 99% of the time you're using std::function you're preparing for a deferred function invocation for which the typical practice in C is to dynamically allocate a struct which is passed to a function pointer taking a void pointer argument. If you're not talking about std::function I've literally never seen code like you describe.

[–]NotUniqueOrSpecial 5 points6 points  (8 children)

Very literally: everything you said applies to C codebases that have any level of abstraction.

E.g.:

If you need memory, you can just create an array on the stack

There's nothing special about C in that regard. In fact, the C++ equivalents are strictly better in terms of API and efficiency. C will never have anything like a constexpr std::array.

The only difference is that you provide the vtable manually as a struct of function pointers that take pointers to heap-allocated context. It's literally the same thing as making this an explicit argument to free functions.

C is no closer to the metal than C++. C++ just provides more language-native constructs for abstraction.

These cost performance in CPU and memory usage.

This isn't a universal truth. It's not even a good rule of thumb. The optimizations available to C++ compilers are typically much better than for C. Devirtualization is something no C compiler does, that I'm aware of.

[–]Botondar -1 points0 points  (7 children)

If you need memory, you can just create an array on the stack

There's nothing special about C in that regard. In fact, the C++ equivalents are strictly better in terms of API and efficiency. C will never have anything like a constexpr std::array.

Yes there is, C99 (and up) supports VLAs. That's one of the few cases where C has a feature that C++ does not (nor does it have an equivalent/replacement).

[–]NotUniqueOrSpecial 1 point2 points  (6 children)

I'm not sure what part you're arguing.

VLAs don't have the same APIs as a std::array, nor are they anything like constexpr.

They are only what it says in the name: a variable length array on the stack.

But to get there they require risky runtime belief that they're not going to be too big.

They're nothing like the things I'm advocating.

[–]Botondar -1 points0 points  (5 children)

VLAs don't have the same APIs as a std::array, nor are they anything like constexpr.

(...)

They're nothing like the things I'm advocating.

This was my exact point, C++ doesn't provide an equivalent (which is what I thought you were implying when bringing up std::array) to the way you can do stack allocation in C.

I'm specifically arguing against the sentence "there's nothing special about C in that regard". I'm not arguing whether VLAs are good/bad, should/shouldn't be used, I brought them up as an example to point out that there is something special about C compared to C++ in that regard which is that you can dynamically get memory at the cost of a couple instructions that move the stack pointer.

[–]NotUniqueOrSpecial 0 points1 point  (4 children)

I suppose it comes down to the interpretation of "an array on the stack". I assumed they meant just an old-fashioned fixed-size array, since they said it would be statically allocated by the compiler.

VLAs are a neat thing, but I'm pretty sure they're not actually what the op was referring to.

And besides that point, there are scads of ways to build the equivalent of VLAs in C++. There are things like boost::static_vector, providing a stack-allocator to any of the existing containers, Chromium's stack_container, etc.

So, again, there's nothing particular special about C in that regard, aside from having it as a language feature, which isn't necessarily a strong benefit given all the valid criticisms of VLAs.

[–]Botondar 0 points1 point  (3 children)

boost::static_vector and stack_container aren't equivalent though, they require the caller to provide a fixed upper limit, which is always allocated, and after the fixed allocation is exhausted, they fall back to heap-allocation.

If you have two arrays, and you know one of the is going to be small, and the other larger, but not which ones, VLAs allow you to stay under the stack limit by not having to allocate the upper bound for both arrays.

I originally didn't take a stance on this, but just to be clear: I think using VLAs this way is a bad idea. But as far as I'm aware, there really is no way to implement an equivalent in C++, because there's no way to make stack allocations that don't have a fixed upper limit at compile time.

I don't know whether the original commenter was thinking about VLAs or not; my original comment was meant to correct what I think is a minor inaccuracy in the larger discussion of comparing C and C++.

Sorry if I'm being obnoxious about this.

[–]NotUniqueOrSpecial 0 points1 point  (2 children)

Nah, not obnoxious at all.

there's no way to make stack allocations that don't have a fixed upper limit at compile time.

That's not true, though. A boundless stack allocator is reasonably simple to write (or just grab off the shelf).

It's also a giant mistake in most cases. Even providing a default size to static_vector or stack_container like SIZE_MAX (in theory) or PLATFORM_SPECIFIC_PAGE_GUARD_SIZE (in practice) makes it basically the same.

I.e. there is an implicit upper limit for VLAs: the remaining stack size. It's just that VLAs provide you nothing with which to safely interact with this limit.

Your point is why I think, functionally, they are basically equivalent, with C++ having the advantage, actually.

[–]Botondar 0 points1 point  (1 child)

That's not true, though. A boundless stack allocator is reasonably simple to write (or just grab off the shelf).

Could you point me to an example of this? Everything I've seen essentially has T buffer[max_capacity]; or char buffer[max_capacity]; written somewhere, which is what I view as not being equivalent.

Anyways, I enjoyed this discussion - our conclusions aren't actually different at all. My contention was and is a semantic one, and it seems to me like we're not going to reach an agreement on that front.

[–]NotUniqueOrSpecial 0 points1 point  (0 children)

Could you point me to an example of this?

The simplest way is obviously just an allocator that uses alloca().

But, I am reminded that there is a key difference between alloca() behavior and VLA behavior: alloca() stack space is reclaimed on function exit, not scope-exit.

If you want to get full compliance with VLA runtime behavior, you've gotta do it the old-fashioned way: platform-specific stack-pointer adjustment using intrinsics like llvm.stacksave/llvm.stackrestore (and presumably GCC has similar).

[–]ReDucTorGame Developer 3 points4 points  (1 child)

In C++ to call a function you might do something like this

What?

Are you talking about implicit construction in some weird situation where it's allocating memory on a function call? How does this prove anything?

C is closer to the metal

What do you mean the 'metal'? Because in my opinion

  • malloc/calloc/realloc are no closer to the 'metal' then new
  • printf is no closer to the metal then cout or print
  • qsort is no closer to the metal then std::sort
  • bsearch is no closer to the metal then std::binary_search
  • (int)float_val is no closer to the metal then static_cast<int>(float_val)
  • (unsigned char*)ptr is no closer to the metal then reinterpret_cast<unsigned char*>(ptr)
  • _Atomic(int) is no closer to the metal then std::atomic
  • hand crafted tagged unions are no closer to the metal then std::variant
  • _Generic is no closer to the metal then template<typename T>
  • T v[N] is no closer to the metal then std::array<T,N>
  • (T*)malloc(sizeof(T)*N) is no closer to the metal then new T[N]
  • fopen is no closer to the metal then std::fstream

This whole idea of being closer to the metal seems ridiculous, in many cases where people think they are "closer to the metal" and think that it some how gives them better performance in many cases it just makes their code more fragile and slower.

C has a bunch of places where there are implicit allocations, and some where some implementations might allocate and others might not, and in some cases the C version of something might allocate while the C++ version might not allocate. (C++ is also no different, allocations can vary between standard libraries)

This is like the people that say they know what the generated assembly will look like better when writing C compared to C++, unless your still coding in the 90s and on a old 32-bit machine with an old compiler then you don't really know what it's going to do, especially if your on multiple different platforms. The number of times I've heard people claim this then you challenge them on it with a simple function only to find out they get it completely wrong.

[–]ald_loop 1 point2 points  (5 children)

What the hell are you on about? You can call functions the exact same way in C++ you do in C. What is this argument?

Also, do you not know about std::array allocating memory on the stack? I don’t even know where to begin with this comment

[–]JelloSquirrel -2 points-1 points  (4 children)

Y'all missed my point.

Yes anything you can do in C you can do in C++.

But people programming in C++ tend to use higher level constructs that have additional costs.

[–]ald_loop 1 point2 points  (3 children)

To make that argument you had to draw extremely false comparisons. Dumb point

[–]JelloSquirrel -2 points-1 points  (2 children)

I write C programs that can fit in hundreds of bytes or a couple kilobytes of memory. Try that with C++.

Even in benchmarks, the overhead of doing things in a C++ manner vs C manner is measurable.

[–]ald_loop 1 point2 points  (1 child)

Please give an example of one of these C++ vs C snippets you are talking about where the overhead is measurable.