A wrapper of C++ std::vector for functional programming

Overview

CMake Build Matrix GitHub license

Say hello to functional C++ vectors

A wrapper for C++ std::vector geared towards functional programming and fluent APIs. The primary focus is readability at the call site (not performance) and eliminating manual management of vector indices. This is heavily influenced and inspired by C# and Swift.

Compilation (Cmake)

Dependencies

  • CMake >= 3.14

Minimum C++ version

  • C++11

An out-of-source build strategy is used. All following examples assume an output build folder named build. If no additional argument is passed to CMake, C++11 is used. Otherwise, you can pass -DCMAKE_CXX_STANDARD=17 and it will use C++17 for example.

macOS (Xcode)

cd functional_vector
cmake -S . -B build -G Xcode

Then open the generated functional_vector.xcodeproj in the build folder.

macOS (Makefiles/clang)

cd functional_vector
cmake -S . -B build
cmake --build build
build/tests/unit_tests

macOS (Makefiles/g++)

Assuming you have installed Homebrew, then you can use the gcc and g++ compilers by doing the following (this example uses version gcc 11)

cd functional_vector
cmake \
    -S . \
    -B build \
    -DCMAKE_C_COMPILER=/opt/homebrew/Cellar/gcc/11.2.0/bin/gcc-11 \
    -DCMAKE_CXX_COMPILER=/opt/homebrew/Cellar/gcc/11.2.0/bin/g++-11
cmake --build build
build/tests/unit_tests

Linux (Makefiles)

cd functional_vector
cmake -S . -B build
cmake --build build
build/tests/unit_tests

Windows (Visual Studio)

cd functional_vector
cmake -S . -B build

Then open the generated functional_vector.sln in the build folder.

Usage

zip, map, filter, sort

#include "functional_vector.h" // instead of <vector>

struct person {
    person(int age, std::string name)
    : age(age), name(std::move(name))
    {}
    int age;
    std::string name;
};

// ...

// the employees' ages
const functional_vector<int> ages({32, 45, 37, 23});

// the employees' names
const functional_vector<std::string> names({"Jake", "Anna", "Kate", "Bob"});

const auto employees_below_40 = ages
    // zip two vectors for simultaneous processing
    .zip(names)

    // apply the functional map algorithm (transform from one type to another)
    .map<person>([](const auto& pair) {                     
        return person(pair.first, pair.second);
    })
    
    // filter the elements using a local function (lambda)
    .filter([](const auto& person) {
        return person.age < 40;
    })
    
    // sort according to custom predicate
    .sort([](const auto& person1, const auto& person2) {
        return person1.age < person2.age;
    });

/*
 prints the following:
 Bob is 23 years old.
 Jake is 32 years old.
 Kate is 37 years old.
 */
employees_below_40.for_each([](const auto& person) {
    std::cout << person.name << " is " << person.age << " years old." << std::endl;
});

index search

#include "functional_vector.h" // instead of <vector>

const functional_vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});

const auto first_index_of_one = numbers.find_first_index(1);
// returns 0
first_index_of_one.value();

const auto last_index_of_one = numbers.find_last_index(1);
// returns 8
last_index_of_one.value();

// all_indices_of_one -> { 0, 6, 8 }
const auto all_indices_of_one = numbers.find_all_indices(1);

const auto index_of_nine = numbers.find_first_index(9);
// returns false
index_of_nine.has_value();

remove, insert

#include "functional_vector.h" // instead of <vector>
#include "index_range.h"

functional_vector<int> numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});

// numbers -> functional_vector<int>({1, 4, 2, 5, 3, 1, 7, 1});
numbers.remove_at(4);

// numbers -> functional_vector<int>({4, 2, 5, 3, 1, 7, 1});
numbers.remove_front();

// numbers -> functional_vector<int>({4, 2, 5, 3, 1, 7});
numbers.remove_back();

// numbers -> functional_vector<int>({4, 2, 7});
numbers.remove_range(index_range::start_count(2, 3));

// numbers -> functional_vector<int>({4, 8, 2, 7});
numbers.insert_at(1, 8);

// numbers -> functional_vector<int>({-10, 4, 8, 2, 7});
numbers.insert_front(-10);

// numbers -> functional_vector<int>({-10, 4, 8, 2, 7, 9});
numbers.insert_back(9);

// numbers -> functional_vector<int>({-10, 4, 8, 3, -2, 5, 2, 7, 9});
numbers.insert_at(3, std::vector({3, -2, 5}));

// numbers -> functional_vector<int>({4, -6, 7, -10, 4, 8, 3, -2, 5, 2, 7, 9});
numbers.insert_front(functional_vector({4, -6, 7}));

// numbers -> functional_vector<int>({4, -6, 7, -10, 4, 8, 3, -2, 5, 2, 7, 9, 7, 3});
numbers.insert_back(std::initializer_list({7, 3}));

size/capacity, reserve/resize

#include "functional_vector.h" // instead of <vector>

// numbers.capacity() = 9
// numbers.size() = 9
functional_vector<int> numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});

// numbers -> functional_vector<int>({1, 4, 2, 5, 8});
// numbers.capacity() = 9
// numbers.size() = 5
numbers.resize(5);

// numbers -> functional_vector<int>({1, 4, 2, 5, 8, 0, 0});
// numbers.capacity() = 9
// numbers.size() = 7
numbers.resize(7);

// empty_numbers.capacity() = 0
// empty_numbers.size() = 0
functional_vector<int> empty_numbers;

// empty_numbers.capacity() = 5
// empty_numbers.size() = 0
empty_numbers.reserve(5);

all_of, any_of, none_of

#include "functional_vector.h" // instead of <vector>

functional_vector<int> numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});

// returns true
numbers.all_of([](const auto &number) {
    return number < 10;
});

// returns false
numbers.all_of([](const auto &number) {
    return number > 2;
});

// returns true
numbers.any_of([](const auto &number) {
    return number < 5;
});

// returns false
numbers.any_of([](const auto &number) {
    return number > 9;
});

// returns true
numbers.none_of([](const auto &number) {
    return number < -2;
});

// returns false
numbers.none_of([](const auto &number) {
    return number > 7;
});

Parallel algorithms

Since C++17 several STL algorithms can be executed in parallel.

clang on macOS does not yet fully support the parallel execution model, however on Windows and Linux, a functional_vector supports the following parallel algorithms

for_each_parallel
map_parallel
filter_parallel
sort_parallel
all_of_parallel
any_of_parallel
none_of_parallel
Issues
  • functional_vector is not Immutable

    functional_vector is not Immutable

    One of the main rule of the functional programming is to make everything immutable by default.

    If you look at the screenshot, you see a basic test file that must succeed with immutable vector. To explain, when you add something to the vector, it must return the vector with addition, but the old vector must not contain the added value(s). Idem for deletion, filter, etc...

    This make things better in logic because you didn't got any side effect when you execute a function. Function has Immutable input and return the result. Always in that way.

    I'm really interested in immutable programmation in C++ and that can be good to find every point of this awesome type of programming in this language.

    image

    opened by shiipou 2
  • Refactor

    Refactor

    I replaced all the use of std::function<T(U)> with templates -- that way we will get the exact type of the callable instead of a type-erased function with some overhead.

    functional_tuple is a generic name and since there are only two data members, using the name pair seem more concise.

    I added a few more iterable functions to make the bunch complete when used in ranged-based loops.

    The use of cbegin() and cend() is enforced on const objects.

    const objects cannot be std::moved from, it it were possible, it would defeat the whole point of them being const -- so I removed the call to std::move on all const objects

    opened by iamOgunyinka 2
  • Address compiler warnings in Windows

    Address compiler warnings in Windows

    There are many compiler warnings in Windows, which should be addressed, like the following

    • index_range.obj : MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance (warning LNK4075: ignoring '/INCREMENTAL' due to '/LTCG' specification)

    • functional_vector_test.obj : MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance (warning LNK4075: ignoring '/INCREMENTAL' due to '/LTCG' specification)

    • functional_vector_test.cc(558,2): warning C4834: discarding return value of function with 'nodiscard' attribute

    • functional_vector.h(324,1): warning C4267: 'argument': conversion from 'size_t' to 'int'

    • functional_vector.h(552,9): warning C4018: '<=': signed/unsigned mismatch

    • functional_vector.h(498,9): warning C4389: '==': signed/unsigned mismatch

    invalid 
    opened by jkalias 1
  • Implement parallel versions of algorithms

    Implement parallel versions of algorithms

    Certain STL algorithms exist in parallel alternatives. It would be useful if the library included these parallel alternatives as well (only for > C++17 and not macOS, as these are not yet implemented in the clang system).

    Possible functions to be parallelized: for_each -> for_each_parallel map -> map_parallel filter -> filter_parallel sort -> sort_parallel all_of -> all_of_parallel any_of -> any_of_parallel none_of -> none_of_parallel

    enhancement 
    opened by jkalias 0
  • A little refactoring

    A little refactoring

    Removed the need for std::function. Added const iterators. Renamed functional_vector_tuple to functional_vector_pair. Changed the use of some initializations. Moved the member function from the middle of the class to the bottom. (optionally) reformatted the file to use clang-format.

    opened by iamOgunyinka 0
  • Enhance README with more examples

    Enhance README with more examples

    We can better illustrate how the library is meant to be used by writing some examples in the README, for example for the following functions/cases

    • find_first_index
    • find_last_index
    • find_all_indices
    • remove_first / remove_last
    • insert_at (single and range, mutating and non-mutating)
    • remove_at (sing and range, mutating and non-mutating)
    • replace_at (sing and range, mutating and non-mutating)
    documentation good first issue 
    opened by jkalias 0
  • Enable backwards compatibility with C++11

    Enable backwards compatibility with C++11

    When we search for the index of a given element in the vector, we return std::optional to handle the non-existence case. This means that we need at least C++17. However, this significantly limits the range of code bases for which the library can be used.

    There are many open source implementations of a C++11 backwards compatible version for std::optional, but this adds an extra dependency. Perhaps we can use a custom type to represent this specific case of index return values.

    enhancement 
    opened by jkalias 0
  • Improve performance with lazy operations

    Improve performance with lazy operations

    Consider the following example from the documentation

    const auto employees_below_40 = ages
        .zip(names)
        .map<person>([](const auto& pair) {                     
            return person(pair.first, pair.second);
        })
        .filter([](const auto& person) {
            return person.age < 40;
        })
        .sort([](const auto& person1, const auto& person2) {
            return person1.age < person2.age;
        });
    

    This snippet will iterate the corresponding instance of functional_vector 4 times, once for every call (zip, map, filter and sort). If ages would have been a significantly long vector, then the current implementation is really inefficient.

    In such cases it would be really useful if we had lazy variants of these algorithms, such that for the final result only one iteration would be performed. A possible but not definite implementation could look like this

    const auto employees_below_40 = ages
        .lazy_zip(names) // lazy on the top-level call, which propagates down the chain
        .map<person>([](const auto& pair) {                     
            return person(pair.first, pair.second);
        })
        .filter([](const auto& person) {
            return person.age < 40;
        })
        .sort([](const auto& person1, const auto& person2) {
            return person1.age < person2.age;
        })
        .get_result(); // or .execute();
    
    enhancement 
    opened by jkalias 0
  • Enhance library with wrapper for std::map

    Enhance library with wrapper for std::map

    Similarly to functional_vector, which wraps std::vector, we can write a wrapper for std::map.

    Possible names: functional_map functional_dictionary (inspired from C#, Python, Swift etc)

    enhancement 
    opened by jkalias 0
Cpp-std-fwd - forward declarations for C++ std headers

cpp-std-fwd Forward declarations for most useful runtime classes of the C++ 17 standard library. DISCLAIMER: This project is meant as a proof-of-conce

Philip Trettner 66 Apr 22, 2022
:computer: C++ Functional Terminal User Interface. :heart:

FTXUI Functional Terminal (X) User interface A simple C++ library for terminal based user interface. Demo: Feature Functional style. Inspired by [1] a

Arthur Sonzogni 3.3k Jul 3, 2022
Fire for C++: Create fully functional CLIs using function signatures

Fire for C++ Fire for C++, inspired by python-fire, is a single header library that creates a command line interface from a function signature. Here's

Kristjan Kongas 434 Jun 10, 2022
C++ functions matching the interface and behavior of python string methods with std::string

Pystring is a collection of C++ functions which match the interface and behavior of python's string class methods using std::string. Implemented in C+

Sony Pictures Imageworks 724 Jul 3, 2022
Bitset Sort, a faster std::sort replacement.

Bitset Sort Bitset Sort is a variant of quick sort, specifically BlockQuickSort. Bitset Sort uses a carefully written partition function to let the co

null 63 Jun 6, 2022
C++ getopt wrapper to make it easier to parse command line arguments

Options is a simple C++ wrapper for getopt that makes it easier to handle command line argument parsing in C++. See demo.cc and the Makefile for an e

Gary Hollis 1 Oct 30, 2021
Cython wrapper around C++ iostream

ciostream Provides a Cython wrapper around C++ iostream, allowing Python extensions to use Python streams with C++ code that uses C++ iostreams. Examp

Matthew Ballance 2 Dec 16, 2021
Tutored programming project for the first semester of my DUT in computer science

Pac-Man Project Description First of all, this game has been realized in binomial as part of our first programming tutorial project of our first year

Arthur Pellegrini 1 Nov 5, 2021
Fast C++ container combining the best features of std::vector and std::deque

veque The double-ended vector A very fast C++17 container combining the best features of std::vector and std::deque "In Most Cases, Prefer Using deque

null 97 May 4, 2022
C++11/14/17 std::expected with functional-style extensions

expected Single header implementation of std::expected with functional-style extensions. Clang + GCC: MSVC: Available on Vcpkg and Conan. std::expecte

Sy Brand 796 Jul 1, 2022
C++11/14/17 std::optional with functional-style extensions and reference support

optional Single header implementation of std::optional with functional-style extensions and support for references. Clang + GCC: MSVC: std::optional i

Sy Brand 645 Jun 29, 2022
The PULP Ara is a 64-bit Vector Unit, compatible with the RISC-V Vector Extension Version 0.9, working as a coprocessor to CORE-V's CVA6 core

Ara Ara is a vector unit working as a coprocessor for the CVA6 core. It supports the RISC-V Vector Extension, version 0.9. Dependencies Check DEPENDEN

null 138 Jul 4, 2022
Benchmarking a trivial replacement for std::vector

std::vector replacement benchmark Dependencies You'll need gnuplot and bash to run ./bench.sh. In addition to that, you'll need to have gcc and clang

Dale Weiler 7 May 23, 2022
static_vector implementation in terms of std::vector. No need to implement each function again and again ;)

static_vector static_vector implementation in terms of std::vector. No need to implement each function again and again ;) The usage is basically the s

Alex 4 Apr 1, 2022
A dynamic array similar to std::vector

Vector [] vector is a dynamic array implemented using c++ and it has almost all the features of std::vector including iterators. Usage can be used in

Tanish Porwal 1 Dec 21, 2021
Invoke.hpp - std::invoke/std::apply analogs for C++11/14

invoke.hpp std::invoke/std::apply analogs for C++11/14 Requirements gcc >= 4.9 clang >= 3.8 msvc >= 2015 Installation invoke.hpp is a header-only libr

Matvey Cherevko 32 Apr 27, 2022
Cpp-std-fwd - forward declarations for C++ std headers

cpp-std-fwd Forward declarations for most useful runtime classes of the C++ 17 standard library. DISCLAIMER: This project is meant as a proof-of-conce

Philip Trettner 66 Apr 22, 2022
A wrapper for intel SSE/AVX vector instructions

VMath A wrapper for intel SSE/AVX vector instructions This is just a toy thing to figure out what working with intrinsics is like. I tried to keep it

Dennis 7 Apr 24, 2022
A wrapper around std::variant with some helper functions

A wrapper around std::variant with some helper functions

Eyal Amir 1 Oct 30, 2021
Functional Programming Library for C++. Write concise and readable C++ code.

FunctionalPlus helps you write concise and readable C++ code. Table of contents Introduction Usage examples Type deduction and useful error messages T

Tobias Hermann 1.6k Jun 24, 2022
Functional programming style pattern-matching library for C++

Mach7: Pattern Matching for C++ by Yuriy Solodkyy, Gabriel Dos Reis, Bjarne Stroustrup Abstract Pattern matching is an abstraction mechanism that can

Yuriy Solodkyy 1.2k Jun 24, 2022
A powerful, strongly-typed, functional programming language.

SuperForth About A minimal, performant, strongly-typed, and functional programming language focused on being practical and pragmatic, yet powerful eno

Michael Wang 31 Jun 28, 2022
Uwulang - functional, interpreted, weakly typed programming language written in C

uwulang UWU (Ultimate pWogwamming langUage) is a functional, interpreted, weakly typed programming language written in C. fibo :bool:if(:int:smaller(

Elias Fleckenstein 4 May 15, 2022
PLP Project Programming Language | Programming for projects and computer science and research on computer and programming.

PLPv2b PLP Project Programming Language Programming Language for projects and computer science and research on computer and programming. What is PLP L

PLP Language 5 Jun 23, 2022
:computer: C++ Functional Terminal User Interface. :heart:

FTXUI Functional Terminal (X) User interface A simple C++ library for terminal based user interface. Demo: Feature Functional style. Inspired by [1] a

Arthur Sonzogni 3.3k Jul 3, 2022
A fully-functional low-cost reversion of Mutable instrument's Peaks

$peaks, a fully-functional low-cost reversion of Mutable instrument's Peaks Motivation Where I am from it is sometimes very difficult to source compon

null 32 Feb 14, 2022
[WIP] Demo of a minimal but functional Dawn-based WebGPU client and server

dawn client-server example The goal of this demo is to create a minimal but functional Dawn-based WebGPU client and server with the following traits:

Rasmus 15 Nov 20, 2021
Functional C++ iterator library

iter (alpha) Functional C++20 iterator library. Godbolt demo Small, single header, feature-rich, functional C++20 iterator library that aims to be sim

Nasal Daemon 16 Jun 5, 2022
A curated list of awesome things built with the JSON processor and turing-complete functional language jq.

A curated list of awesome things built with the JSON processor and turing-complete functional language jq.

fiatjaf 517 Jun 26, 2022