C++ map quick example

Here’s a quick example of using a C++ map:

#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> grades;
    grades["John"] = 85;
    grades["Jane"] = 92;
    grades["Jim"] = 78;

    std::cout << "John's grade: " << grades["John"] << std::endl;
    std::cout << "Jane's grade: " << grades["Jane"] << std::endl;
    std::cout << "Jim's grade: " << grades["Jim"] << std::endl;

    return 0;
}

In this example, we use a std::map to store the grades of three students (John, Jane, and Jim). Each student’s name is used as a key to look up their corresponding grade. The code uses the [] operator to insert new grades into the map and to look up existing grades.

When we run this program, it outputs the following:

John’s grade: 85

Jane’s grade: 92

Jim’s grade: 78

This demonstrates how a std::map can be used to store key-value pairs and look up values based on keys. It’s a powerful and flexible container that can be used in many applications.

How to read long compiler outputs

Reading long compiler outputs can be overwhelming and time-consuming, but there are several steps you can take to make it easier:

  1. Scan for error messages: Look for the word “error” in the output, as this indicates a problem that needs to be fixed. Start by fixing the first error, as it may resolve subsequent errors.
  2. Look for error messages that are repeated: If the same error message is repeated multiple times, it may be easier to resolve all instances of the error at once.
  3. Locate the file and line number of the error: The compiler will usually provide the name of the file and line number where the error occurred. This information can be used to quickly locate the problem in your code.
  4. Read the error message carefully: The error message will usually give you a clue as to what the problem is and how to fix it. Pay close attention to the error message and take the time to understand what it is telling you.
  5. Use a text editor with error navigation: Some text editors have plugins that can automatically parse the compiler output and allow you to quickly navigate to the location of the error in your code.
  6. Consult online resources: If you are not sure how to resolve an error, you can consult online resources such as Stack Overflow, the GCC documentation, or other forums.
  7. Try to understand the root cause of the error: Compiler errors often have multiple causes, so try to understand the root cause of the error so you can fix it for good.

By following these steps, you can make reading long compiler outputs easier and more manageable.

NanoHTTPD

NanoHTTPD is a tiny http server which can be used for testing.

It is a lightweight and simple HTTP server library written in C++. It provides a convenient way to serve HTTP content, such as HTML pages, images, and JSON data, directly from your Java applications. NanoHTTPD is easy to use, fast, and suitable for both small and large projects and is a great choice in automated tests.

Benefits of Using NanoHTTPD

  1. Lightweight and Simple: NanoHTTPD is a compact library, making it easy to integrate into your projects and keep your application size small. The library has a simple and intuitive API, making it easy to understand and get started with.
  2. Flexible: NanoHTTPD is highly flexible and customizable, allowing you to create custom responses and handling of requests to fit your specific needs. You can serve content in any format, such as HTML, images, and JSON, and you can handle requests for specific routes, perform authentication, and implement custom error handling.
  3. Fast: NanoHTTPD uses non-blocking I/O and is optimized for high performance, making it suitable for high-traffic applications. The library is capable of handling many concurrent connections, ensuring fast and responsive serving of content.
  4. Cross-platform: NanoHTTPD is written in Java and runs on any platform that supports Java, making it a cross-platform solution.

Getting Started with NanoHTTPD in Typescript

To start using NanoHTTPD, you need to include the library in your project. You can either download the library and include it in your project manually or use a build tool such as Maven or Gradle to manage your dependencies.

Once you have included the library, you can create a new NanoHTTPD server by extending the NanoHTTPD class and implementing the serve method. In the serve method, you can define the behavior of your server, such as handling requests and returning responses.

Here’s an example of a simple NanoHTTPD server that returns a “Hello, World!” message, in Typescript:

import fi.iki.elonen.NanoHTTPD;

public class HelloWorldServer extends NanoHTTPD {
    public HelloWorldServer() {
        super(8080);
    }
    
    @Override
    public Response serve(IHTTPSession session) {
        return newFixedLengthResponse("Hello, World!");
    }
    
    public static void main(String[] args) {
        new HelloWorldServer().start();
    }
}

In this example, the server listens on port 8080 and returns a “Hello, World!” message for every request. You can start the server by calling the start method, and it will listen for incoming requests until it is stopped.

Customizing Responses

NanoHTTPD provides several methods for returning custom responses, including returning HTML pages, images, and JSON data. You can use the newFixedLengthResponse method to return a custom response, and you can specify the response body, headers, and status code.

NanoHTTPD in C++

Here’s an example of a NanoHTTPD server that returns an HTML page, this time in C++:

#include <iostream>
#include "NanoHTTPD.h"

class HtmlServer : public NanoHTTPD {
 public:
  HtmlServer() : NanoHTTPD(8080) {}

  Response *serve(const Request &request) override {
    std::string html = "<html>"
                       "<body>"
                       "<h1>Hello, World!</h1>"
                       "</body>"
                       "</html>";

    return new Response(Response::OK, "text/html", html);
  }
};

int main(int argc, char **argv) {
  HtmlServer server;
  server.start();

  return 0;
}

This code creates a simple HTTP server that returns an HTML page with a “Hello, World!” heading. The Response constructor is used to return the HTML content, and the response type is set to “text/html”. The status code is set to Response::OK to indicate that the request was successful.

This is just a simple example, but you can extend it to return more complex content and handle different types of requests.

Code Review, what to look for

Why Code Reviews

Code review is an essential part of the software development process that helps to ensure the quality of code and catch potential issues before they become a problem. By having other developers review the code, it helps to identify areas for improvement, promote best practices, and ensure that code is maintainable, scalable, and secure. Code reviews can be conducted using a variety of tools, such as code review platforms, linting tools, automated code review tools, code comparison tools, and code coverage tools. The goal of Code review is to improve the quality of code and make the development process more efficient and effective. Regular Code Reviews can help to promote a culture of collaboration and teamwork within the development team, leading to better code and a more successful project.

What to look for

A code review is an important part of the software development process and developers should look for the following aspects when conducting a code review:

  1. Code Quality: Check if the code is clean, readable, and adheres to established coding standards. Ensure that the code is optimized and free of bugs.
  2. Functionality: Ensure that the code meets the requirements and that it functions as expected.
  3. Security: Check for potential security vulnerabilities and ensure that the code follows best practices for security.
  4. Test Coverage: Ensure that the code is covered by adequate test cases and that the tests are thorough.
  5. Performance: Review the code for performance bottlenecks and ensure that it is optimized for speed and efficiency.
  6. Scalability: Ensure that the code can scale to meet the needs of the users as the system grows.
  7. Maintainability: Check that the code is easy to maintain and can be easily updated and extended in the future.
  8. Documentations: Check if the code is properly documented, including comments and inline documentation, to help other developers understand it.

A successful code review is a collaborative effort that enhances the quality of the code and aligns it with the requirements of both the users and the development team. It should be a constructive process that helps to identify areas for improvement and ensures the code is optimized for maintenance, scalability, and security.

Tools

There are several tools available to help enhance code quality during code reviews:

  1. Linting Tools: These tools scan code for potential issues such as syntax errors, style violations, and semantic problems. Examples include ESLint and JSLint for JavaScript and Pylint for Python.
  2. Code Review Platforms: These platforms provide a centralized place for code review, allowing teams to review, discuss, and track changes to code. Examples include GitHub, GitLab, and Bitbucket.
  3. Automated Code Review Tools: These tools can automatically identify potential issues in code, such as security vulnerabilities, performance bottlenecks, and missing test coverage. Examples include SonarQube, CodeClimate, and Crucible.
  4. Code Comparison Tools: These tools allow developers to compare and merge changes to code. They can highlight differences between code versions and help to identify potential conflicts. Examples include Meld and Beyond Compare.
  5. Code Coverage Tools: These tools measure how much of the code is covered by tests and can identify areas where additional tests are needed. Examples include Cobertura and Istanbul.

Using these tools in combination with manual code review can help ensure that code quality is maintained and improved throughout the development process.

5 reasons why it makes sense to work with branches and tests in a single developer project

Working with branches and automated tests can bring a host of benefits to a single developer project, even if the project isn’t being worked on by multiple people. Here are some of the reasons why:

  1. Enhanced efficiency: When working with branches, a solo developer can tackle multiple features or bug fixes simultaneously, without having to worry about disrupting the main codebase. Additionally, by utilizing automated tests, the developer can validate that changes made in a branch don’t break existing functionality in a fast and efficient manner.
  2. Superior code quality: Automated tests can help catch bugs and issues early in the development process, long before they become problematic and harder to resolve. This leads to a more stable codebase and better code quality overall.
  3. Optimal version control: Branches allow a single developer to switch between different versions of code easily, as well as revert back to a previous version if necessary. This also makes it easier for the developer to manage code reviews and collaborate with other developers if the need arises in the future.
  4. Increased confidence: Automated tests provide a safety net for changes made in the code, which can give the developer more confidence when making modifications. If issues arise, the tests will quickly detect them, allowing the developer to fix them promptly.
  5. Support for experimentation: Branches make it possible for a developer to experiment with new ideas or approaches without affecting the main codebase. This can be especially valuable when exploring new technologies or finding new solutions to problems.

In conclusion, working with branches and automated tests can lead to improved efficiency, better code quality, optimal version control, increased confidence, and support for experimentation even in single developer projects. Whether you’re a beginner or an experienced developer, utilizing these tools can help streamline your development process and lead to better results.

10 shorthands commonly used in Code Reviews

There are several shorthands and abbreviations commonly used in code reviews:

  1. nit – nitpicking. Refers to minor and cosmetic changes that the reviewer suggests to the code (typos, formatting etc)
  2. N/A – Not Applicable, used to indicate that a particular comment or suggestion does not apply to the code being reviewed.
  3. +1 – Indicates agreement or support for a particular change or suggestion.
  4. -1 – Indicates opposition or disapproval of a particular change or suggestion.
  5. ACK – Acknowledge, used to indicate that the reviewer has seen the comment or suggestion and will address it.
  6. WIP – Work In Progress, used to indicate that the code being reviewed is still a work in progress and may not be complete or ready for review.
  7. RTFC – Read The F***ing Code, used to suggest that the reviewer should go back and read the relevant code before making a comment or suggestion.
  8. FIXME – A placeholder used to indicate that a particular piece of code needs to be fixed in the future.
  9. TODO – A placeholder used to indicate that a particular task needs to be completed in the future.
  10. LGTM

These shorthands and abbreviations are commonly used in code reviews to speed up the review process and make it more efficient. However, it’s important for all participants in the review to understand and agree on their meanings to avoid confusion and ensure effective communication.

If you know or use other shorthands or abbreviations, please let me know.

The 6 drawbacks of linter tools

While linter tools are widely used and can be incredibly helpful in detecting issues and improving code quality, they do have some disadvantages as well. Some of the common disadvantages of using linter tools include:

  1. False positives: Linters may produce false positive warnings or errors, which can be frustrating and lead to wasted time trying to resolve non-issues.
  2. Configuration complexity: Setting up a linter can be challenging, especially for large projects with multiple contributors and a complex codebase. It can be difficult to configure the linter to meet the specific needs of the project and the development team.
  3. Learning curve: Using a linter can require a learning curve for developers, as they need to understand how to use and configure the tool effectively. This can be especially challenging for developers who are new to the tool or the programming language.
  4. Inconsistent enforcement: Linters may not always be enforced consistently, leading to situations where some developers may not adhere to the linter’s recommendations. This can lead to inconsistent code quality and undermine the value of the linter.
  5. Limited scope: Linters are typically limited in scope and can only detect issues related to code syntax, style, and formatting. They may not be able to detect more complex issues such as performance bottlenecks or security vulnerabilities.
  6. Unfamiliar codebase: If a linter is being applied to an unfamiliar codebase, it may produce a large number of warnings and errors that can be overwhelming for the developer to resolve. This can lead to frustration and a sense that the tool is not effective.

In conclusion, while linter tools can be incredibly helpful in detecting issues and improving code quality, they also have some disadvantages that need to be taken into consideration. It is important to weigh the benefits and drawbacks of using a linter and determine if it is the right tool for your specific project and development team.

Code Review – A Critical Component of Software Development

Code reviews are an essential aspect of software development that can significantly enhance the quality and reliability of your code. They provide an opportunity for developers to learn from one another, share their expertise, and collaborate on creating better code.

One of the key benefits of code reviews is improved code quality. Through code reviews, developers can identify and resolve potential bugs, performance issues, and security vulnerabilities before the code is released to production. This proactive approach can save time and resources in the long run, as it is more cost-effective to catch and fix problems early in the development process.

In addition to improving code quality, code reviews also facilitate knowledge sharing and best practices. Reviewing the code of others can help developers understand the codebase and learn new techniques for writing high-quality code. This sharing of knowledge and expertise can lead to increased efficiency and better collaboration among team members, as everyone works towards a common goal.

Code reviews also play a crucial role in enhancing team communication. By working together to review and improve code, developers can build a sense of teamwork and collaboration. This can result in better communication and higher-quality code, as everyone works together to ensure the code meets the necessary standards and specifications.

Consistency in code is another important aspect that can be maintained through code reviews. By ensuring that code follows established coding standards, code reviews make it easier to maintain and enhance the code over time. This can greatly reduce the time and effort required for code maintenance and updates, as everyone on the team follows the same standards and best practices.

Finally, code reviews can also improve documentation, making it easier for others to understand and work with the code in the future. By reviewing the documentation and ensuring its completeness and accuracy, code reviews can help ensure that the code is well-documented and easy to understand.

In conclusion, code reviews are a valuable tool that can provide numerous benefits for both individual developers and development teams. Incorporating code reviews into your development process can help you write better code, share knowledge, communicate effectively, maintain consistency, and enhance documentation. Don’t overlook the importance of code reviews – make them a part of your workflow for the best results.

C++ Modules, since C++20

C++ Modules are a way to organize and manage code in large C++ projects.

Imagine you have a big project with many files and libraries, and each file depends on other files and libraries. Currently, in C++, to use a piece of code from another file, you have to include a header file with declarations, which can result in slow build times and difficult-to-manage dependencies.

C++ Modules aim to solve this problem by allowing you to package up all the code from a library into a single “module” file. You can then import this module into your own code, and you’ll have access to all the code in the module without having to include individual header files. This leads to faster build times and easier-to-manage dependencies.

Think of modules as containers for your code that make it easier to reuse and share with others, without having to worry about the details of how the code is organized or which files depend on which other files.

std::expected in C++23

std::expected is a type defined in the C++23 standard library as a way of handling errors in C++. It is used to represent the result of an operation that may either produce a value or an error. It provides a way to handle expected results in a type-safe and exception-free manner.

The std::expected template has two template parameters: the first is the type of the value that the operation may produce, and the second is the type of the error that the operation may return. The std::expected object can be in one of two states: a valid state, in which it holds a value, or an invalid state, in which it holds an error.

You can use the std::expected object just like a regular value, with the advantage that you can also check if the operation was successful or not. You can use the value() member function to access the stored value, or the error() member function to access the stored error. If the std::expected object is in the invalid state and you try to access the value, a compile-time error will occur.

Here is an example of using std::expected:

#include <iostream>
#include <expected>
#include <string>

std::expected<std::string, std::string> get_greeting(bool happy)
{
    if (happy) {
        return std::string("Hello, friend!");
    } else {
        return std::make_unexpected(std::string("Oh no, something went wrong."));
    }
}

int main()
{
    auto greeting = get_greeting(true);
    if (greeting) {
        std::cout << *greeting << std::endl;
    } else {
        std::cout << greeting.error() << std::endl;
    }
    return 0;
}

In this example, the get_greeting function returns a std::expected<std::string, std::string> object. If the happy argument is true, the function returns a string with a friendly greeting. If happy is false, the function returns an error message using std::make_unexpected.

In the main function, the code checks the state of the std::expected object. If the std::expected object is in the valid state, it contains the greeting and the code outputs the greeting to the console. If the std::expected object is in the invalid state, it contains the error message and the code outputs the error message to the console.

This example demonstrates how std::expected provides a way to handle expected results in a type-safe and exception-free manner, allowing you to check for success or failure and respond accordingly.

Why exception free

std::expected is exception-free because it doesn’t use exceptions to signal error conditions. Instead, it uses a return value to indicate success or failure, which can be checked using the valid() member function or by casting the std::expected object to a bool.

The advantage of using std::expected over exceptions is that it provides a clear and predictable way to handle error conditions. With exceptions, it can be difficult to know exactly where an exception will be thrown, making it harder to write robust and maintainable code. With std::expected, you can see from the function signature what type of error the function might return, and you can handle the error directly in the code that calls the function. This makes it easier to reason about the behavior of your code and to avoid unintended side effects.

In addition, using std::expected can also be more efficient than using exceptions because it avoids the overhead of constructing and unwinding the stack for each exception. This can make your code faster and more scalable, especially in resource-constrained environments where performance is critical.

This is an update to an earlier article where I explained std::expected before it was introduced to the C++23 standard library.