![]() |
VCTR
|
A powerful C++ 20 wrapper around your favorite standard library containers. This library is currently under active development, expect a 95% stable API in the current state.
VCTR is a library that makes working with vectors of any kind extremely expressive and easy while creating extremely well optimized code at the same time. We at sonible build real-time audio processing applications where we care a lot about code that is guaranteed to be heap allocation free, so a major part of the functionality was designed with that goal in mind. In order to be compatible to existing code that uses containers from the C++ standard library, we try to wrap classes from the standard library wherever possible. The VCTR project has two main building blocks:
VCTR has two container class templates: vctr::Vector, a std::vector backed resizable heap allocated container and vctr::Array, a std::array backed fixed size stack container. Besides that, it also has a class template that can act as a view to externally owned data which is vctr::Span, a std::span backed class which however derives from the same base class as vctr::Vector and vctr::Array. That common base class makes it easy to work on both, data contained in VCTR containers and data from external non-VCTR APIs.
Most of the time when working with vectors, we want to call a certain function on all elements of a vector, perform element wise computations to combine the values of multiple source vectors, reduce the content of a vector or often a combination from all of these tasks. Expressions aim to offer a straightforward syntax for chained modifications on vectors while avoiding the creation of intermediate temporary vectors at all cost.
Let's look at a first example:
We can see that expressions are built up from chaining individual operations with the operator <<. It can also be seen that VCTR makes use of class argument template deduction wherever posible. a is of type vctr::Array<int, 4>, b is of type vctr::Vector<int>, c is of type vctr::Span<float, 4> and d is of type vctr::Array<float, 4>. Note that the source operands are integer, but the vctr::exp expression transforms int inputs into float outputs. Also, if one operand has a compile-time static size like a in this case, the code assumes an expression result based on that operand to have the same size. If the expression contains run-time dynamic sized operands, debug builds will insert a run-time assertion that warns the developer if the size does not match.
Expressions don't necessarily need to be mathematical expressions. We can use them to apply all kinds of transformations on vectors. For an example, we can convert a numerical vector into a vector of strings:
strings will now be a vctr::Vector<std::string>containing string representations of all elements from numbers.
For an overview of all available expressions, there is an Expressions section in the documentation.
Besides the expressions, VCTR also offers quite a few member functions on the container and view classes. First of all, every member function from the wrapped standard library classes is forwarded. We decided to stick to the standards library snake_case style for them, although the VCTR project uses camelCase for member functions everywhere else. This makes it easy to identify such forwarded member functions of std::vector, std::array and std::span. On top of that, VCTR adds a lot of convenient functions that make tasks like finding and rearranging elements, accessing sub-spans of a vector and in case of vctr::Vector adding and removing elements:
For an overview of all available member functions, have a look at the Core Types section in the documentation.
If you want to write functions that take VCTR types as arguments, you have multiple options. If you care about maximum performance, it's recommended to write a function template and pass it as concept constrained template:
This approach allows the compiler to fully inline the function call. Furthermore, most VCTR types have additional optional template arguments that cary information about e.g. the alignment of memory etc. With the template approach, these informations are still visible within the function. If you want to constrain the value type that can be passed to the function you can also use a more specialised constrain:
You can also allow unevaluated expressions to be passed to a function, like e.g.
Accordingly, when you are returning VCTR instances created from within a function you should consider an auto return type, which has the same advantages of keeping the entire metadata kept in a certain instance. In general, you should stick to the almost always auto rule, e.g. these two lines will possibly be not the same:
Still, there might be cases where function templates are no suitable choice. If you want to accept all kinds of VCTR types as arguments to those functions, use vctr::Span as type. For read-only containers, qualify the value type argument of the span as const, for mutable container use the non-const equivalent:
vctr::Span has implicit constructors that will make a wide range of arguments silently convert to these types, which makes the above approach suitable for cases where non-vctr source and destination containers are involved.
Last but not least, there might be cases where the exact container type is known. In that case, it's of course also a solid approach to just pass the exact types by reference.
VCTR is a CMake based header-only C++ 20 library. The recommended way to use it is to add it as a git submodule in your project:
If your project uses CMake, you can simply add it to your CMake project using the add_subdirectory command and link against the sonible::vctr target:
To use it in your project, include the vcrt/vctr.h header:
You should be ready to use VCTR now!
In most cases you simply want to #include <vctr/vctr.h>. If you only want to access the forward declarations of the public types you can #include <vctr/vctr_forward_declarations.h> as a lightweight alternative.
VCTR is using cutting-edge C++ library features and needs a recent compiler to work properly. It is currently tested with the following compilers:
In order to achieve high performance, VCTR uses two platform specific vector operation libraries, these are:
On Apple systems the Accelerate framework is a required dependency. If you are using VCTR using CMake, the framework will be linked and used automatically; no configuration is necessary.
Intel IPP is an optional dependency that is available on Intel (x64) systems. All VCTR features will work without it, but you might see an increase in performance.
To install IPP, please follow the instructions on the intel website or install it using python: pip3 install ipp-static.
If you want VCTR to conveniently take care of linking against an existing IPP installation, use the VCTR_AUTOLINK_IPP=1 CMake option to instruct VCTR to find IPP and link against it.
VCTR will look for the required headers and libraries in common locations. In case you use a non-standard location, you can pass its location using the VCTR_IPP_ROOT command line option when exporting the project:
There might be cases where you want to integrate VCTR into a project that already links against IPP itself. In that case you have to do nothing special. VCTR will try to use IPP as soon as it can resolve the ipp.h header. In case you want to disable usage of IPP even if the headers are available, define the VCTR_USE_IPP preprocessor flag to 0.
GCE-Math (Generalized Constant Expression Math) is a templated C++ library enabling compile-time computation of mathematical functions. The header only gcem library is an optional dependency that allows us to use a lof of VCTRs math functions as constexpr to perform computations in compile time evaluated contexts.
VCTR will try to use GCE-Math as soon as it can resolve the gcem.hpp header. The easiest way to achieve that is to integrate GCE-Math via CMake and link against the gcem CMake target. In case you want to disable usage of GCE-Math even if the headers are available, define the VCTR_USE_GCEM preprocessor flag to 0.
Note that the minimum required version of GCE-Math is 1.16.0.
The documentation up until this point assumes that VCTR is used in a CMake-based project. If you however use a different setup, you can follow the steps below to use this library:
include folder to your header search path.Accelerate framework.In case you want to enable IPP support:
ipp.h can be found.ippcore(mt), ipps(mt) and ippvm(mt).In case you want to enable GCE-Math:
It is also possible to access a demo project that showcases some of VCTR's features. To build the demo project, export the project using the -D VCTR_BUILD_DEMO=1 CMake option after cloning:
VCTR's unit tests, located in the test subfolder, are dependent on Catch2 and GCE-Math.
The preferred method of installing the dependencies is using the C++ package manager conan. If conan is available on your system, export the project with the cmake-conan integration from the git submodule CMAKE_PROJECT_TOP_LEVEL_INCLUDES=ext/cmake-conan/conan_provider.cmake and VCTR_BUILD_TEST=1 options:
In case you want to integrate the VCTR unit test cases into your own catch2 based unit tests, you can define VCTR_EXPOSE_UNIT_TEST_CASES_TARGET to 1 and link against the sonible::vctr_test_targets target which contains all test cases but not the test main.
The project is currently under development. Contributions to the project are highly appreciated, just have a look at the issue section to find open issues. We'll add a contribution guideline to this repository soon.
(This section is still under development, more infos will follow)
snake_case headers are intended to be included by the user, CamelCase headers are implementation details and cannot be included on their own.#include <vctr_utils/...> supply additional utility functions that are not strictly relevant to the core functionality of the VCTR project. They are used in the test, benchmark, and demo projects and might have additional third party dependencies.#include <vctr_test_utils/...> supply additional utility functions that are useful especially for Catch2-based tests. They depend on having Catch2 greater version 3.0.0 available. If you are writing Catch2 based unit tests, you might be especially interested in the matchers that can be accessed via #include <vctr_test_utils/vctr_catch_matchers.h>.(This section is still under development, more infos will follow)
This section explains some commonly used names and concepts that are used throughout the project.
The extent definition is basically taken from std::span. In all situations where the size of a container, a view, or an expression is known at compile time, the extent will equal the size. In cases where the size is only known at runtime, the extent will equal std::dynamic_extent which in turn is defined to std::numeric_limits<size_t>::max() on all platforms we know. A Span might have both, a dynamic and a non-dynamic extent. A Vector will always have a dynamic extent. An Array will always have a non-dynamic extent. Expressions will inherit the extent of their source(s).
VCTR is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3 (LGPLv3) only, as published by the Free Software Foundation.
This basically implies, in legally non-binding terms:
Please find the full licensing terms and conditions in the LICENSE file.