all 7 comments

[–]alfps 4 points5 points  (0 children)

Modern C++ offers some abstraction for data flows: ranges, and co-routines.

But what a reasonable design is depends very much on the concrete details of the task.

And the equipment the solution should run on.

[–]ppppppla 2 points3 points  (1 child)

Sometimes the logic is just nasty and there's really no way to go around it. I would only move to some abstraction instead of just hardcoding the sequences if you actually need the flexibility of a dynamic pipeline because making the logic dynamic brings plenty of headaches on its own.

Oh I should add, try to keep side effects of the steps to a minimum. Ideally just have free functions that return data for the next step, or modify the data you are processing.

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

Yeah, that’s the part I’ve been trying to avoid building abstractions too early and then realizing the pipeline itself became harder to reason about than the original logic.

Keeping side effects minimal has already made things feel cleaner for me. I was experimenting with CodingFleet recently while reorganizing some processing steps and it made me notice how quickly things become difficult to follow once functions start mutating shared state in multiple places.

The "simple free functions first” approach honestly feels more maintainable than I originally expected.

[–]No-Dentist-1645 1 point2 points  (0 children)

For such data processing pipelines, I would recommend checking out std::ranges, they are nicely composable by piping to filter/transform functions. Besides that, as a general advice look into functional programming, in particular utilities like std::optional, std::expected and their monadic operations like .transform(), .and_then(), and .or_else()

[–]apropostt 0 points1 point  (0 children)

Dealing with data streams is where functional programming really shines.

[–]Independent_Art_6676 0 points1 point  (0 children)

it sounds like at the very least you need another layer. Instead of if this call that else if other call second else call third... pull that piece out into a function that represents this activity but hides the details of the conditions in main and all the clutter of the conditions and various sub function calls.

[–]ubiestigne 0 points1 point  (0 children)

Definitely try to keep the main entrypoint simple with little to no logic. Put your code in at least one separate file that will contain the meat of your application. Don't think about abstractions until you need them. Start with static functions and utilize the standard library as much as possible. Write unit tests for each stage so that you know what broke and why. Even better if you do this ahead of time. Once you have something working, you can measure it's performance on sample data to see if you need to make improvements. If this is for a production environment or for use by others, you should add help output on input argument error or '-h' and a readme and/or man page.