Live data from Hacker News

Show HN: Embedded Ring Buffer C++98

github.com

21–30 of 41 posts

Re: Show HN: Embedded Ring Buffer C++98

#21
post #16

Earlier quoted context omitted.

Assuming pre-C++20 semantics, which were anyway compiler specific. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p115...

Do you know which of these proposed changes is actually happening? I don't think all of them are, right?

You can find the C++ draft by searching for "n4861", if you're not the kind of person who wants to pay for (or has institutional access to) the final version of the spec.

The draft lists ++ / += of volatile deprecated, lists volatile function parameters and return types as deprecated, but does not mention deprecation of volatile member functions (or I didn't find it).

Keep in mind that the standard does change between draft and finalization, and I've been bitten by this before (one draft of C is missing library functions present in the final standard).

Re: Show HN: Embedded Ring Buffer C++98

#22

Earlier quoted context omitted.

Do you know which of these proposed changes is actually happening? I don't think all of them are, right?

You can find the C++ draft by searching for "n4861", if you're not the kind of person who wants to pay for (or has institutional access to) the final version of the spec. The draft lists ++ / += of volatile deprecated, lists volatile function parameters and return types as deprecated, but does not mention deprecation of volatile member functions (or I didn't find it). Keep in mind that the standard does change betwee…

Thanks!

Re: Show HN: Embedded Ring Buffer C++98

#23
volatile does not imply compiler fence on gcc, so this code has a race condition when updating read/write_position, you need compiler barriers here

>data[write_position] = value;

>COMPILER_BARRIER(); // __asm__ volatile("":::"memory");

>write_position = (write_position + 1U) % LENGTH;

and same in Skip()

Re: Show HN: Embedded Ring Buffer C++98

#24
post #16

Earlier quoted context omitted.

Assuming pre-C++20 semantics, which were anyway compiler specific. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p115...

With one exception, the relevant details aren't really compiler-specific, and C++20 keeps the relevant parts... if you perform two operations on volatile values, the operations can't be reordered, because the spec says so. > Accesses through volatile glvalues are evaluated strictly according to the rules of the abstract machine. Not the best, clearest, most useful part of the C++ spec but the core concept is there...…

Thanks for putting it better than my short remark.

As addition, some of the complaints regarding the new changes might be addressed in C++23.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p213...

Re: Show HN: Embedded Ring Buffer C++98

#25
post #7

Earlier quoted context omitted.

Ouch, yeah. They need std::fill(). This is the kind of thing that makes you lose faith in a C++ library. (Also, what's with the volatile private variables?!)

The use of volatile is typical here. It allows the ring buffer to be used from interrupts, as long as you have one reader and one writer at a time. I haven't checked the code for correctness, but in a typical ring buffer implementation intended to be used in interrupts, you would make the read and write pos volatile. To write, you put the value in the array, and then advance the write position. To read, you copy a va…

But the ”writing” bool in the example in the README is not declared volatile. A bit weird...

Re: Show HN: Embedded Ring Buffer C++98

#26
post #2

Looking for CC, advice and bugs!

One thing I found useful is instead of

  volatile T member;

  T access( void ) { return member; }
Use volatile on the functions instead:

  T member;

  T access( void ) volatile { return member; }
This has causes all access to this-> within the function to be volatile, including read_position, write_position, overrun_flag and data[].

When inlining these functions within other parts of the code, the compiler can hoist reads into registers from the non-volatile members unless you add the compiler barrier that @ghhhhhk8899jj mentioned.

Re: Show HN: Embedded Ring Buffer C++98

#27

Earlier quoted context omitted.

The use of volatile is typical here. It allows the ring buffer to be used from interrupts, as long as you have one reader and one writer at a time. I haven't checked the code for correctness, but in a typical ring buffer implementation intended to be used in interrupts, you would make the read and write pos volatile. To write, you put the value in the array, and then advance the write position. To read, you copy a va…

Unfortunately the compiler can still reorder nonvolatile operations across volatile ones, so unless the buffer is also volatile, it is not as useful as one would expect.

[deleted]

Re: Show HN: Embedded Ring Buffer C++98

#28

Earlier quoted context omitted.

Do you know which of these proposed changes is actually happening? I don't think all of them are, right?

You can find the C++ draft by searching for "n4861", if you're not the kind of person who wants to pay for (or has institutional access to) the final version of the spec. The draft lists ++ / += of volatile deprecated, lists volatile function parameters and return types as deprecated, but does not mention deprecation of volatile member functions (or I didn't find it). Keep in mind that the standard does change betwee…

The latex sources of the C++ standard are on github

https://github.com/cplusplus/draft/tree/c+%2B20

I assume that the C++20 branch actually contains the final version, but you'll have to generate the pdf yourself.

Re: Show HN: Embedded Ring Buffer C++98

#29

Don't use "NULL" in C++[1]. [1] https://cpp4arduino.com/2018/10/26/why-cpp-programmers-dont-...

What else then? In c++11 you would of course use nullptr_t, but the OP code is pre-c++11. GCC used to use a magic builtin in pre C++11 to implement NULL, but IIRC they removed it as non-conforming.

[deleted]

Re: Show HN: Embedded Ring Buffer C++98

#30
post #7

Earlier quoted context omitted.

Ouch, yeah. They need std::fill(). This is the kind of thing that makes you lose faith in a C++ library. (Also, what's with the volatile private variables?!)

The use of volatile is typical here. It allows the ring buffer to be used from interrupts, as long as you have one reader and one writer at a time. I haven't checked the code for correctness, but in a typical ring buffer implementation intended to be used in interrupts, you would make the read and write pos volatile. To write, you put the value in the array, and then advance the write position. To read, you copy a va…

> The use of volatile is typical here.

This is definitely true. C++ programmers typically do this.

> It allows the ring buffer to be used from interrupts

Unfortunately "allows" here is telling us about the programmers not the hardware. The programmers see this and figure eh, I don't really understand this but somebody wrote "volatile" so I guess they knew what they were doing.

> Volatile ensures that if a read is interrupted by a write or vice versa, the entire operation is still atomic

If you want atomic operations you need to use atomic operations not volatile ones. C++ 98 doesn't provide standardized atomics, so you would need to find out on each target what (if anything) you're required to do to get atomic behaviour.

The volatile keyword turns accesses into explicit memory reads and writes. This is what you need if you're a device driver, because your "memory" access might really not be to RAM at all. If the compiler elides a series of repeating writes to the CGA card because they don't seem to be needed after analysing the program, the effect is that the screen is blank and the program's purpose was not fulfilled.

This keyword does not mean "I want a Sequentially Consistent memory model across all my code, except somehow still very fast". That's not a thing.

Volatile accesses for things that are clearly just RAM are a code smell. As a result the volatile keyword in C and C++ is usually a code smell.

Post reply on HN