VittorOmeo https://vittorioromeo.info/ Program in C++ from basic to expert! Wed, 02 Jul 2025 06:14:43 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.1 https://vittorioromeo.info/wp-content/uploads/2025/02/logo-106x106.jpg VittorOmeo https://vittorioromeo.info/ 32 32 How Not to Get Lost in Learning C++: A Roadmap for Beginners https://vittorioromeo.info/how-not-to-get-lost-in-learning-c-a-roadmap-for-beginners/ Wed, 02 Jul 2025 06:14:43 +0000 https://vittorioromeo.info/?p=267 C++ is a powerful and flexible programming language used in a wide variety of fields, from developing operating systems and games to creating high-load server …

The post How Not to Get Lost in Learning C++: A Roadmap for Beginners appeared first on VittorOmeo.

]]>
C++ is a powerful and flexible programming language used in a wide variety of fields, from developing operating systems and games to creating high-load server applications. If you’ve decided to learn this language, you’ve likely encountered the need to go through a long and sometimes confusing journey. However, with the right roadmap, you can move forward confidently, without losing your bearings or getting lost in the sea of information. This article will help you understand how to stay on track and effectively learn C++.

Why C++ Is an Important Choice for Beginners

For many beginners, choosing a programming language can become a real headache. However, if you want to master not only the basics of programming but also learn to work with high-level tasks, C++ is an excellent choice. The language offers vast opportunities for learning and career growth. Unlike many modern languages, it gives you full control over system resources, which is especially important for fields like game development, system programming, as well as working with algorithms and data structures.
It’s also worth noting that C++ is widely used in other areas, such as graphics development, artificial intelligence, and even online entertainment, like creating platforms for online casinos. Understanding how basic C++ concepts work can be useful even if you want to try your hand at developing highly interactive applications for such domains.

If you’re looking for reliable online casinos with a variety of games from leading global developers, visit slovenskecasino.net. Here you will find only trustworthy platforms offering a wide range of gaming options and exciting opportunities for everyone. Each player can find a casino with attractive conditions and bonus programs that will make your gaming experience even more exciting and profitable.

Steps on the Path to Mastering C++

Master the Basics of Programming

Before diving into C++, it’s important to understand the basic principles of programming. Without this knowledge, it will be hard to comprehend more complex aspects of the language. At this stage, you should familiarize yourself with concepts such as variables, conditional statements, loops, and functions. If you have no experience with other programming languages, start with simple courses or video materials to understand how the programming process works.

Learn C++ Syntax

C++ is known for its complex and flexible syntax. For beginners, it’s important to become familiar with the fundamental constructs of the language, such as operators, functions, classes, and objects. At this stage, you should also study fundamental concepts like pointers, dynamic memory, data structures, and classes. Gradually, you can dive deeper into the intricacies of the syntax: operator overloading, inheritance, and polymorphism.

It’s very important to learn how to work with different data types and basic data structures—arrays, lists, and strings. Understanding their internal structure and features will help you write more efficient and optimized code.

Practice and Writing Your First Code

Once you’ve grasped the basic concepts of C++, it’s time to start practicing. It’s essential not only to read theoretical material but also to try writing code in practice. Start with simple programs like a calculator, a program for sorting numbers, or solving basic mathematical tasks. This approach will help you reinforce your knowledge and see in practice how the basic concepts of the language work.

Solving Problems and Working on Projects

To progress more quickly, start solving problems on platforms such as Codeforces or LeetCode. This will help you learn how to work with algorithms and data structures. Once you feel confident, move on to more complex projects like creating simple games or developing small utilities. Tasks in real projects will require more knowledge and patience from you, but they help you gain experience and develop skills for working with real-world problems.

Understanding Advanced Concepts

Once you’ve mastered the basics, it’s time to move on to more advanced aspects of C++. This is where the real adventure begins! Learning templates, multitasking, working with threads, and a deeper understanding of memory management—these are the areas that will open doors for high-level development.

Templates and Code Generation

Templates are an important and powerful concept in C++ that allows you to write universal functions and classes. Mastering this tool will enable you to create more flexible and efficient code. If you want to work on large projects, studying templates is essential.

Working with Multitasking and Threads

Understanding how multitasking works in C++ is a critical aspect of writing efficient and scalable applications. Parallel computing is becoming more and more in demand, and skills in working with threads and asynchronous code will open new horizons for you.

Resources for Advanced Learning

To successfully master C++, it’s essential to continue learning and deepen your knowledge. There are many online courses, textbooks, and forums where you can ask questions, get help, and find new ideas for your projects. It’s recommended to regularly read articles, books, and blogs written by experienced developers.

Books and Online Courses

If you want to dive deeper into the theoretical part, pay attention to classic books such as “C++ Primer” by Stanley B. Lippman or “Effective C++” by Scott Meyers. These resources will help you not only learn the basics but also understand deeper aspects of the language. Online courses from platforms like Coursera, Udemy, and edX can also be an excellent aid in your learning.

Conclusion

Learning C++ is a long but exciting journey. At first, the language may seem complicated and confusing, but over time, you’ll come to realize its true power and flexibility. Remember that it’s not just about studying theory but also about practicing, solving problems, and working on projects. Use a variety of resources for deeper learning, and don’t be afraid to make mistakes—each step on the path to mastery brings you closer to your goal.

The post How Not to Get Lost in Learning C++: A Roadmap for Beginners appeared first on VittorOmeo.

]]>
C++23 and Beyond: What Innovations Are Truly Needed by the Community? https://vittorioromeo.info/c23-and-beyond-what-innovations-are-truly-needed-by-the-community/ Mon, 23 Jun 2025 10:30:23 +0000 https://vittorioromeo.info/?p=256 C++ is one of the most powerful and versatile programming languages, used in various fields, from system programming to game development. Over the years, the …

The post C++23 and Beyond: What Innovations Are Truly Needed by the Community? appeared first on VittorOmeo.

]]>


C++ is one of the most powerful and versatile programming languages, used in various fields, from system programming to game development. Over the years, the language has continued to evolve, with each new version introducing improvements that aim to make developers’ lives easier, enhance performance, and improve code safety. In this article, we will explore the key innovations in C++23 and the potential future developments of the language.

C++23: Steps Towards Simplifying Development
C++23 is the next version of the language after C++20, which introduced numerous innovations, including concepts, coroutine enhancements, and other significant changes. C++23 also brings a number of updates that are expected to provide real benefits to developers.
One of the most noticeable improvements in C++23 is the support for the std::ranges standard library, which makes working with ranges more convenient and safe. This enhancement helps reduce boilerplate code and improves readability. Additionally, a new way to handle multitasking has been added with extensions for coroutines, allowing for easier management of asynchronous execution.
Another important innovation is the support for algorithms that handle matrices and multidimensional arrays, which will significantly simplify the development of applications using machine learning, image processing, and other scientific computations. It is also worth noting improvements in static and dynamic typing, which make code more reliable and predictable.

The Need for Support of Modern Architectures
One of the current challenges for C++ is optimizing its performance for new processor architectures, such as ARM and RISC-V. It is important that compilers and language standards continue to support these architectures while ensuring high performance and minimal overhead.
In C++23, support has been added for extensions that work with new processors, enabling the utilization of these architectures’ capabilities for more efficient code execution. This update is especially important for developing software for embedded systems and mobile devices, where performance is crucial.
However, improvements do not stop here. In the future, it is essential to continue working on compiler optimizations to ensure that C++ code can fully leverage the capabilities of modern processors, including multitasking and parallelism. This requires the integration of new technologies and approaches in the field of compilation and optimization.

Enhancements in Security
In today’s world, where data and application security is becoming increasingly important, C++ cannot remain indifferent. Despite its power, the language has long been criticized for its memory management issues, which could lead to errors and vulnerabilities. In response to these challenges, C++23 introduces new mechanisms for working with memory and security.
One such improvement is the enhanced handling of safe pointers and automatic memory management in standard libraries. This helps minimize the risks of memory leaks and resource management errors. Another noteworthy change is improvements in multitasking and asynchronous operations, where it is crucial to ensure that concurrently running threads do not lead to unpredictable states in the program.
However, despite significant progress in security, C++ remains a complex language for beginners, especially when it comes to working with pointers and low-level aspects. Therefore, the introduction of new tools and libraries that simplify working with security becomes increasingly important.

Adapting to New Industry Requirements
As technology evolves, the requirements for programming languages change. Software development for cloud services, artificial intelligence, the Internet of Things, and other innovative fields demands new approaches and tools. C++ continues to be an essential tool for high-performance computing, but in order to remain relevant in the long term, it must adapt to new realities.
One of these areas is the development of C++ as a language for parallel and distributed computing. In recent years, libraries and technologies for multitasking have been actively evolving, and C++ is not lagging behind. For example, enhanced support for multitasking and threads in C++23 enables the development of more complex systems with high performance. This is particularly important in artificial intelligence development, where complex computations and processing large datasets require maximum optimization and execution speed.

Challenges and Obstacles in Development
Despite all the positive changes, C++ faces several challenges. One of the main issues is the complexity and high learning curve of the language. Many new features, improvements, and concepts, such as coroutines and concepts, require a deep understanding, which can be a barrier for newcomers.
It is also important to note the difficulties associated with maintaining backward compatibility. C++ is a language with a long history, and many old programs and libraries need to be supported even in newer versions. This imposes limitations on the introduction of radical changes and requires a cautious approach when adding new features.

Conclusion
C++ continues to evolve, and C++23 is an important step toward improving development convenience, security, and performance. The introduction of new standards, such as range handling, better multitasking support, and integration with new architectures, makes the language a more modern and flexible tool for developers. However, in order for C++ to remain relevant in the future, it must continue adapting to the changing demands of the industry and constantly improve its security and performance.

The post C++23 and Beyond: What Innovations Are Truly Needed by the Community? appeared first on VittorOmeo.

]]>
Two C++ Cultures: How the Divide Between Legacy and Modern Code Defines the Future of the Language https://vittorioromeo.info/two-c-cultures-how-the-divide-between-legacy-and-modern-code-defines-the-future-of-the-language/ Mon, 23 Jun 2025 10:28:42 +0000 https://vittorioromeo.info/?p=250 C++ is one of the most powerful and flexible programming languages, used to solve a wide variety of tasks: from systems programming to game development …

The post Two C++ Cultures: How the Divide Between Legacy and Modern Code Defines the Future of the Language appeared first on VittorOmeo.

]]>


C++ is one of the most powerful and flexible programming languages, used to solve a wide variety of tasks: from systems programming to game development and online entertainment, such as online casinos. Developed in the early 1980s, the language has come a long way since then, but today it continues to be divided into two opposing cultures: legacy and modern code. Despite their similarities in some aspects, these two approaches coexist in parallel and, moreover, define the future of C++.

The Divide Between Legacy and Modern Code
For decades, C++ has evolved by adding new features and improvements. However, despite this, the old practices and programming methods, which were established in the language’s early years, continue to have a significant impact on development. This creates a clear divide between how code is written today and how it was written in the past. The divide between legacy and modern code is not only evident in syntax but also in design principles, memory management, and approaches to testing and debugging programs.
Programming in legacy C++ is often associated with direct memory management, the use of pointers, and minimal abstraction. In this context, developers are required to control every aspect of the program’s operation, which demands a high level of knowledge and attention. This, in turn, can lead to difficulties in maintaining and scaling code, especially in large projects like online casino systems, where performance and security are equally important.
Modern C++ is significantly different from what it was decades ago. Modern standards, such as C++11, C++14, C++17, and C++20, introduced new features such as smart pointers, lambda functions, as well as capabilities for multithreading and parallel computing. These features allow developers to write safer and more efficient code, making resource management easier and improving performance. With such improvements, it has become possible to develop more complex systems capable of handling heavy loads, as seen in the world of online gaming, including platforms like online casinos, where maintaining high request processing speed and server stability is crucial.
If you are looking for reliable online casinos with a variety of games from leading global developers, visit novecasino.net. Here you will find only trustworthy platforms offering a wide range of gaming options and exciting opportunities for everyone. Each player can find a casino with attractive terms and bonus programs that will make your gaming experience even more enjoyable and profitable.

Why the Divide Exists
Historically, early versions of C++ lacked advanced mechanisms such as standard libraries for memory management or abstractions for multitasking. Developers were forced to create such solutions manually, leading to a large amount of low-level code. Over time, the language underwent significant changes, and standards like C++11 introduced many new features that significantly simplified development.
However, legacy code written according to C++98 or C++03 standards continues to exist because in many large projects, such as systems supporting online entertainment or other long-term services, updates don’t happen quickly. Transitioning from old code to new can be a complex and expensive process, especially if it requires rewriting major parts of the system. This creates a divide between two cultures: one that uses the old programming style and one that actively uses the new C++ standards.

Impact on the Future of C++
The divide between legacy and modern code has a significant impact on the future of the language. On one side of the divide are the old, time-tested practices that demonstrate high performance, while on the other side are the innovative ideas that allow developers to make code more readable, safe, and efficient. This causes ongoing tension and results in the language remaining at the crossroads of two worlds.
On one hand, there is a need to maintain old systems and avoid completely replacing them, especially in large companies that cannot afford to rewrite everything from scratch. On the other hand, it is equally important that the new features of C++ provide real advantages, including easier multitasking support, improved memory management, and better compatibility with modern architectures.
It is expected that in the future, the language will continue to evolve toward improvements that make programming more convenient and secure, while still maintaining its power and flexibility. This will require finding an optimal balance between maintaining compatibility with legacy code and incorporating new features. For example, in the world of online entertainment, where high performance and stability are required, it is crucial that modern solutions do not conflict with already existing infrastructures while improving their quality.

Outlook for C++ in the Near Future
In the coming years, we can expect even closer integration of modern C++ capabilities with tools for parallel computing, big data processing, and artificial intelligence. This will open new horizons for the development of both large multitasking systems and high-speed applications, such as those used in online casinos.
However, it is important to note that the divide between legacy and modern code will not disappear in the near future. Instead, there will likely be a process of gradual migration, where new standards are slowly introduced into old systems. This will require developers to be able to work with both C++ cultures, as well as a readiness to continuously learn and adapt to changes.
In conclusion, C++ remains one of the most in-demand programming languages, thanks to its power and flexibility. The divide between legacy and modern code is not just a technical problem, but also a challenge for developers, requiring a search for a balance between the stability of old solutions and the advantages of new features. The future of C++ depends on how the community manages this divide and how effectively new standards will be used to solve modern problems.

The post Two C++ Cultures: How the Divide Between Legacy and Modern Code Defines the Future of the Language appeared first on VittorOmeo.

]]>
C++ in the Age of Rust: Competition or Symbiosis? https://vittorioromeo.info/c-in-the-age-of-rust-competition-or-symbiosis/ Mon, 23 Jun 2025 10:27:49 +0000 https://vittorioromeo.info/?p=247 In the world of programming, two of the most popular and powerful languages are C++ and Rust. C++ has been around for several decades and …

The post C++ in the Age of Rust: Competition or Symbiosis? appeared first on VittorOmeo.

]]>
In the world of programming, two of the most popular and powerful languages are C++ and Rust. C++ has been around for several decades and boasts a huge user base, while Rust is a relatively young language, emerging only in 2010. However, despite its “youth,” Rust is quickly gaining popularity among developers, promising a safer and more modern approach to software development. A question that is actively discussed in the developer community is whether C++ and Rust are in competition with each other, or if these two languages can coexist, complementing each other in a symbiotic relationship where each occupies its own niche.

Reasons for C++’s Popularity
C++ was created by Bjarne Stroustrup in 1983 and has since become the foundation for many large-scale software projects. Its key advantages include high performance, flexibility, and deep integration with the system level, allowing developers to create efficient applications across a wide range of fields. From operating systems to video games, from embedded devices to high-performance computing—C++ covers a broad spectrum of use cases.
C++ developers value its ability to precisely control system resources, including memory management. Despite its complexity and the potential for errors such as memory leaks and pointer issues, many consider this language to offer unparalleled power and flexibility.

Rust’s Growing Popularity
Rust, on the other hand, was created with the goal of addressing the issues developers face when using C++. It is a language focused on safety, which is especially important when working with low-level systems. Rust attracts attention with its unique approach to memory management. Unlike C++, where the programmer is responsible for allocating and deallocating memory, Rust employs a system of ownership and borrowing, which avoids issues like memory leaks without the need for a garbage collector. This makes Rust not only safe but also highly performant.
Rust also supports concurrency and parallelism, making it attractive to developers of high-performance applications where speed and safety are priorities. Its unique language features, such as the type system and borrowing mechanism, help minimize the risks associated with developing multi-threaded programs, significantly reducing the chances of errors that are common in multi-threaded C++ applications.

Advantages and Disadvantages of C++ and Rust
One of the most significant advantages of C++ is its maturity and widespread industry support. Many large software systems, including operating systems and high-performance applications, are already written in C++, and migrating to Rust in such cases may be challenging and costly. Additionally, C++ benefits from a vast array of libraries and frameworks, making development easier.
However, C++ also has its drawbacks. One of the major downsides is its complexity and the large number of errors that can occur due to improper memory management. These errors can lead to memory leaks, program crashes, or even security vulnerabilities. Despite the availability of modern frameworks and debugging tools, C++ still requires a high level of attention and expertise from its developers.
Rust, on the other hand, addresses many of these issues, particularly in the areas of safety and memory management. Its type system and ownership model allow the compiler to guarantee safety at the code level, significantly reducing the likelihood of issues like memory leaks. However, despite all of its advantages, Rust is not yet as widely adopted in the industry as C++. This can create barriers to adopting the language in existing projects that already use C++, as well as challenges in training developers to switch to a new language.

Competition or Symbiosis?
Whether C++ and Rust are competitors or can work in symbiosis depends on the specific goals and tasks at hand. If viewed as competitors, one could argue that Rust offers a safer and more modern approach to development, providing developers with solutions to problems that C++ programmers face, such as memory management and concurrency. However, at the same time, C++ still plays a crucial role in the programming world, thanks to its maturity and extensive library support.
On the other hand, one could argue that C++ and Rust can coexist symbiotically, occupying different niches. For example, in high-performance applications and systems that require resource control and maximum performance, C++ may continue to be the primary tool. However, in new projects where safety and ease of memory management are paramount, Rust may be the preferred choice.
Moreover, there are many projects where both languages can be used. For instance, parts of a system may be written in C++, while other parts may be written in Rust, leveraging the strengths of each language. In such cases, developers can combine C++’s speed and flexibility with Rust’s safety and modern features, leading to better outcomes.

Conclusion
Thus, while C++ and Rust may compete in some areas, their symbiosis seems much more likely. C++ remains a key tool for developing high-performance applications, particularly in cases where resource control is necessary. Rust, meanwhile, draws developers with its safety and ease of working with memory. Both languages have unique strengths and can be used depending on the specific needs of a project. Ultimately, the choice between C++ and Rust depends on the tasks at hand and which approach will be most effective in solving those problems.

The post C++ in the Age of Rust: Competition or Symbiosis? appeared first on VittorOmeo.

]]>
Quake VR: Classic Shooter in Virtual Reality https://vittorioromeo.info/quakevr/ Sat, 04 Jan 2025 16:17:00 +0000 https://vittorioromeo.info/?p=188 Quake VR is a unique opportunity to immerse yourself in the legendary world of the classic 1996 shooter with full virtual reality support. Thanks to …

The post Quake VR: Classic Shooter in Virtual Reality appeared first on VittorOmeo.

]]>
Quake VR is a unique opportunity to immerse yourself in the legendary world of the classic 1996 shooter with full virtual reality support. Thanks to fan modifications and enthusiasts, the iconic game from id Software has been given new life in the VR format, offering players a whole new level of immersion.

History and Evolution of Quake VR

The original Quake was a revolutionary FPS that set standards for the genre, such as fully three-dimensional levels, fast gameplay, and networked multiplayer. With the release of VR devices, enthusiastic developers created modifications to adapt the game for VR helmets. One of the most popular versions was Quake VR based on Quake 1 Darkplaces Engine, supporting Oculus Quest, Valve Index, HTC Vive and other devices.

Quake VR features

  • Full support for VR controls – free movement system, teleportation and the ability to use controllers as weapons.
  • 3D depth effect – a sense of real scale of levels, monsters and environments.
  • Original atmosphere and improved graphics – support for HD textures, dynamic lighting and anti-aliasing.
  • Mod support – the ability to add new levels, weapons and effects.
  • Co-op and multiplayer – some builds allow you to play in VR with friends.

Is Quake VR worth playing?

If you are a fan of classic shooters and are looking for new emotions from a time-tested game – Quake VR is definitely worth a try. It’s a unique opportunity to travel back to the 90s, but with a modern level of immersion that makes the game even more exciting and dynamic.

Have you tried Quake in VR yet? Share your impressions!

The post Quake VR: Classic Shooter in Virtual Reality appeared first on VittorOmeo.

]]>
Embracing Modern C++ Safely https://vittorioromeo.info/emcpps.html Fri, 11 Oct 2024 19:37:00 +0000 https://vittorioromeo.info/?p=167 “Embracing Modern C++ Safely” is a guide to using the new C++11 and C++14 features effectively, avoiding common mistakes and potential risks. Drawing on years …

The post Embracing Modern C++ Safely appeared first on VittorOmeo.

]]>
“Embracing Modern C++ Safely” is a guide to using the new C++11 and C++14 features effectively, avoiding common mistakes and potential risks.

Drawing on years of experience with large mission-critical projects, four leading C++ experts have categorized the language’s features into three groups:

  • Safe – easy to use, difficult to make mistakes in their application, and provide significant value.
  • Conditionally safe – have high value but require deep understanding and experience to use safely.
  • Dangerous – have an unfavorable risk-to-benefit ratio, easily lead to errors, and are justified only in rare specialized cases.

This book gathers the C++ community’s experience with C++11/14 features and helps you make informed decisions that consider the real balance between safety, efficiency, and complexity in large-scale software projects. The author team has analyzed real code bases to objectively demonstrate the pros and cons of each innovation.

After reading this book, you will be able to:

  • Understand how the key features of C++11/14 work and in which situations it is best to apply them.
  • Avoid critical errors and take into account non-trivial nuances of the language.
    Identify features that require additional training, experience, and collective review.
  • Develop coding standards and style guides appropriate for your team.
  • Competently and incrementally incorporate modern C++ features into existing projects.

The book will be useful for experienced C++ developers, team leaders, and technical managers who want to improve productivity, code quality, and long-term code maintenance.

Translated with DeepL.com (free version)

The post Embracing Modern C++ Safely appeared first on VittorOmeo.

]]>
Continuations Without Memory Allocation https://vittorioromeo.info/index/blog/zeroalloc_continuations_p0.html Sat, 27 Jul 2024 17:09:00 +0000 https://vittorioromeo.info/?p=158 One of the main advantages of future (or promise, depending on the language used) is the ability to compose them via asynchronous continuations. Consider an …

The post Continuations Without Memory Allocation appeared first on VittorOmeo.

]]>
One of the main advantages of future (or promise, depending on the language used) is the ability to compose them via asynchronous continuations. Consider an example:

// pseudocode
auto f = when_all([]{ return http_get(“cat.com/nicecat.png”); }
[]{ return http_get(“dog.com/doggo.png”); })
.then([](auto p0, auto p1)
{
send_email(“mail@grandma.com”, combine(p0, p1))
});

f.execute(some_scheduler);

In this example, HTTP requests can be executed in parallel (depending on the scheduler’s operation, such as thread pooling). Once both requests are completed, a lambda function is automatically called with their results.

This model is convenient because it allows us to define oriented acyclic graphs of asynchronous computations in a clean and clear syntax. This is why the std::future standard is evolving to support .then and when_all. These features are included in the N4538 “Extensions for concurrency” proposal and were excellently presented by Anthony Williams in his talk “Concurrency, Parallelism and Coroutines” at ACCU 2016.

Hidden overhead

Consider the std::experimental::future::then signature:

template
auto std::experimental::future::then(F&&& func)
-> std::experimental::future< std::result_of_t(std::experimental::future)>
>;

The return type is again std::experimental::future, which means that the continuation is typed via future. A possible implementation of then might look like this:

template
auto then(std::experimental::future& parent, std::launch policy, F&&& f)
-> std::experimental::future< std::result_of_t&)>
>
{
return std::async(std::launch::async, [
parent = std::move(parent),
f = std::forward(f)
]
{
parent.wait();
return std::forward(f)(parent);
});
}

The use of std::async and type erasure hint at possible allocations. Let’s evaluate their value.

I wrote five .cpp files, using boost::async and boost::future, where I created a chain of .then continuations starting at zero and ending at four. For example:

// zero continuations
int main()
{
return boost::async([]{ return 123; }).get();
}
// one continuation
int main()
{
return boost::async([] { return 123; })
.then([](auto x) { return x.get() + 1; })
.get();
}

Alternative design

Instead of erasing types, you can preserve them by avoiding allocations and retaining the ability to compose asynchronous computations. Consider the following variant:

int main()
{
auto f = initiate([]{ return 1; })
.then([](int x){ return x + 1; })
.then([](int x){ return x + 1; });

f.execute(/* some scheduler */);

}

With a fully synchronous scheduler:

struct synchronous_scheduler
{
template
decltype(auto) operator()(F&&& f) const
{
return f();
}
};

The compiler generates only 2 lines of assembly code! And with std::async scheduler:

struct asynchronous_scheduler
{
template
decltype(auto) operator()(F&&& f) const
{
auto fut = std::async(f);
return fut.get();
}
};

891 lines of code are generated – but that number doesn’t grow when you add extensions, since the compiler can inline calls!

Implementation

Function initiate:

template
auto initiate(F&&& f)
{
return node{std::forward(f)}
}

Template class node:

template
struct node : F
{
template
node(FFwd&&& f) : F{FWD(f)} {}
template <typename FThen>
auto then(FThen&&& f_then);

template <typename Scheduler>
decltype(auto) execute(Scheduler&& s) &
{
return s(*this);
}

};

Implementation .then:

template
auto then(FThen&&& f_then)
{
return node{[
parent = std::move(*this),
f_then = FWD(f_then)
]() mutable -> decltype(auto)
{
return f_then(static_cast(parent)());
}};
}

This approach eliminates allocations and simplifies compiler optimization. In the next article we will analyze when_all and thread pooling.

Thank you for your attention!

The post Continuations Without Memory Allocation appeared first on VittorOmeo.

]]>
Capturing Perfectly-Forwarded Objects in Lambdas https://vittorioromeo.info/index/blog/capturing_perfectly_forwarded_objects_in_lambdas.html Mon, 08 Jul 2024 04:27:00 +0000 https://vittorioromeo.info/?p=161 Perfect forwarding is a powerful feature in C++ that allows template functions to retain the lvalue/rvalue nature of their arguments. This helps prevent unnecessary copies …

The post Capturing Perfectly-Forwarded Objects in Lambdas appeared first on VittorOmeo.

]]>
Perfect forwarding is a powerful feature in C++ that allows template functions to retain the lvalue/rvalue nature of their arguments. This helps prevent unnecessary copies and enables reference semantics without requiring multiple overloads. However, capturing perfectly-forwarded objects in lambdas can lead to unexpected results if not handled correctly.

This article explores the nuances of perfect forwarding within lambda captures, identifying potential pitfalls and presenting an elegant solution.

What We’ll Cover:

  • Why using std::forward in lambda captures can cause unexpected behavior.
  • Implementing a wrapper to correctly handle perfect forwarding in lambdas.
  • Using macros to reduce verbosity.
  • Extending the solution to variadic argument packs.

The Problem

Consider the following struct definition:

struct A
{
    int _value{0};
};

Now, let’s define a lambda function:

auto foo = [](auto& a)
{
    return [&a]{ ++a._value; };
};

Using foo with an instance of A behaves as expected:

A my_a;
foo(my_a)();
std::cout << my_a._value << "\n"; // Prints `1`

Now, let’s generalize foo to use perfect forwarding:

auto foo = [](auto&& a)
{
    return [a = std::forward<decltype(a)>(a)]() mutable
    {
        ++a._value;
        std::cout << a._value << "\n";
    };
};

Unexpected Behavior

While this works for rvalue references:

auto l_inner = foo(A{});
l_inner(); // Prints `1`
l_inner(); // Prints `2`

It fails for lvalue references:

A my_a;
auto l_inner = foo(my_a);
l_inner();
l_inner();
std::cout << my_a._value << "\n"; // Prints `0` (unexpected)

Why Does This Happen?

The issue lies in how lambda captures work. When we use a = std::forward<decltype(a)>(a), a is always captured as a value, not a reference. This means mutations inside the lambda do not affect the original object.

The Solution: A Capture Wrapper

To ensure the correct behavior, we introduce a wrapper class that can store either a reference or a value, depending on how it is initialized.

Implementing fwd_capture_wrapper

template <typename T>
struct fwd_capture_wrapper : impl::by_value<T>
{
    using impl::by_value<T>::by_value;
};

// Specialized version for references

template <typename T>
struct fwd_capture_wrapper<T&> : impl::by_ref<T>
{
    using impl::by_ref<T>::by_ref;
};

The implementation details of by_value and by_ref are:

template <typename T>
class by_value
{
private:
    T _x;

public:
    template <typename TFwd>
    by_value(TFwd&& x) : _x{std::forward<TFwd>(x)} {}

    auto& get() & { return _x; }
    const auto& get() const& { return _x; }
    auto get() && { return std::move(_x); }
};

For references, we use std::reference_wrapper to avoid issues with copying:

template <typename T>
class by_ref
{
private:
    std::reference_wrapper<T> _x;

public:
    by_ref(T& x) : _x{x} {}

    auto& get() & { return _x.get(); }
    const auto& get() const& { return _x.get(); }
    auto get() && { return std::move(_x.get()); }
};

We define a helper function for easy usage:

template <typename T>
auto fwd_capture(T&& x)
{
    return fwd_capture_wrapper<T>(std::forward<T>(x));
}

Updating foo

auto foo = [](auto&& a)
{
    return [a = fwd_capture(std::forward<decltype(a)>(a))]() mutable
    {
        ++a.get()._value;
        std::cout << a.get()._value << "\n";
    };
};

This ensures correct behavior for both lvalue and rvalue references.

Reducing Noise with Macros

Using std::forward and fwd_capture explicitly can be cumbersome. We define a macro:

#define FWD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)
#define FWD_CAPTURE(...) fwd_capture(FWD(__VA_ARGS__))

Now, we can simplify foo:

auto foo = [](auto&& a)
{
    return [a = FWD_CAPTURE(a)]() mutable { /* ... */ };
};

Supporting Variadic Argument Packs

If foo takes multiple arguments, capturing them correctly requires additional handling:

auto foo = [](auto&&... xs)
{
    return [xs_pack = std::make_tuple(FWD_CAPTURE(xs)...)]() mutable
    {
        std::apply([](auto&&... xs)
        {
            ((++xs.get()._value, std::cout << xs.get()._value << "\n"), ...);
        }, xs_pack);
    };
};

This allows foo to handle multiple perfectly-forwarded arguments while maintaining reference semantics.

A Simpler Alternative

An even simpler approach is to directly use std::tuple as the wrapper:

template <typename... Ts>
auto fwd_capture(Ts&&... xs)
{
    return std::tuple<Ts...>(FWD(xs)...);
}

Accessing the captured values requires std::get:

template <typename T>
decltype(auto) access(T&& x)
{
    return std::get<0>(FWD(x));
}

This approach achieves the same goal with significantly less boilerplate.

Conclusion

Capturing perfectly-forwarded objects in lambdas requires careful handling to maintain the correct reference semantics. Using a wrapper like fwd_capture_wrapper or leveraging std::tuple provides an effective solution. Macros help reduce verbosity, and extending the approach to variadic arguments ensures flexibility.

By understanding these nuances, C++ developers can write more robust and efficient generic code when working with lambda captures and perfect forwarding.

The post Capturing Perfectly-Forwarded Objects in Lambdas appeared first on VittorOmeo.

]]>
Therese Cameron https://vittorioromeo.info/therese-cameron/ Sat, 06 Jul 2024 18:30:00 +0000 https://vittorioromeo.info/?p=93 “At first I was a bit afraid to learn C++ because I thought it was too difficult. But thanks to the course on VittorOmeo, I …

The post Therese Cameron appeared first on VittorOmeo.

]]>
“At first I was a bit afraid to learn C++ because I thought it was too difficult. But thanks to the course on VittorOmeo, I realized that programming is accessible if you approach it in the right way. The course was so clear that I started writing programs myself and improving them. I’m very glad I started with this course!”

The post Therese Cameron appeared first on VittorOmeo.

]]>
Zero-Overhead C++17 Currying & Partial Application https://vittorioromeo.info/index/blog/cpp17_curry.html Wed, 03 Jan 2024 20:49:00 +0000 https://vittorioromeo.info/?p=173 Modern C++ standards introduce powerful features that enable functional programming patterns to flourish. Among these, currying and partial application stand out as particularly useful techniques. …

The post Zero-Overhead C++17 Currying & Partial Application appeared first on VittorOmeo.

]]>
Modern C++ standards introduce powerful features that enable functional programming patterns to flourish. Among these, currying and partial application stand out as particularly useful techniques.

In this article, we will:

  • Explain the concepts of currying and partial application.
  • Implement a generic constexpr zero-overhead curry function in C++17.
  • Analyze the generated assembly to demonstrate the lack of overhead.

Understanding Currying

Currying is a technique in which a function that takes multiple arguments is transformed into a sequence of functions, each taking a single argument.

Example: Non-Curried vs. Curried Function

A simple function with three parameters:

auto add3(int a, int b, int c) {
    return a + b + c;
}
add3(1, 2, 3); // Returns 6.

A curried version:

auto curried_add3(int a) {
    return [a](int b) {
        return [a, b](int c) {
            return a + b + c;
        };
    };
}
curried_add3(1)(2)(3); // Returns 6.

Benefits of Currying

Currying allows for incremental argument binding, improving readability and reducing redundancy:

auto add2_one = curried_add3(1);
auto add1_three = add2_one(2);

add1_three(3); // Returns 6.
add1_three(4); // Returns 7.

This can be useful in practical scenarios such as filtering or searching through a collection:

std::vector<std::string> names{/* ... */};

auto find_in_names = curried_find(std::begin(names))(std::end(names));
auto jack = find_in_names("Jack");
auto rose = find_in_names("Rose");

Understanding Partial Application

Partial application refers to fixing a subset of a function’s arguments, returning another function with a reduced number of arguments.

Example: Partial Application

partial_add3(1, 2, 3);  // Returns 6.
partial_add3(1)(2, 3);  // Returns 6.
partial_add3(1, 2)(3);  // Returns 6.
partial_add3(1)(2)(3);  // Returns 6. (Currying!)

This can be implemented in C++17 using recursion, variadic templates, if constexpr, and fold expressions:

template <typename... Ts>
auto partial_add3(Ts... xs) {
    static_assert(sizeof...(xs) <= 3);

    if constexpr (sizeof...(xs) == 3) {
        return (0 + ... + xs);
    } else {
        return [xs...](auto... ys) {
            return partial_add3(xs..., ys...);
        };
    }
}

Implementing a Generic curry Function

We aim to write a curry function that:

  • Enables currying and partial application for any function object.
  • Is constexpr-friendly when applicable.
  • Introduces zero overhead compared to manual implementations.

Declaration

template <typename TF>
constexpr decltype(auto) curry(TF&& f);

The return type decltype(auto) ensures that the final function call retains the exact return type of the original function.

Definition

template <typename TF>
constexpr decltype(auto) curry(TF&& f) {
    if constexpr (std::is_invocable_v<TF>) {
        return std::forward<TF>(f)();
    } else {
        return [xf = std::forward<TF>(f)](auto&&... partials) mutable constexpr {
            return curry([
                partial_pack = std::tuple{std::forward<decltype(partials)>(partials)...},
                yf = std::move(xf)
            ](auto&&... xs) constexpr -> decltype(auto) {
                return std::apply([&yf](auto&&... ys) -> decltype(auto) {
                    return std::invoke(yf, std::forward<decltype(ys)>(ys)...);
                }, std::tuple_cat(partial_pack, std::tuple{std::forward<decltype(xs)>(xs)...}));
            });
        };
    }
}

How It Works

  1. The base case executes the function when all required arguments are present.
  2. Otherwise, the function returns a lambda that captures partial arguments and recursively calls curry.
  3. Arguments are stored in a tuple and applied using std::apply.

Performance Analysis

To verify that curry introduces no overhead, we compare generated assembly for direct calls vs. curried calls.

Benchmark Code

constexpr auto sum = [](auto a, auto b, auto c, auto d, auto e, auto f, auto g, auto h) constexpr {
    return a + b + c + d + e + f + g + h;
};

constexpr auto expected = sum(0, 1, 2, 3, 4, 5, 6, 7);

constexpr auto s0 = curry(sum)(0, 1, 2, 3, 4, 5, 6, 7);
constexpr auto s1 = curry(sum)(0)(1, 2, 3, 4, 5, 6, 7);

Results

Optimization LevelBaseline (Lines)curry (Lines)Overhead
O014140%
O1220%
O2220%
O3220%
Ofast220%

No additional overhead is introduced when using curry.

Compiler Bugs & Issues

While curry is efficient, it exposes some compiler limitations:

  • Some versions of g++ crash with internal errors.
  • clang++ has issues handling the recursion depth.

These issues have been reported:

  • GCC bug #78006
  • Clang bug #31435

Conclusion

C++17 allows for a zero-overhead, generic curry implementation that supports both currying and partial application. The assembly analysis confirms that modern compilers optimize curry effectively. Despite some compiler issues, curry is a powerful tool for functional programming in C++.

The post Zero-Overhead C++17 Currying & Partial Application appeared first on VittorOmeo.

]]>