/r/cpp

Photograph via snooOG

Discussions, articles and news about the C++ programming language or programming in C++.

Discussions, articles, and news about the C++ programming language or programming in C++.

For C++ questions, answers, help, and advice see r/cpp_questions or StackOverflow.


Get Started

The C++ Standard Home has a nice getting started page.

Videos

The C++ standard committee's education study group has a nice list of recommended videos.

Reference

cppreference.com

Books

There is a useful list of books on Stack Overflow. In most cases reading a book is the best way to learn C++.


Show all links

Filter out CppCon links

Show only CppCon links

/r/cpp

280,291 Subscribers

11

It is undefined behavior that allows me to be an engineer.

For context: I am an electronics engineer by education and practice, but I switched to full time software development in 2016.

By far, C and C++ has been most of my career. Dangerous languages perhaps, but languages that know they are dangerous, and document exactly where the sharp edges are located.

However, for about a year and a half, I've been branching out and it appears to me that every single language and technology I look at has behaviors that are not defined. They just don't admit to it.

I opened an issue in docker's documentation because they were missing an explanation for a behavior. Closed as resolved with, paraphrased, "read the repository if you want to know how docker works."

By that standard, C++ doesn't have undefined behavior either! Just read your compiler's source code and you'll know what happens.

Same problem with Microsoft's Azure cloud services. Many guides and tutorials but no authoritative source that explains how Azure works, what each option does, and how they interact - or if there is, it's sufficiently burrowed that I've been unable to find it after searching for a while, and their representatives cannot guide me to it either. Dotnet projects? Same issue again, there's any number of properties you can set for your project and you just have to guess from their name what they will do - and don't get me started on the difficulty of writing provably correct golang.

  • I can sign for the correctness of C/C++ software. I might be wrong because mistakes happen, but those are mistakes, not lies.
  • I cannot sign off on most other software. If I claim that I know something is correct, I am lying. I don't know. I'm guessing. "It worked so far."

I can write software in all these languages, use all these other technologies. I cannot do engineering with them. I wish I could.

11 Comments
2024/05/12
20:04 UTC

53

What's your favorite Boost alternative?

Just looking for discussion. Mainly curious about how y'all feel about Boost and its kin. I'm sure most folks here have experience having to deal with emulating a feature from a future C++ standard, or some other task that might fit in the STL. What's given you the least headaches? Does Boost remain king to y'all?

56 Comments
2024/05/12
01:54 UTC

0

Can neovim provide perfect IDE experience for C++?

So I have recently started with Linux and don't have Visual Studio. There are two options either go with Neovim or use CLion, I have a little experience with Vim as a Editor and was wondering with which one should I continue. My inclination is towards learning Neovim so I was wondering if it would be a right choice to go with it. Can it provide every functionality that an IDE does.

63 Comments
2024/05/11
20:47 UTC

0

The unfortunate state of the C++ standard library.

Is the status quo sustainable? Everything is just getting more and more complex and tons of stuff seems to be undocumented. Two days ago I wanted to do something I thought was simple: Wrap two views as a single view such that it appears as if its concatenated (concats lazily). I look at cppreference and there's std::views::concat, but it's not in C++23. Okay no problem, just use ranges-v3. I try to use the concat in ranges-v3 and after a lot of headaches, a friendly individual from r cppquestions lets me know that ranges-v3 sometimes doesn't play nice with std ranges (apparently it's a well known problem). Okay no problem, just write a custom view that does what you want. Writing the logic outside of a view is only a handful of code, so it probably isn't that hard to wrap it around a view interface and plug it in. I look online for documentation of what a std view should have / some examples and I find nothing but a few surface level blog posts that don't really explain what's happening. Okay no problem, just fire up ChatGPT4 and ask it to give me the skeleton for a C++ range view and tell it what I'm trying to do. It produces some code. I plug it into godbolt and it doesn't work. Okay no problem, just go back and forth with ChatGPT, feeding it the compiler error messages and spot checking for any obvious problems myself. After several hours of going back and forth, still doesn't work. I can't understand why and there's no official documentation that tells me what I'm supposed to implement in terms of an interface. The compiler output just tells me that it can't find a valid begin(...) and no other information. I try testing against the type traits for input_range and it fails, but still doesn't really tell me anything about why it failed. Pages and pages of output that don't really say anything other than "I can't find this for std::ranges::begin()" or something to that effect. After several days of struggling, I go back to r cppquestions and another friendly individual helps me tack on the missing pieces. I ask them if there's any official documentation for this and the answer is "I don't think there's like an official document that explains how all these helper types like view_interface or the new range_adaptor_closure are supposed to be used. The proposals that introduce them are informative, but they're aimed at experts that are already familiar with the inner workings of ranges."

So, I have to rely on the kindness of strangers to get anything non-trivial done? Other languages have abundant documentation on what you need to do to create a new stream / range, complete with well commented examples, right in front of your face as soon as you decide you want to look at it. Why isn't this the case for C++? I just want to write some code "the way it's meant to be written" and so far it's been torture. Add to this a seemingly innumerable number of footguns, a seemingly innumerable number of edge cases, documentation that doesn't seem to exist, half-baked implementations of things in the stdlib across both clang and gcc (MSVC seems to be better with implementing things). More and more the language just seems to be a playground for people who fetishize complexity. It's so byzantine and cryptic that a commercial LLM struggles with it. Is this sustainable? At what point does the ecosystem collapse in on itself from new engineers shying away from it. Rant over.

51 Comments
2024/05/11
19:39 UTC

38

What is the easiest Cpp networking library?

(i mean for very simple lan socket stuff)

59 Comments
2024/05/11
13:09 UTC

46

Interesting projects you can write with 500 to 1000 lines of code?

Looking to do some small projects like a dozen of them in the next 3 weeks. What would you recommend?

49 Comments
2024/05/10
21:53 UTC

18

Qt vs wxWidgets vs GTK recommendation

So I'm thinking of learning a C++ GUI framework now and I wanted to hear people's opinions on which is best. People say QT is great because it's very fast and has lots of features, but the licensing model makes me a bit uneasy. If I had to some day, how much would I be able to do with just LGPL components?

GTK is cross platform, but it seems native look is not a priority for them. This leads to strange inconsistencies in different UIs which might confuse users. Is GTK still the best option for performance reasons? Or does QT have better performance too?

Finally there's wxWidgets. They compile to native toolkits on every platform, so it's probably the best for native look and feel. But is there a performance overhead because of the bindings and platform specific stuff?

What does everyone think?

Edit: I thought of another potential advantage of GTK: It has Rust bindings. I don't think any of the others do.

36 Comments
2024/05/10
21:44 UTC

6

Limited friendship

Although I don't like to do it, sometimes I need to use friend declarations to give private access to external classes/functions. Whenever I do, those external entities only need access to 1 or 2 members of the private class. But, giving friendship opens up the entire insides to the friends. So, it can become difficult to understand which privates get used externally, without inspecting the friends. To try to keep things sane, I segregate those members to their own private section. Best I can do is put comments to say they are used externally.

private: // These are used by friends
    Foo mBar;
    Some Function();
private:
    //... Other private sections are not used by friends.

I wish C++ supported a 'friend:' access specifier in classes, similar to public/protected/private.

friend:
    Foo mBar;
    Some Function();
private:
    // ...

If a class has a 'friend:' section, then those members are consider private, and they can be accessed by friend classes/functions. If such a section is present, the friend classes/functions are not allowed to access other private sections.

It would be nice to have limited friendship.

15 Comments
2024/05/10
19:10 UTC

12

Why isn't there a C++ Collective on StackOverflow?

There are currently 10 Collectives on StackOverflow. In the help on Collectives there is nothing about how to create a Collective, but here is a StackOverflow Meta answer that says to create one, you have to contact the StackOverflow company, and they will create it for you, if they agree with it.

Why isn't there a C++ Collective on StackOverflow? Were there efforts to create it?

32 Comments
2024/05/10
15:57 UTC

8

Seeking Feedback on a New Approach to C++ Event Callback Design

Hey everyone,

I've been playing around with designing an API in C++20 for an internal project and stumbled upon what seems like a fresh approach to handling event callbacks. I'm excited about it but unsure if it's a well-known technique. I'd love to get your thoughts and insights on it.

The Challenge:

Imagine you have a function Process() that needs to keep others in the loop by firing events like Started, Stopped, and Error during its execution.

Approach 1: Classic Runtime Interface

In traditional C++ style, you might set up an interface like this:

struct IObserver
{
    virtual void OnStarted() = 0;
    virtual void OnStopped() = 0;
    virtual void OnError(std::string const& error) = 0;
};

void Process(IObserver& observer) 
{
    observer.OnStarted();
    if (something_went_wrong) {
        observer.OnError("oops, an error occurred!");
    }
    observer.OnStopped();
}

Users would define their observer classes:

struct MyObserver : IObserver {
     // Implement methods...
};

MyObserver observer;
Process(observer);

Approach 2: Modern Compile-Time Observer Pattern

Now, let's modernize things a bit with some compile-time magic:

template<typename T>
concept Observer = requires(T observer, std::string const& error) {
    { observer.OnStarted() } -> std::same_as<void>;
    { observer.OnStopped() } -> std::same_as<void>;
    { observer.OnError(error) } -> std::same_as<void>;
};

void Process(Observer auto& observer) 
{
    observer.OnStarted();
    if (something_went_wrong) {
        observer.OnError("uh-oh, an error!");
    }
    observer.OnStopped();
}

Users still define their observer classes:

struct MyObserver {
     // Implement methods...
};
static_assert(Observer<MyObserver>);

MyObserver observer;
Process(observer);

Approach 3: Fancy-Pants Generic Lambda

But wait, there's more! Let's get even fancier with some generic lambdas:

struct EventStarted{};
struct EventStopped{};
struct EventError{
    std::string const& error;
};

void Process(Observer auto& observer) 
{
    observer(EventStarted{});
    if (something_went_wrong) {
        observer(EventError{"oh no, an error!"});
    }
    observer(EventStopped{});
}

Now, users can create their callbacks on the fly:

Process([]<typename T>(T event) {
    if constexpr (std::is_same_v<T, EventError>) {
        format("error: {}", event.error)
    }
    else if constexpr (std::is_same_v<T, EventStopped>) {
        // Handle stopped event...
    }
    // Ignore the start event.
});

So, what do you all think? Is this fancy generic lambda thing a known trick, or am I onto something new? Any potential downsides I should watch out for? Like are there any potential performance impacts from wrapping multiple parameters into a single event type?

Looking forward to hearing your thoughts,

Qiang

10 Comments
2024/05/10
07:47 UTC

0

Inside STL: All about C++11 thread and C++20 jthread

This article is based on the source code of gcc-13, providing a detailed explanation of the usage and kernel implementation of C++11 std::thread, demonstrating how to use a mix of pthread + thread, and explaining the usage and kernel implementation of C++20 jthread.

Panorama: https://miro.medium.com/v2/resize:fit:4800/format:webp/1*aKTR79IWpoqyPTEblKVh8g.png

article: https://levelup.gitconnected.com/stl-c-11-thread-and-c-20-jthread-everything-you-need-to-know-91146c7b8086

18 Comments
2024/05/10
01:41 UTC

102

Why is dependency management so hard in cpp and is there a better way?

Basically every language I've worked with so far has been ok with dependency management. You need a library, you install that library, you use it. Sometimes there's version conflicts and such, but that's not what I'm talking about here. In Python: pip install; in JS: npm install; in Rust: add it to Cargo.toml; Java: POM File. These are what I worked with, and I know there's other ways to do it in each one, but still, it's basically "hey, I need to use this" "ok". But with cpp, every single time I've had to deal with some dependency it's been a huge effort to try and relearn cmake, or having to go manually after library files, have compilers screaming at me that it can't find something that's RIGHT THERE.

So, what's the deal? Is there something I'm missing here? I really hope so, please let me know how wrong I am.

149 Comments
2024/05/09
16:17 UTC

71

The best delegator yet!

https://gitlab.com/loblib/delegator

I have open-sourced a part of my database that is a superior replacement for `std::function`. It is more performant, easier to understand and much more flexible. It has not been heavily tested yet so bug reports are very welcome! Any other form of contribution as well!

13 Comments
2024/05/09
15:18 UTC

13

Best practice for optimum performance: multidimensional arrays

Hi, I'm a scientific programmer. I primarily use Python and FORTRAN. I have to write a code that involves solving a finite difference equation on multidimensional vector array.

(1) Defining a multidimensional array: The array is electric field with three components (ex, ey, ez) defined at (Nx, Ny, Nz) grid. Where Nx, Ny and Nz are the number of points along X, Y and Z. So if Nx, Ny, and Nz = 10, 9, 8 my array will be [10, 9, 8, 3] shape.

Then I have to define gradient along all the three axes and solve some numerical equations which involves matrix multiplications.

I have done this in Python and it was straight forward using Numpy Ndarrays.

I recently tried porting my code to C++. I realised that defining multidimensional arrays in C++ is not so straightforward.

What is the fastest and easiest method to implement such a multidimensional array in c++?

Any advice is helpful.

32 Comments
2024/05/08
20:38 UTC

56

Raptor - A high-level algorithmic skeleton C++ template library designed to ease the development of parallel programs using CUDA

Hi everyone, I wrote a blog post about a small C++ library I recently wrote called raptor. I thought I'd share it here for anyone interested: https://dma-neves.github.io/dma/raptor.html

Raptor offers similar performance and capabilities to thrust, but offers a more abstract interface. Raptor provides a set of smart containers (vector, array, vector, scalar) and skeletons (map, reduce, scan, filter, sort) that can be applied over the smart containers. Containers can store the data both on the host (CPU) and device (GPU), and expose a seemingly unified memory address space. Skeletons are executed on the device. Containers are automatically and lazily allocated and synchronized whenever a skeleton is executed.

19 Comments
2024/05/08
17:58 UTC

21

Do you use char* or std::string for stream of characters?

I'm aware that std::string is preferred over char*, but is it always the case? For example,

auto str = "Hello world";

"str" type will be a char*, unless we explicitly mention that it's a std::string. So I'm bit confused here the use cases and if there's any downside (in performance) when using std::string over char* (especially for bytes that are will not be modified later).

53 Comments
2024/05/08
15:54 UTC

46

I'm jobhunting and I'm sad I won't be able to use CPP

I'm a recent grad jobsearching. I've never seen an intro-level CPP role, those usually ask for like 5 years of experience. Intro level roles usually ask for Python or React/Typescript.

I do like Python, but I miss the types. As for TS. . . It's really not even close as using something like CPP or Java. Writing JS/TS classes feels like weenie hut jr. compared to classes in CPP.

I hope to work with CPP eventually but I can tell it'll be a while.

38 Comments
2024/05/08
15:52 UTC

24

What is a “senior developer”?

I know the definition of “senior developer” may vary company to company and so on but, according to your definition of senior developer;

  • Which skills should a senior developer have?
  • What would you suggest to a junior/mid developer for their seniority journey?

I am working as an embedded software developer (C++, Yocto, etc). I would be happy if you add additional info about embedded industry.

52 Comments
2024/05/08
11:30 UTC

19

Tina Ulbrich @ StockhlmCpp : Throwing Tools at Ranges

1 Comment
2024/05/08
04:31 UTC

12

Unexplained anomalies and crashes when creating a thread with an infinite loop

Hello everyone, I understand that this issue is really complex and probably involves something terribly wrong somewhere else in the code, but I'm seriously stuck and hoping someone can shed some light on this.

I'm using Ubuntu 22.04, with Clang++.

Ubuntu clang version 14.0.0-1ubuntu1.1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

This code should have no visible effects whatsoever:

std::thread([]()
{
  for(;;);
}).detach();

And yet, if I place it anywhere in my code, it either segfaults, double-calls functions that are only called once, throws an exception from some unexpected destructor (as if it's exiting the nested scope when it isn't), etc.

One example of where I tried this, while trying to isolate the problem, is here:

// static void test()
// {
//     for(;;);
// }

void VoxelverseClient::mainLoop()
{
    //std::thread(test).detach();

    std::thread([]()
    {
        //asm volatile ("int $0x3");
        for (;;);
    }).detach();

    // dispatchTask([]()
    // {
    //     for (;;);
    // });

    // dispatchTask([]()
    // {
    //     for(;;);
    // });

    GraphicsContext context;
    
    Display::navPush<PreloaderDisplay>();

    Uint32 lastUpdateTicks = 0;
    Uint32 nextUpdateTicks = 0;

    SDL_ShowCursor(SDL_DISABLE);
    
    while (running)
    {
        // ...
    }
}

The while(running) loop is not exiting. Notice the `GraphicsContext context;` below; it contains a `AverageTimeTracker` as a field. There are 3 more of those in the global scope:

static AverageTimeTracker fastUpdateTracker;
static AverageTimeTracker updateTracker;
static AverageTimeTracker frameTracker;

If I run this code in release mode, I get `free(): invalid size`. If I run it with AddressSanitizer I get a segmentation fault with the following stack trace:

    #0 0x64213ce695c4 in __asan::Allocator::Deallocate(void*, unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType) (/home/mariusz/Madd/voxelverse/build/voxelverse-client+0x205c4) (BuildId: 52024cb82f3357dd05ff97d403b32badbe8030d4)
    #1 0x64213cf25845 in operator delete(void*) (/home/mariusz/Madd/voxelverse/build/voxelverse-client+0xdc845) (BuildId: 52024cb82f3357dd05ff97d403b32badbe8030d4)
    #2 0x740ea32751b7 in std::__new_allocator<std::_List_node<double> >::deallocate(std::_List_node<double>*, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/new_allocator.h:158:2
    #3 0x740ea32751b7 in std::allocator<std::_List_node<double> >::deallocate(std::_List_node<double>*, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/allocator.h:200:25
    #4 0x740ea32751b7 in std::allocator_traits<std::allocator<std::_List_node<double> > >::deallocate(std::allocator<std::_List_node<double> >&, std::_List_node<double>*, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/alloc_traits.h:496:13
    #5 0x740ea32751b7 in std::__cxx11::_List_base<double, std::allocator<double> >::_M_put_node(std::_List_node<double>*) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_list.h:522:9
    #6 0x740ea32751b7 in std::__cxx11::_List_base<double, std::allocator<double> >::_M_clear() /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/list.tcc:81:4
    #7 0x740ea32751b7 in std::__cxx11::_List_base<double, std::allocator<double> >::~_List_base() /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_list.h:575:9
    #8 0x740ea32751b7 in AverageTimeTracker::~AverageTimeTracker() /home/mariusz/Madd/voxelverse/src/AverageTimeTracker/AverageTimeTracker.hpp:11:7
    #9 0x740ea1edc252  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xdc252) (BuildId: e37fe1a879783838de78cbc8c80621fa685d58a2)
    #10 0x740ea1a94ac2 in start_thread nptl/./nptl/pthread_create.c:442:8
    #11 0x740ea1b2684f  misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

Note #8: The destructor of `AverageTimeTracker`. I don't see ANY reason why that would execute inside the thread that's supposed to just have an infinite loop.
You're probably wondering what happens when I try one of the commented-out sections:

  1. If I create the thread with `std::thread(test).detach();` it prints "Successfully initialized audio" twice, implying that AudioManager::init() is called twice, even though there is only one call to it in the code. Then, the main thread fails to create the SDL window. No idea how that's correlated.
  2. If i use dispatchTask(), it works without issues. dispatchTask() does not create a thread; it instead passes the functor to an existing thread in my async thread pool. But those in turn are created with the c++ threading API, and somehow those don't cause problems.

When I insert the "int $0x3", which seems to be the only way to successfully set the breakpoint inside that thread using GDB, I am able to find where the function is. Forgive me if I'm crazy, but I can't see the infinite loop in here. The code below is compiled in RELEASE mode.

0000000000340c60 <_ZNSt6thread11_State_implINS_8_InvokerISt5tupleIJZN16VoxelverseClient8mainLoopEvE3$_0EEEEE6_M_runEv>:
  340c60: cc                    int3   
  340c61: 66 2e 0f 1f 84 00 00  cs nopw 0x0(%rax,%rax,1)
  340c68: 00 00 00 
  340c6b: 0f 1f 44 00 00        nopl   0x0(%rax,%rax,1)

0000000000340c70 <_ZN18AverageTimeTrackerD2Ev>:
  340c70: 41 56                 push   %r14
  340c72: 53                    push   %rbx
  340c73: 50                    push   %rax
  340c74: 49 89 fe              mov    %rdi,%r14
  340c77: 48 8b 3f              mov    (%rdi),%rdi
  340c7a: 4c 39 f7              cmp    %r14,%rdi
  340c7d: 74 11                 je     340c90 <_ZN18AverageTimeTrackerD2Ev+0x20>
  340c7f: 90                    nop
  340c80: 48 8b 1f              mov    (%rdi),%rbx
  340c83: e8 d8 3b df ff        call   134860 <_ZdlPv@plt>
  340c88: 48 89 df              mov    %rbx,%rdi
  340c8b: 4c 39 f3              cmp    %r14,%rbx
  340c8e: 75 f0                 jne    340c80 <_ZN18AverageTimeTrackerD2Ev+0x10>
  340c90: 48 83 c4 08           add    $0x8,%rsp
  340c94: 5b                    pop    %rbx
  340c95: 41 5e                 pop    %r14
  340c97: c3                    ret    
  340c98: 0f 1f 84 00 00 00 00  nopl   0x0(%rax,%rax,1)
  340c9f: 00 

The `je 340c90` does not branch. The code continues down to `call 134860 <_ZdlPv@plt>`; which I found is "operator delete"!!!

Looking at this further, it looks like _ZN18AverageTimeTrackerD2Ev has something to do with AverageTimeTracker; perhaps the destructor???

But in this case the problem is clear: my function does not contain the infinite loop; it just does "int3", followed by "nop", and then falls through into another function. If I change "int $0x3" to the no-op `mov %rbx, %rbx` I get:

0000000000340c60 <_ZNSt6thread11_State_implINS_8_InvokerISt5tupleIJZN16VoxelverseClient8mainLoopEvE3$_0EEEEE6_M_runEv>:
  340c60: 48 89 db              mov    %rbx,%rbx
  340c63: 66 2e 0f 1f 84 00 00  cs nopw 0x0(%rax,%rax,1)
  340c6a: 00 00 00 
  340c6d: 0f 1f 00              nopl   (%rax)

0000000000340c70 <_ZN18AverageTimeTrackerD2Ev>:
  340c70: 41 56                 push   %r14
  340c72: 53                    push   %rbx
  340c73: 50                    push   %rax
  340c74: 49 89 fe              mov    %rdi,%r14
  340c77: 48 8b 3f              mov    (%rdi),%rdi

And thus it once again looks like the code simply falls through the function, and the loop is nowhere to be seen.

Does anyone have the slightest idea what could be going on here?

18 Comments
2024/05/07
17:45 UTC

46

From a work perspective, what do you think is the most worthwhile direction for C++ to invest in at the moment?

From a work perspective, what do you think is the most worthwhile direction for C++ to invest in at the moment?

82 Comments
2024/05/07
12:19 UTC

Back To Top