SkillAgentSearch skills...

FunctionalPlus

Functional Programming Library for C++. Write concise and readable C++ code.

Install / Use

/learn @Dobiasd/FunctionalPlus

README

logo

CI (License Boost 1.0)

FunctionalPlus

helps you write concise and readable C++ code.

Table of contents

Introduction

Great code should mostly be self-documenting, but while using C++ in reality you can find yourself dealing with low-level stuff like iterators or hand-written loops that distract from the actual essence of your code.

FunctionalPlus is a small header-only library supporting you in reducing code noise and in dealing with only one single level of abstraction at a time. By increasing brevity and maintainability of your code it can improve productivity (and fun!) in the long run. It pursues these goals by providing pure and easy-to-use functions that free you from implementing commonly used flows of control over and over again.

Say you have a list of numbers and are interested in the odd ones only.

bool is_odd_int(int x) { return x % 2 != 0; }

int main()
{
    typedef vector<int> Ints;
    Ints values = {24, 11, 65, 44, 80, 18, 73, 90, 69, 18};
    // todo: get odd numbers from values ...
}

There are different possibilities to attain your goal. Some of them are:

  1. write a (range based) for loop
    Ints odds;
    for (int x : values)
    {
        if (is_odd_int(x))
        {
            odds.push_back(x);
        }
    }
  1. use std::copy_if from the STL
    Ints odds;
    std::copy_if(std::begin(values), std::end(values),
            std::back_inserter(odds), is_odd_int);
  1. use keep_if from FunctionalPlus
    auto odds = fplus::keep_if(is_odd_int, values);

If you think version 3 could be the one most pleasant to work with, you might like FunctionalPlus. And if you still think the hand-written for loop is easier to understand, also consider what would happen if the loop body (i.e. a corresponding lambda function in the call to fplus::keep_if) would be much longer. When reading keep_if you would still immediately know that odds can only contain elements that came from values and were selected by some, possibly complicated, predicate. In the for loop case you have no idea what is happening until you read the whole loop body. The loop version probably would need a comment at the top stating what the use of keep_if would tell at first glance.

Usage examples

Below are some short examples showing nice things you can do with functions and containers using FunctionalPlus.

The same old song

You can test the content of a container for various properties, e.g.

#include <fplus/fplus.hpp>
#include <iostream>

int main()
{
    std::list<std::string> things = {"same old", "same old"};
    if (fplus::all_the_same(things))
        std::cout << "All things being equal." << std::endl;
}

The I in our team

There also are some convenience functions for retrieving properties of containers. For example you can count the occurrences of a character in a string.

#include <fplus/fplus.hpp>
#include <iostream>

int main()
{
    std::string team = "Our team is great. I love everybody I work with.";
    std::cout << "There actually are this many 'I's in team: " <<
        fplus::count("I", fplus::split_words(false, team)) << std::endl;
}

Output:

There actually are this many 'I's in team: 2

The cutest kitty

Finding the highest rated element in a container is very simple compared to a hand-written version(1, 2).

#include <fplus/fplus.hpp>
#include <iostream>

struct cat
{
    double cuteness() const
    {
        return softness_ * temperature_ * roundness_ * fur_amount_ - size_;
    }
    std::string name_;
    double softness_;
    double temperature_;
    double size_;
    double roundness_;
    double fur_amount_;
};

void main()
{
    std::vector<cat> cats = {
        {"Tigger",   5, 5, 5, 5, 5},
        {"Simba",    2, 9, 9, 2, 7},
        {"Muffin",   9, 4, 2, 8, 6},
        {"Garfield", 6, 5, 7, 9, 5}};

    auto cutest_cat = fplus::maximum_on(std::mem_fn(&cat::cuteness), cats);

    std::cout << cutest_cat.name_ <<
        " is happy and sleepy. *purr* *purr* *purr*" << std::endl;
}

Output:

Muffin is happy and sleepy. *purr* *purr* *purr*

Function composition, binding, and map creation

Let's say you have the following function given.

std::list<int> collatz_seq(int x);

And you want to create an std::map<std::uint64_t, std::string> containing string representations of the Collatz sequences for all numbers below 30. You can implement this nicely in a functional way too.

#include <fplus/fplus.hpp>
#include <iostream>

// std::list<std::uint64_t> collatz_seq(std::uint64_t x) { ... }

int main()
{
    typedef std::list<int> Ints;

    // [1, 2, 3 ... 29]
    auto xs = fplus::numbers<Ints>(1, 30);

    // A function that does [1, 2, 3, 4, 5] -> "[1 => 2 => 3 => 4 => 5]"
    auto show_ints = fplus::bind_1st_of_2(fplus::show_cont_with<Ints>, " => ");

    // A composed function that calculates a Collatz sequence and shows it.
    auto show_collats_seq = fplus::compose(collatz_seq, show_ints);

    // Associate the numbers with the string representation of their sequences.
    auto collatz_dict = fplus::create_map_with(show_collats_seq, xs);

    // Print some of the sequences.
    std::cout << collatz_dict[13] << std::endl;
    std::cout << collatz_dict[17] << std::endl;
}

Output:

[13 => 40 => 20 => 10 => 5 => 16 => 8 => 4 => 2 => 1]
[17 => 52 => 26 => 13 => 40 => 20 => 10 => 5 => 16 => 8 => 4 => 2 => 1]

The functions shown not only work with default STL containers like std::vector, std::list, std::deque, std::string etc. but also with custom containers providing a similar interface.

Type deduction and useful error messages

FunctionalPlus deduces types for you where possible. Let's take one line of code from the Collatz example:

    auto show_collats_seq = fplus::compose(collatz_seq, show_ints);

collatz_seq is a function taking an uint64_t and returning a list<uint64_t>. show_ints takes a list<uint64_t> and returns a string. By making use of function_traits, written by kennyim, it is possible to automatically deduce the expression fplus::compose(collatz_seq, show_ints) as being a function taking an uint64_t and returning a string, so you do not have to manually provide type hints to the compiler.

If two functions whose "connecting types" do not match are passed in, an unambiguous error message describing the issue will be generated. FunctionalPlus uses compile time assertions to avoid the confusingly long error messages compilers generate when faced with type errors in function templates.

Changing the way you program from "writing your own loops and nested ifs" to "composing and using small functions" will result in more errors at compile time but will pay out by having fewer errors at runtime. Also, more precise compile-time errors will reduce the time spent debugging.

Tutorial

The article "Functional programming in C++ with the FunctionalPlus library; today: HackerRank challenge Gemstones" provides a smooth introduction into the library by showing how one could develop an elegant solution to a problem using the FunctionalPlus approach.

Also on Udemy there is a course "Functional Programming using C++" that makes heavy use of FunctionalPlus to explain general functional concepts.

Forward application and composition

The "Gemstones" tutorial above explains how one can apply functional thinking to arrive at the solution below for the following problem:

Find the number of characters present in every line of an input text.

std::string gemstone_count(const std::string& input)
{
    using namespace fplus;

    typedef std::set<std::string::value_type> characters;

    const auto lines = split_lines(false, input); // false = no empty lines

    const auto sets = transform(
        convert_container<characters, std::string>,
        lines);

    // Build the intersection of all given character sets (one per line).
    const auto gem_elements = fold_left_1(
        set_intersection<characters>, sets);

    return show(size_of_cont(gem_elements));
}

By using the functionality from namespace fwd, you can get along without temporary variables, and make it clear that the whole process is simply pushing the input through a chain of functions, similar to the pipe concept in the Unix command line.

std::string gemstone_count_fwd_apply(const std::string& input)
{
    using namespace fplus;
    typede
View on GitHub
GitHub Stars2.3k
CategoryDevelopment
Updated9d ago
Forks176

Languages

C++

Security Score

100/100

Audited on Mar 18, 2026

No findings