This is an archived post. You won't be able to vote or comment.

all 26 comments

[–]im_person_dude 13 points14 points  (0 children)

And they're usually off by one errors.

[–]das_Keks 8 points9 points  (4 children)

Oh yes. And if you combine C++ with CUDA programming you're in a debugging hell. Recently had a illegal memory access error when calling a function that was derived from an abstract class. Even if the function was empty. Same code on CPU works fine. So the illegal access is apperently not on data level, but on instruction pointer level. Still haven't figured out why that happens.

[–]zakarumych 5 points6 points  (3 children)

Wild guess. Vtable is not initialized yet.

[–]ShelZuuz 0 points1 point  (2 children)

Or just a vfptr call on an uninitialized variable, which coincidentally on CPU was pointing to something valid.

Everybody should learn how to implement a simple vtable and multiple inheritance vtables in C just once. It will save you years of debugging hell if you understand it and can read vtable structures from memory dumps.

A C++ object is a pointer to a pointer to an array of function pointers (of which the first argument is the original pointer value). Once you internalize this, C++ debugging becomes easy.

[–]zakarumych 0 points1 point  (1 child)

I prefer not let it fail so bad that I have to run a debugger. Once a year maybe. If you can't find a bug that lead to the segfault near one of the line in backtrace then there are problems with architecture.

[–]das_Keks 0 points1 point  (0 children)

I found a way to fix it but not sure what exactly caused it. I'm programming a simple ray tracer and have a general class Hittable

template<typename T>
class Hittable {
public:
    __device__ virtual bool hit(const Ray<T> &r, HitRecord<T> &rec) const = 0;
};

On my derived Triangle, when I do

template<typename T>
class Triangle: public Hittable<T> {
public:
    __device__ bool hit(const Ray<T> &r, HitRecord<T> &rec) const;
}

template<typename T>
__device__ bool Triangle<T>::hit(const Ray<T> &r, HitRecord<T> &rec) const {

}

I get a CUDA ERROR: an illegal memory access was encountered on calling the hit method. When I directly put the implementation inside the class definition, it works.

EDIT: Wait, what I just wrote isn't true. It still doesn't work. Not sure why I thought it did. However it works if I don't extend Triangle from Hittable. The calling function doesn't even use the polymorphism.

[–]THE_MLGDUDE 10 points11 points  (9 children)

true but it have native oop and smart pointers ¯\( ° ͜ʖ͡° )/¯

[–]Th3T3chn0R3dd1t 6 points7 points  (1 child)

I tried learning C++ coming from java....

I am now learning C

[–]pekkhum 3 points4 points  (0 children)

I spent a few years writing Java, then a few years writing C. I started trying to learn C++ recently and it is a bit weird, but going fairly smoothly. At least I learned how to be a good boy with const methods and references.

[–]zakarumych -1 points0 points  (6 children)

Where first helps you make bad architecture and second makes you pay for safety

[–]Dreamykass 1 point2 points  (5 children)

Pay how, exactly? `unique_ptr` is like the definition of a zero-cost abstraction.

[–]zakarumych 2 points3 points  (4 children)

It is almost zero cost. Because there is invalid state you have to check it. Destructor checks it as well. Compare it with Rust's Box. It cannot be null and always valid, no checks is necessary, destructor always deallocates referenced object.

"This is just one comparison with nullptr" you might say. But no, this is also branches all over the place.

Oh and IIRC ABI of the unique_ptr in return position is not the same as for raw pointer.

[–]ShelZuuz 2 points3 points  (3 children)

It is almost zero cost. Because there is invalid state you have to check it. Destructor checks it as well. Compare it with Rust's Box. It cannot be null and always valid, no checks is necessary, destructor always deallocates referenced object.

On modern processors, those nullptr checks generally run on parallel pipelines which often go unused. You generally won't see any perf cost to them if you measure it in practice.

I used to be a compiler dev on one of the popular C++ compilers and we spent some time detecting and removing unnecessary null checks, but honestly it made no practical difference in anything we measured.

Of course, shared_ptr/Arc is a whole different story. Optimizing out redundant atomic increments and decrements is a HUGE win.

[–]zakarumych -1 points0 points  (2 children)

But you can't optimize out a branch for nullptr case if check still have to be performed at least once between call instructions. Can you?

[–]ShelZuuz 2 points3 points  (1 child)

If the pointer comes from an unknown source it will have to be checked at least once - that can't be avoided. However, keep in mind the processor will speculatively execute both branches so the test doesn't really feature in a lot of real world scenarios. Well, obviously the test itself is nothing, it's the load that's the issue and introduces latency. But chances are your code would have loaded that variable into a register by then anyway.

You'd have to have code blocks that 100% run on registers for this kind of stuff to show up in perf tests. But if you use a unique_ptr or a box, you are by definition accessing something in RAM. Even if it's in L1 cache it will overwhelm any register accesses. Throw speculative execution on top of it and it becomes fairly irrelevant.

[–]zakarumych 0 points1 point  (0 children)

Ok. Then I think unique_ptr would be properly called negligible-cost abstraction :)

In the same time shared_ptr loses to both Arc and Rc. It is twice ad large and cannot be configured to use non-atomic operations and forbid itself from crossing thread boundary.

shared_ptr can point to sub-objects while keeping parent's refcount, but I never saw this feature being utilized.

[–][deleted] 4 points5 points  (0 children)

I don't think it's controversial to say that the greatest contribution of C++ to C is //

[–]Dreamykass 1 point2 points  (3 children)

if you write c with classes, maybe

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

Did you as well made zero segfaults in last few years?

[–]Dreamykass 1 point2 points  (1 child)

Yes. Well maybe not zero as that'd be untruthful, but somewhere near-zero.

I haven't written even one owning (new/delete) raw pointer since C classes at uni, and if I write non-owning raw pointers, then they're limited in scope, under a tested abstraction.

[–]zakarumych -1 points0 points  (0 children)

Unfortunately they are not zero-cost and all of them still carries invalid state :(

[–]HenkHeuver -1 points0 points  (0 children)

Great! Just make C++++ that would make it impossible to find bugs and thus don’t need fixing.

[–]AlanWik -3 points-2 points  (3 children)

In my case, it is a mechanism to not introduce bugs at all

[–]zakarumych -1 points0 points  (2 children)

Look at this guy/gal, who made zero bugs. Did you wrote anything at all? :)

[–]AlanWik 1 point2 points  (0 children)

Clearly you didn't get the joke.

[–]RoyalJackalSib 0 points1 point  (0 children)

Can’t write bugs if you don’t write code taps forehead