This is a C++ Library header for the upcoming C++29 standard, providing a set of utility functions for working with std::vector. The library is currently in the experimental stage and is subject to change.
experimental/smart_view_vectorexperimental/smart_view_stringexperimental/smart_viewreflectvector_ptr
To install the library, copy the 29 directory to your system's include directory:
cp -r ./29 /usr/include/c++/
To use the library, include the header file in your C++ source code:
#include <c++/29/experimental/smart_view_vector>The library provides the following functions:
includes(const std::vector<T>& vec, const bool& value): Checks if a vector contains a specific value.includes(const std::vector<T>& vec, const std::function<bool(const T&)>& callback): Checks if a vector contains an element that satisfies a given condition.every(const std::vector<T>& vec, const bool& value): Checks if all elements in a vector are equal to a specific value.every(const std::vector<T>& vec, const std::function<bool(const T&)>& callback): Checks if all elements in a vector satisfy a given condition.print(const std::vector<T>& vec): Prints the contents of a vector to the standard output.
Here's an example of how to use the library:
#include <iostream>
#include <format>
#include <c++/29/experimental/smart_view_vector>
int main() {
smart_view_vector::print(std::vector<uint>{2, 3, 4, 5});
std::cout << std::format("includes: {0}", smart_view_vector::includes({1, 3, 4}, std::function<bool(const int&)>([](const int& num){ return num % 2 == 0; }))) << std::endl;
std::cout << std::format("every: {0}", smart_view_vector::every({1, 3, 4}, std::function<bool(const int&)>([](const int& num){ return num % 2 == 0; }))) << std::endl;
std::cout << std::format("{0}", smart_view_vector::every({2, 2, 2}, std::function<bool(const int&)>([](const int& num){ return num % 2 == 0; }))) << std::endl;
}std::optional<T> find<T>(const std::vector<T>& vec, const std::function<bool(const T&)>& callback)size_t findIndex<T>(const std::vector<T>& vec, const std::function<bool(const T&)>& callback)
The find and findIndex functions are utility functions that can be used to search for an element in a vector that satisfies a given condition.
The find function takes a vector and a callback function as arguments. It iterates over the elements in the vector and applies the callback function to each element. The find function now returns an std::optional<T>. This allows the function to return the actual element that satisfies the condition, or std::nullopt if no such element is found.
Here is an example usage of the find function:
std::vector<int> vec = {1, 2, 3, 4, 5};
auto result = smart_view_vector::find(vec, std::function<bool(const int&)>([](const int& num) { return num > 3; }));
if (result) {
std::cout << *result << std::endl; // will return 4
} else {
std::cout << "No match found" << std::endl;
}The findIndex function is similar to the find function, but instead of returning the element that satisfies the condition, it returns the index of that element in the vector. If no element satisfies the condition, it returns -1.
Here is an example usage of the findIndex function:
std::vector<int> vec = {1, 2, 3, 4, 5};
int result = smart_view_vector::findIndex(vec, std::function<bool(const int&)>([](const int& num) { return num < 3; }));
std::cout << result << std::endl;Both functions use a simple linear search algorithm, which has a time complexity of O(n), where n is the size of the vector. If the vector is sorted, a more efficient search algorithm such as binary search can be used to improve the performance.
The filter function is a utility function that creates a new vector containing only the elements from the original vector that satisfy a given condition. The condition is specified using a callback function.
Here is an example usage of the filter function:
std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector<int> filtered_vec = smart_view_vector::filter(vec, std::function<bool(const int&)>([](const int& num) { return num % 2 == 0; }));
std::cout << smart_view_vector::to_string(filtered_vec) << std::endl; // Output: "{2, 4}"The filter function can be useful when you need to extract a subset of elements from a vector based on some condition.
The each function is a utility function that can be used to iterate over the elements in a vector and apply a callback function to each element.
The each function takes a vector and a callback function as arguments. It iterates over the elements in the vector and applies the callback function to each element, passing the element, its index, and the entire vector as arguments to the callback function.
Here is an example usage of the each function:
std::vector<int> vec = {1, 2, 3, 4, 5};
std::smart_view::vector::each(vec, std::function<void(const int&, const size_t&, const std::vector<int>&)>([](const int& num, const size_t& index, const std::vector<int>& vec) {
std::cout << "Element " << num << " at index " << index << " in vector " << smart_view_vector::to_string(vec) << std::endl;
}));This will output:
Element 1 at index 0 in vector {1, 2, 3, 4, 5}
Element 2 at index 1 in vector {1, 2, 3, 4, 5}
Element 3 at index 2 in vector {1, 2, 3, 4, 5}
Element 4 at index 3 in vector {1, 2, 3, 4, 5}
Element 5 at index 4 in vector {1, 2, 3, 4, 5}
The each function can be useful when you need to perform some operation on each element in a vector, but you don't need to modify the vector itself.
The to_string function is a utility function that converts a vector to a string representation. The string representation consists of the elements of the vector enclosed in curly braces {} and separated by commas ,.
Here is an example usage of the to_string function:
std::vector<int> vec = {1, 2, 3, 4, 5};
std::string str = smart_view_vector::to_string(vec);
std::cout << str << std::endl; // Output: "{1, 2, 3, 4, 5}"The to_string function can be useful when you need to convert a vector to a string for debugging or logging purposes.
To use the library, include the header file in your C++ source code:
#include <c++/29/experimental/smart_view_string>This is a header file for the smart_view_string namespace, which provides utility functions for working with std::string objects in C++. The header file is part of the GNU ISO C++ Library and is released under the terms of the GNU General Public License.
The smart_view_string namespace contains the following functions:
startsWith(const std::string &str, const std::string &prefix): Returnstrueif thestrstarts with theprefix, andfalseotherwise.endsWith(const std::string &str, const std::string &suffix): Returnstrueif thestrends with thesuffix, andfalseotherwise.split(const std::string &str, char delimiter): Splits thestrinto a vector of substrings based on thedelimitercharacter.
Here is an example usage of these functions:
#include <iostream>
#include <vector>
#include <c++/29/experimental/smart_view_string>
int main() {
std::string str = "Hello, World!";
if (smart_view_string::startsWith(str, "Hello")) {
std::cout << "String starts with 'Hello'" << std::endl;
}
if (smart_view_string::endsWith(str, "World!")) {
std::cout << "String ends with 'World!'" << std::endl;
}
std::vector<std::string> tokens = smart_view_string::split(str, ',');
for (const auto& token : tokens) {
std::cout << token << std::endl;
}
return 0;
}This will output:
String starts with 'Hello'
String ends with 'World!'
Hello
World!
To use the library, include the header file in your C++ source code:
#include <c++/29/experimental/smart_view>This is a header file for the smart_view namespace, which is a part of the GNU ISO C++ Library. The smart_view namespace provides utility functions for working with std::string and std::vector objects in C++.
The smart_view namespace contains two sub-namespaces:
string: Contains utility functions for working withstd::stringobjects.vector: Contains utility functions for working withstd::vectorobjects.
The smart_view namespace is included in the std namespace, so you can access its functions directly through the std::smart_view namespace.
Here is an example usage of the smart_view namespace:
#include <iostream>
#include <vector>
#include <string>
#include <c++/29/experimental/smart_view>
int main() {
std::string str = "Hello, World!";
if (std::smart_view::string::startsWith(str, "Hello")) {
std::cout << "String starts with 'Hello'" << std::endl;
}
if (std::smart_view::string::endsWith(str, "World!")) {
std::cout << "String ends with 'World!'" << std::endl;
}
std::vector<std::string> tokens = std::smart_view::string::split(str, ',');
for (const auto& token : tokens) {
std::cout << token << std::endl;
}
std::vector<int> vec = {1, 2, 3, 4, 5};
std::optional<int> result = std::smart_view::vector::find(vec, std::function<bool(const int&)>([](const int& num) { return num % 2 == 0; }));
if (result.has_value()) {
std::cout << "First even number is " << *result << std::endl;
}
return 0;
}This will output:
String starts with 'Hello'
String ends with 'World!'
Hello
World!
First even number is 2
To use the library, include the header file in your C++ source code:
#include <c++/29/reflect>This is a header file for a reflect class that provides a set of static member functions for performing runtime type identification and downcasting of polymorphic objects. The class is designed to be used in a C++29 environment, as indicated by the __cxx29 namespace.
The reflect class has three template functions called construct. Each function takes a pointer or reference to an object of some abstract type _Abstract and attempts to dynamic cast it to a pointer of type T.
The first function takes a pointer to an object, the second function takes a reference to an object, and the third function takes a unique_ptr to an object.
If the dynamic cast is successful, the function returns a pointer to the object of type T. If the dynamic cast fails, the function returns a null pointer.
Tip
The reflect class can be useful when you need to perform runtime polymorphism and you want to avoid using dynamic_cast directly in your code. By using the reflect class, you can centralize all the dynamic casting in one place and make your code more readable and maintainable.
To use the reflect class, you can include the header file in your source code and use the construct function. Here is an example usage:
#include <iostream>
#include <memory>
#include <c++/29/reflect>
class Shape {
public:
virtual ~Shape() {}
virtual void draw() const = 0;
};
class Circle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a circle" << std::endl;
}
};
class Square : public Shape {
public:
void draw() const override {
std::cout << "Drawing a square" << std::endl;
}
};
int main() {
std::unique_ptr<Shape> shape = std::make_unique<Circle>();
const Circle* circle = std::reflect::construct<Circle>(shape);
if (circle != nullptr) {
circle->draw(); // Output: "Drawing a circle"
}
std::unique_ptr<Shape> shape2 = std::make_unique<Square>();
const Circle* circle2 = std::reflect::construct<Circle>(shape2);
if (circle2 != nullptr) {
circle2->draw(); // This code will not be executed because the dynamic cast will fail
}
return 0;
}In this example, we have an abstract class Shape and two concrete classes Circle and Square that inherit from Shape. We use the reflect::construct function to dynamic cast a unique_ptr<Shape> to a Circle*. If the dynamic cast is successful, we call the draw function on the Circle object. If the dynamic cast fails, we do nothing.
The ownership method is designed to perform dynamic type casting (dynamic cast) of objects managed by smart pointers or regular pointers. It allows for the safe conversion of a pointer to a base class into a pointer to a derived class, if possible.
The ownership method uses dynamic_cast to convert a pointer to a base class into a pointer to a derived class. If the conversion is possible, the method returns a pointer to the derived class. If the conversion is not possible (for example, if the object is not an instance of the derived class), the method returns nullptr.
The ownership method supports both const and non-const pointers, as well as smart pointers. It considers the constness of the input pointer and returns the corresponding pointer type (const or non-const).
The ownership method is intended for performing dynamic type casting and supporting both const and non-const pointers, as well as smart pointers. It allows for the safe conversion of a pointer to a base class into a pointer to a derived class, if possible. This is particularly useful in scenarios where access to the methods and data of the derived class is needed, which are not accessible through the base class interface.
- Access to derived class methods: The
ownershipmethod allows access to the methods of the derived class, which are not accessible through the base class interface. - Support for constness: The
ownershipmethod supports both const and non-const pointers, making it more versatile. - Support for smart pointers: The
ownershipmethod supports smart pointers, making it safer and more convenient to use in modern C++ programs.
-
Constness:
- The
constructmethods always return a const pointer (const T*). - The
ownershipmethods consider the constness of the input pointer and return the corresponding pointer type (const or non-const).
- The
-
Versatility:
- The
constructmethods are less versatile since they always return a const pointer. - The
ownershipmethods are more versatile since they support both const and non-const pointers, as well as smart pointers.
- The
-
Use of
constexprandnoexcept:- The
ownershipmethods useconstexprandnoexcept, which can improve code performance and safety. - The
constructmethods do not useconstexprandnoexcept.
- The
Take a look at an example of using the construct and ownership methods to perform dynamic type casting and access methods of a derived class.
#include <iostream>
#include <memory>
#include <c++/29/reflect>
class Shape {
public:
virtual ~Shape() {}
virtual void draw() const = 0;
};
class Circle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a circle" << std::endl;
}
void circleSpecificMethod() const {
std::cout << "Circle-specific method called" << std::endl;
}
};
class Square : public Shape {
public:
void draw() const override {
std::cout << "Drawing a square" << std::endl;
}
};
int main() {
// Create a unique_ptr to manage a Circle object
std::unique_ptr<Shape> shape = std::make_unique<Circle>();
// Use the ownership function to perform a dynamic cast
const Circle* circlePtr = std::reflect::ownership<Circle>(std::reflect::construct<Shape>(shape));
if (circlePtr != nullptr) {
circlePtr->draw();
circlePtr->circleSpecificMethod(); // This method is specific to Circle
}
// Create another unique_ptr to manage a Circle object
std::unique_ptr<Shape> anotherShape = std::make_unique<Circle>();
// Use the ownership function to perform a dynamic cast
const Circle* anotherCirclePtr = std::reflect::ownership<Circle>(anotherShape);
if (anotherCirclePtr != nullptr) {
anotherCirclePtr->draw();
anotherCirclePtr->circleSpecificMethod(); // This method is specific to Circle
}
};Output:
Drawing a circle
Circle-specific method called
Drawing a circle
Circle-specific method called
Explanation of the example
-
Creating an object:
- A smart pointer
std::unique_ptr<Shape>is created to manage aCircleclass object.
- A smart pointer
-
Using the
constructmethod:- The
constructmethod is used to perform dynamic type casting fromstd::unique_ptr<Shape>toconst Shape*. - This step is necessary to obtain a pointer to the base class
Shape.
- The
-
Using the
ownershipmethod:- The
ownershipmethod is used to perform dynamic type casting fromconst Shape*toconst Circle*. - This step allows obtaining a pointer to the derived class
Circle.
- The
-
Checking and calling methods:
- It is checked that the
circlePtrpointer is notnullptr. - If the pointer is not
nullptr, thedrawandcircleSpecificMethodmethods of theCircleclass object are called.
- It is checked that the
const Circle* circlePtr = std::reflect::ownership<Circle>(std::reflect::construct<Shape>(shape));This code section demonstrates the combined use of the construct and ownership methods to perform dynamic type casting. Let's break it down step by step:
-
Calling the
constructmethod:- The
constructmethod takes a smart pointerstd::unique_ptr<Shape>and performs dynamic type casting toconst Shape*. - This allows obtaining a pointer to the base class
Shape.
- The
-
Calling the
ownershipmethod:- The
ownershipmethod takes a pointerconst Shape*and performs dynamic type casting toconst Circle*. - This allows obtaining a pointer to the derived class
Circle.
- The
Note
Thus, the combined use of the construct and ownership methods enables safe and convenient dynamic type casting and access to derived class methods.
This example demonstrates how the construct and ownership methods can be used together to perform dynamic type casting and access derived class methods. The construct method is used to obtain a pointer to the base class, while the ownership method is used to obtain a pointer to the derived class. This approach allows for safe and convenient handling of objects managed by smart pointers.
To use the library, include the header file in your C++ source code:
#include <c++/29/vector_ptr>The vector_ptr library is part of the GNU ISO C++ Library, designed to handle heterogeneous collections of pointers in a type-safe manner. This library facilitates the storage of pointers to objects of different types in a single container, while providing mechanisms to safely cast them back to their original types.
- Dynamically Adding Pointers: The
push_back()method allows you to add pointers of various types (including raw pointers,std::unique_ptr,std::shared_ptr, andstd::weak_ptr) to the vector. - Type-Safe Access: The
get()method provides type-safe access to the pointers stored in the vector, preventing potential runtime errors. - Resource Management: The destructor of
vector_ptrautomatically deletes the objects pointed to by the stored pointers, ensuring proper resource cleanup.
- C++17 or higher
- GCC 10.1 or later (for complete C++20 support)
- Standard Template Library (STL)
Example with reflect
The example utilizes the reflect library to create a const reference to the Circle object. This allows you to access the draw() method of the Circle object without needing to worry about potential memory issues.
#include <iostream>
#include <memory>
#include <c++/29/experimental/smart_view>
#include <c++/29/reflect>
#include <c++/29/vector_ptr>
class Shape {
public:
virtual ~Shape() {}
virtual void draw() const = 0;
};
class Circle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a circle" << std::endl;
}
};
class Square : public Shape {
public:
void draw() const override {
std::cout << "Drawing a square" << std::endl;
}
};
int main() {
std::vector_ptr vp;
std::unique_ptr<Shape> shape = std::make_unique<Circle>();
vp.push_back(shape);
try {
Circle* circlePtr = vp.get<0, Circle>();
const Circle* circle = std::reflect::construct<Circle>(circlePtr);
circle->draw();
} catch (const std::out_of_range& e) {
std::cerr << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "An error occurred: " << e.what() << std::endl;
}
};Tip
- Memory Management: When using raw pointers with
vector_ptr, ensure that the objects are properly allocated and deallocated. Thevector_ptrdestructor will handle deleting the objects, but you need to be responsible for creating them. - Error Handling: The
get()method throws anout_of_rangeexception if the index is invalid. It's crucial to handle this exception in your code.
Here's a breakdown of the vector_ptr technique:
- Purpose: The
vector_ptrclass serves as a container for managing pointers to objects, providing a safe and efficient way to store and access these pointers. - Benefits:
- Type Safety: The
get()method ensures type safety during access, preventing potential runtime errors. - Resource Management: The destructor automatically deallocates the objects pointed to, avoiding memory leaks.
- Flexibility: It allows storing various pointer types (raw pointers, smart pointers) in a single container.
- Type Safety: The
- Use Cases: The
vector_ptrtechnique is particularly useful in scenarios where you need to store pointers to a collection of objects and perform operations on them. This could involve:- Managing a pool of objects: This is useful for scenarios like object caching or resource management.
- Implementing a factory pattern: You can store pointers to different object factories.
- Maintaining a list of objects: You can use
vector_ptrto store pointers to objects in a list and efficiently iterate over them.
This library is distributed under the GNU General Public License, version 3 or later.