Sunday, May 14, 2023

Deploy and run your C++ project on docker

 To deploy or run your C++ exe project with dlls on Docker, you can follow these steps:

  1. Install Docker on your Windows PC. You can download Docker from the Docker website.

  2. Create a Dockerfile. The Dockerfile is a text file that tells Docker how to build your image. In the Dockerfile, you will need to specify the base image, the commands to build your project, and the files to copy into the image.

  3. Build the image. Once you have created the Dockerfile, you can build the image using the docker build command. The docker build the command will create a new image based on the instructions in your Dockerfile.

  4. Run the image. Once you have built the image, you can run it using the docker run command. The docker run command will start a new container based on the image.

Here is an example of a Dockerfile that you can use to deploy a C++ exe project with dlls:

FROM ubuntu:latest

RUN apt-get update && apt-get install -y build-essential g++

COPY . /app

WORKDIR /app

RUN make

CMD ./my_exe

This Dockerfile will build an image that contains all of the dependencies needed to run your C++ exe project. To run the image, you can use the following command:

docker run -it my_image

This will start a new container based on the my_image image and run your C++ exe project.

Here are some additional tips for deploying C++ exe projects with dlls on Docker:

  • Use a base image that contains all of the dependencies needed to build and run your project. This will help to keep your image size small.
  • Use a build step to compile your project and copy the executable file into the image. This will help to ensure that your project is built for the correct architecture.
  • Use a run command to start your project. This will help to ensure that your project is running in the correct environment.

Saturday, May 13, 2023

Angular quick start

Angular is a popular web development framework for building client-side web applications. It is written in TypeScript and is developed and maintained by Google.

Here are the steps you need to follow to get started with Angular:

Step 1: Install Node.js and NPM

Node.js is a JavaScript runtime environment that allows you to run JavaScript on the server side. NPM is the Node.js package manager and is used to install packages and dependencies for your application. You can download and install Node.js from the official website.

Step 2: Install Angular CLI

Angular CLI is a command-line interface that provides a set of tools and utilities for creating and managing Angular applications. You can install Angular CLI using the following command:

npm install -g @angular/cli

Step 3: Create a new Angular application

You can create a new Angular application using the following command:

ng new my-app

This command will create a new Angular application in a directory called my-app.

Step 4: Run the Angular application

You can run the Angular application using the following command:

cd my-app ng serve

This command will compile the application and start a development server that can be accessed at http://localhost:4200.

Step 5: Start coding

key concepts and features of Angular:

  1. Components: Components are the building blocks of Angular applications. They are self-contained, reusable modules that define the view and behavior of a part of the user interface. Components consist of an HTML template, a TypeScript class, and associated styles and metadata.

  2. Templates: Templates are used to define the HTML markup and structure of an Angular component. Templates can include data binding, event binding, and structural directives to create dynamic and interactive user interfaces.

  3. Directives: Directives are a way to extend HTML with custom behavior. There are two types of directives in Angular: attribute directives and structural directives. Attribute directives modify the behavior or appearance of an element, while structural directives modify the layout or structure of the DOM.

  4. Services: Services are used to provide functionality that is independent of any particular component or module. Services can be injected into components or other services using Angular's dependency injection system.

  5. Dependency Injection: Angular's dependency injection system is a way to provide components and services with the dependencies they need to function. Dependencies are declared in the constructor of a component or service, and Angular's injector provides the necessary instances.

  6. Routing: Angular provides a powerful routing system that allows you to define navigation paths and associate them with specific components. This makes it easy to create single-page applications with multiple views.

  7. Modules: Angular applications are organized into modules. A module is a logical grouping of components, services, and other features of an application. Modules can be used to encapsulate functionality and to manage dependencies between different parts of an application.

  8. Pipes: Pipes are a way to transform data in an Angular template. Pipes can be used for formatting, filtering, and sorting data.

  9. Forms: Angular provides a comprehensive set of form features that make it easy to capture and validate user input. Angular forms support two-way data binding, form validation, and error handling.

  10. Observables: Observables are a way to handle asynchronous data streams in Angular. Observables can be used to handle HTTP requests, user input, and other asynchronous operations.

Friday, May 12, 2023

How to use OpenSSL to encrypt and decrypt data in C++



This example generates a random key and initialization vector, encrypts a message using AES-256-CBC, and then decrypts the message using the same key and initialization vector.

Note that this example uses a hardcoded plaintext message, but you can replace this with any message you want to encrypt.

Also, remember to include the necessary OpenSSL headers and links against the OpenSSL library when compiling your code.

#include <openssl/aes.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>

void encrypt_decrypt_example() {
// Generate a random key
unsigned char key[AES_BLOCK_SIZE];
RAND_bytes(key, AES_BLOCK_SIZE);

// Generate a random initialization vector
unsigned char iv[AES_BLOCK_SIZE];
RAND_bytes(iv, AES_BLOCK_SIZE);

// Plaintext message to be encrypted
unsigned char plaintext[] = "This is a secret message.";

// Encrypt the message using AES-256-CBC
AES_KEY aes_key;
AES_set_encrypt_key(key, 256, &aes_key);


int len = strlen((char*)plaintext);
int padding = AES_BLOCK_SIZE - len % AES_BLOCK_SIZE;
int ciphertext_len = len + padding;

unsigned char* ciphertext = new unsigned char[ciphertext_len];

AES_cbc_encrypt(plaintext, ciphertext, ciphertext_len, &aes_key, iv, AES_ENCRYPT);

// Decrypt the message
AES_set_decrypt_key(key, 256, &aes_key);
unsigned char* decrypted = new unsigned char[ciphertext_len];
AES_cbc_encrypt(ciphertext, decrypted, ciphertext_len, &aes_key, iv, AES_DECRYPT);


// Print the original message and decrypted message
printf("Original message: %s\n", plaintext);
printf("Decrypted message: %s\n", decrypted);


delete[] ciphertext;
delete[] decrypted;

}


OpenSSL has had some security vulnerabilities in the past, but it is generally considered a secure and reliable encryption library. Like any software, it is possible for hackers to find new vulnerabilities or exploit existing ones. However, OpenSSL is also a widely used and actively maintained open-source project, so security vulnerabilities are typically discovered and fixed quickly.

It's important to keep your OpenSSL installation up to date with the latest security patches, and to follow best practices for securing your system and network. Additionally, it's a good idea to use strong passwords and encryption keys, and to be cautious when sharing sensitive information over the internet. While no system is 100% secure, taking these steps can help minimize the risk of hacking and data breaches.

C++ Plugin Architecture Types

C++ Plugin Architecture Types


There are several types of plugin system architectures in C++. Here are some common ones:

Dynamic Linking

  • This architecture uses shared libraries to dynamically load and unload plugins at runtime. 
  • The plugins are implemented as shared libraries (DLLs on Windows, .so files on Linux), and the plugin manager loads them using dynamic linking functions like dlopen() and GetProcAddress(). 
  • This architecture is widely used on both Windows and Unix-like systems.
  • It's commonly used in multimedia applications, web browsers, and operating systems.

Static Linking

  • This architecture uses static libraries to link the plugins to the main application at compile time. 
  • The plugins are implemented as static libraries that are linked to the main executable using the linker. 
  • This architecture is less flexible than dynamic linking, but it has better performance and can be used on systems that don't support dynamic linking.
  • It's commonly used in embedded systems, game engines, and high-performance computing applications.

Scripting

  • This architecture uses a scripting language like Lua or Python to implement the plugins. 
  • The plugins are implemented as scripts that are executed by an embedded interpreter in the main application. 
  • This architecture is easy to use and allows for rapid prototyping, but it can be slower than native code and may require more memory.
  • It's commonly used in game engines, desktop applications, and web applications.

COM/OLE

  • This architecture uses the Component Object Model (COM) or Object Linking and Embedding (OLE) to implement the plugins.
  • The plugins are implemented as COM/OLE objects that can be loaded and instantiated by the plugin manager. 
  • This architecture is widely used on Windows systems, but it can be complex and difficult to implement. 
  • It's commonly used in Office applications, database applications, and web browsers.

Reflection

  • This architecture uses reflection to dynamically load and instantiate plugins. 
  • The plugins are implemented as C++ classes that can be instantiated using a factory method or a reflection library like Boost. Reflection. 
  • This architecture is flexible and easy to use, but it can be slower than native code and may require more memory. 
  • It's commonly used in frameworks, libraries, and middleware.

C++ plugin API


When designing a C++ plugin API, there are several key factors to consider to ensure that the API is well-designed, modular, and easy to use. Here are some important things to keep in mind when designing a C++ plugin API:

Interface-based design

The plugin API should be designed using an interface-based approach, with a well-defined set of functions that the host application can use to interact with the plugin. 

This helps to ensure that the plugin remains modular and can be easily updated or replaced.

Functionality

The plugin API should provide a clear and well-defined set of functionality that the plugin can provide to the host application. 

This requires careful consideration of the specific use cases and requirements for the plugin.

Compatibility

The plugin API should be designed to be compatible with different versions of the host application, as well as different operating systems and architectures. 

This requires careful attention to API design and testing, to ensure that the API works correctly in all supported environments.

Error handling

The plugin API should be designed to handle errors gracefully, returning error codes or exceptions when necessary. 

This ensures that the plugin does not crash or hang the host application, and allows the host application to handle errors in a consistent way.

Naming conventions

The plugin API should use clear and consistent naming conventions for functions, variables, and other elements. 

This makes it easier for developers to understand and use the API and reduces the likelihood of naming conflicts or confusion.

Documentation

The plugin API should be well-documented, with clear and concise documentation for each function and element. 

This helps developers understand how to use the API and can reduce the likelihood of errors or bugs.

Security

The plugin API should be designed with security in mind, to prevent malicious or unauthorized plugins from accessing sensitive data or resources. 

This requires careful attention to input validation, buffer overflows, and other potential security vulnerabilities.

Designing a C++ plugin API requires careful consideration of the specific requirements and use cases for the plugin, as well as attention to API design, compatibility, error handling, naming conventions, documentation, and security. 

With a well-designed plugin API, you can provide a powerful and extensible extension mechanism for your application, allowing users to customize and enhance the application's functionality.

// Define a plugin interface with a set of functions that the host application can use to interact with the plugin
class PluginInterface 
{
public:
    virtual void initialize() = 0;
    virtual void doSomething(int arg1, float arg2) = 0;
    virtual void cleanup() = 0;
};

// Define an abstract factory interface that the plugin manager can use to create instances of the plugin
class PluginFactory 
{
public:
    virtual PluginInterface* createPluginInstance() = 0;
    virtual void destroyPluginInstance(PluginInterface* instance) = 0;
};

// Define a plugin manager interface that the host application can use to manage the loaded plugins
class PluginManager 
{
public:
    virtual void loadPlugin(const std::string& path) = 0;
    virtual void unloadPlugin(const std::string& path) = 0;
    virtual void enablePlugin(const std::string& path) = 0;
    virtual void disablePlugin(const std::string& path) = 0;
    virtual std::vector<std::string> getAvailablePlugins() const = 0;
    virtual PluginInterface* getPluginInstance(const std::string& path) = 0;
};

// Define a plugin class that implements the plugin interface
class MyPlugin : public PluginInterface 
{
public:
    virtual void initialize() override 
    {
        // Perform initialization tasks
    }
    virtual void doSomething(int arg1, float arg2) override
    {

        // Perform some action using the provided arguments
    }
    virtual void cleanup() override 
    {
        // Perform cleanup tasks
    }
};

// Define a factory class that creates instances of the plugin class
class MyPluginFactory : public PluginFactory 
    {
public:
    virtual PluginInterface* createPluginInstance() override 
    {
        return new MyPlugin();
    }
    virtual void destroyPluginInstance(PluginInterface* instance) override ]    
    {
        delete instance;
    }
};

// Define a plugin manager class that manages the loaded plugins
class MyPluginManager : public PluginManager 
{
public:
    virtual void loadPlugin(const std::string& path) override 
    {
        // Load the plugin from the specified path and add it to the list of loaded plugins
    }
    virtual void unloadPlugin(const std::string& path) override 
    {
        // Unload the specified plugin and remove it from the list of loaded plugins
    }
    virtual void enablePlugin(const std::string& path) override 
    {
        // Enable the specified plugin
    }
    virtual void disablePlugin(const std::string& path) override 
    {
        // Disable the specified plugin
    }
    virtual std::vector<std::string> getAvailablePlugins() const override 
    {
        // Get the list of available plugins
        return std::vector<std::string>();
    }
    virtual PluginInterface* getPluginInstance(const std::string& path) override 
    {
        // Get an instance of the specified plugin
        return nullptr;
    }
};




C++ - std::condition_variable in C++.

`std::condition_variable` in C++.

`std::condition_variable` is a synchronization primitive that allows threads to wait until a certain condition is met. It's typically used in conjunction with a `std::mutex` to provide a way for threads to signal each other when some shared state has changed.

The basic usage of `std::condition_variable` involves creating an instance of the class, and then waiting on it in one thread while notifying it in another thread. Here's some example code:


#include <condition_variable>

#include <mutex>

#include <thread>

#include <iostream>


std::condition_variable cv;

std::mutex m;

bool ready = false;


void worker_thread() {

    std::unique_lock<std::mutex> lock(m);

    while (!ready) {

        cv.wait(lock);

    }

    std::cout << "Worker thread notified!" << std::endl;

}


int main() {

    std::thread t(worker_thread);

    // Do some work...

    {

        std::lock_guard<std::mutex> lock(m);

        ready = true;

    }

    cv.notify_one();

    t.join();

    return 0;

}


In this example, we create a `std::condition_variable` and a `std::mutex`. We also create a boolean flag `ready` which will be used to signal the other thread.

The worker thread waits on the condition variable using a `std::unique_lock` on the mutex. The `wait` function will unlock the mutex and wait for a notification from another thread. When the notification is received, the lock is reacquired and the worker thread continues its work.

In the main thread, we first create the worker thread and then do some work. Once the work is complete, we set the `ready` flag and notify the condition variable using the `notify_one` function. This wakes up the worker thread, which can then proceed with its work.

`std::condition_variable` provides a powerful and flexible way to synchronize threads in a C++ program. By using it in conjunction with other synchronization primitives like `std::mutex` and `std::atomic`, you can build robust and reliable concurrent applications.

C++ - std::future and std::promise in C++

std::future and std::promise

In C++, std::future and std::promise are classes used for asynchronous communication between two threads. They are part of the standard library and can be used for various purposes such as implementing thread-safe data structures or handling parallel computations.

std::future is a class template that represents a value or an exception that will be available in the future. It can be used to wait for the result of an asynchronous operation or to retrieve the value of a function that is being executed in another thread.

std::promise is another class template that allows one thread to store a value or an exception that can be retrieved by another thread later. It is used to communicate the result of an asynchronous operation to another thread.

The basic idea behind std::future and std::promise is that one thread (usually the main thread) creates a std::promise object and passes it to another thread (the worker thread) along with a task to be executed. The worker thread executes the task and sets the value of the promise using the set_value() method. Meanwhile, the main thread waits for the result by calling the get() method on a corresponding std::future object.

Here's a simple example that demonstrates the use of std::future and std::promise in C++:

#include <iostream> #include <future> int main() { std::promise<int> promiseObj; std::future<int> futureObj = promiseObj.get_future(); // Start a new thread to execute a task std::thread t([&promiseObj]() { // Simulate some long-running task std::this_thread::sleep_for(std::chrono::seconds(2)); int result = 42; // Set the value of the promise promiseObj.set_value(result); }); // Wait for the result and print it int result = futureObj.get(); std::cout << "Result: " << result << std::endl; // Join the thread and exit t.join(); return 0; }

std::function, std::promise, and std::future

In this example, we create a std::promise object and a corresponding std::future object. We pass the std::promise object to a new thread along with a task to be executed. The new thread executes the task and sets the value of the promise using the set_value() method. Meanwhile, the main thread waits for the result by calling the get() method on the std::future object. Once the result is available, it is printed to the console.

example of how to use std::function, std::promise, and std::future together to implement a callback function:

#include <iostream> #include <functional> #include <future> void longRunningTask(std::function<void(int)> callback) { // Simulate a long-running task std::this_thread::sleep_for(std::chrono::seconds(3)); // Call the callback with the result of the task callback(42); } int main() { std::promise<int> promise; std::future<int> future = promise.get_future(); std::function<void(int)> callback = [&](int result) { promise.set_value(result); }; // Start the long-running task with the callback function longRunningTask(callback); // Wait for the result from the future int result = future.get(); std::cout << "Result: " << result << std::endl; return 0; }

In this example, the longRunningTask() function simulates a long-running task that takes 3 seconds to complete. The function takes a std::function<void(int)> parameter, which is a callback function that will be called with the result of the task.

In, we create a std::promise<int> and get a std::future<int> from it. We also create a std::function<void(int)> callback function that captures the promise by reference and sets its value when called with the result.

We then call longRunningTask() with the callback function, and wait for the result from the future using future.get(). Finally, we print the result to the console.

This example demonstrates how std::function, std::promise, and std::future can be used together to implement a callback function that receives a result from a long-running task.


Why Exceptions should be handled in programming language?

When exceptions are not handled

Exceptions should be handled in programming languages like C++ or Java because if they are not handled properly, they can cause a program to terminate prematurely or behave unpredictably, which can result in data loss, security vulnerabilities, or other serious problems.

If an exception is thrown in a C++ or Java program and not caught by a corresponding try-catch block, it will propagate up the call stack until it reaches the top-level function of the program. At that point, the program will terminate and any remaining resources held by the program will be released. This can leave the program in an inconsistent state and cause unexpected behavior.

In addition, unhandled exceptions can reveal sensitive information about the program or the system it is running on, making it easier for attackers to exploit security vulnerabilities.

By handling exceptions properly, you can provide better error reporting to users, recover from errors gracefully, and prevent crashes or data corruption. This can help ensure that your program is more robust and reliable, and less vulnerable to security issues.

Exceptions should be handled in C++ and Java to prevent program crashes, ensure data integrity, and improve security.

Handled exception

A handled exception in programming languages like C++ and Java is an exception that is caught and processed by the program using a try-catch block. When an exception is thrown, the program jumps out of the current scope and looks for a corresponding catch block that can handle the exception.

When an exception is handled properly, it improves the program in several ways:

  1. Improved error reporting: By catching and handling exceptions, you can provide more informative error messages to the user, which can help them diagnose and fix problems with the program.

  2. Graceful recovery: Handling exceptions allows your program to recover from errors gracefully and continue executing, rather than crashing or terminating prematurely.

  3. Data integrity: When an exception is thrown, it can leave the program in an inconsistent state, which can lead to data corruption or loss. Handling exceptions can help prevent these issues by providing a way to roll back changes or take other corrective action.

  4. Security: Unhandled exceptions can reveal sensitive information about the program or the system it is running on, making it easier for attackers to exploit vulnerabilities. Handling exceptions properly can help prevent this by providing a way to handle errors without revealing sensitive information.

Example of how handling an exception can improve a Java program:

Let's say you are writing a program that reads input from a file and performs some processing on it. If the file cannot be found or cannot be read, the program should display an error message to the user and exit gracefully.

import java.io.File;

import java.io.FileNotFoundException; import java.util.Scanner; public class MyProgram { public static void main(String[] args) { File inputFile = new File("input.txt"); Scanner scanner = null; try { scanner = new Scanner(inputFile); // read and process input here } catch (FileNotFoundException e) { // handle file not found exception System.err.println("File not found: " + inputFile.getName()); System.exit(1); } finally { if (scanner != null) { scanner.close(); } } } }

In this example, we first create a File object that represents the input file. We then use a try-catch block to catch the FileNotFoundException that can be thrown if the file cannot be found. If the file is found, we create a Scanner object to read input from the file and perform some processing on it.

We also use a finally block to ensure that the Scanner the object is closed when we are finished using it. This is important to prevent resource leaks and ensure that the program releases any system resources it has acquired.

LeetCode C++ Cheat Sheet June

🎯 Core Patterns & Representative Questions 1. Arrays & Hashing Two Sum – hash map → O(n) Contains Duplicate , Product of A...