An open API service indexing awesome lists of open source software.

https://github.com/brunocodutra/alloy

Embrace (post) modern C++
https://github.com/brunocodutra/alloy

cpp cpp17 heterogeneous-algorithms tuples variants visitor

Last synced: 12 months ago
JSON representation

Embrace (post) modern C++

Awesome Lists containing this project

README

          

# Alloy [![version]][semver] [![godbolt.badge]][godbolt.alloy]
Embrace (post) modern C++

## Overview

**Alloy** solves the problem of run-time manipulation of heterogeneous values
with more flexibility and expressive power than the tools provided by the
standard library.

```.cpp
#include "alloy.hpp"

#include
#include
#include

int main() {
// suppose you have some data
auto data = alloy::capture("Hello", ' ', "World" , '!');

// and means to consume it
auto print = [](auto&&... data) {
// we'll simply print to the standard output in this example
(std::cout << ... << std::forward(data)) << std::endl;
};

// all you need is to connect the ends
data >> print; // Hello World!

// just as easily you can build data pipelines
auto predicate = [](auto x) {
return std::string{x} != "!";
};

data >> alloy::filter(predicate) >> print; // Hello World

// with Alloy you can kiss `std::apply` goodbye
alloy::unpack(std::make_tuple(3, '.', "14")) >> print; // 3.14

// you can even iterate through tuples in a regular for-loop
auto tup = std::make_tuple(3, '.', "14");

for(std::size_t i = 0; i < std::tuple_size{}; ++i)
alloy::unpack(tup) >> alloy::at(i) >> alloy::prepend(i, ": ") >> print;

// 0: 3
// 1: .
// 2: 14

// `std::visit` is also a thing of the past
using var = std::variant;

var i = 3;
var c = '.';
var s = "14";

alloy::unpack(i, c, s) >> print; // 3.14

// while you are at it, why not mixing tuples and variants together?
alloy::unpack(std::make_tuple("pi", '='), i, c, s) >> print; // pi=3.14

// tuples and variants are too mainstream?
// not a problem, you can also provide your very own custom data sources
auto produce = [](auto consume) {
return consume("Hello", ' ', "World");
};

// the following are all equivalent and print Hello World
alloy::source{produce} >> print;
produce >> alloy::sink{print};
alloy::source{produce} >> alloy::sink{print};

// and your very own streams too
auto process = [](auto const& sink) {
return [&sink](auto hello, auto _, auto world) {
return sink(hello, _, "brave", _, "new", _, world, '!');
};
};

produce >> alloy::stream{process} >> print; // Hello brave new World!

// embrace (post) modern C++

auto wrap = [](auto const& sink) {
return [&sink](auto word) {
return sink('(', word, ')');
};
};

alloy::forward("post") >> alloy::stream{wrap}
>> alloy::prepend("embrace", ' ')
>> alloy::append(' ', "modern C++") >> print;
}
```

## Motivation

Have you ever wished `std::tuple` provided an overload for `operator []` just
like`std::vector` and `std::map` do?

As you might have guessed, it's actually impossible to overload the subscript
operator for tuples, but the reason why might not be obvious at first.

An example is worth a thousand words, so can you guess the type of `x` below?

```.cpp
auto x = std::make_tuple(1, 1.0, '1', "one")[std::rand() % 4];
```

The type of `x` depends on a run-time value and therefore may not be deduced by
the compiler, which is thus unable to compile such a program to begin with.

Fine, as long as we are able to, say, filter the elements in a `std::tuple`
based on a predicate, we should be able to tackle most real world problems.
That should be easy right?

Nope.

Can you deduce the type of `x` this time?

```.cpp
template
decltype(auto) filter(Tuple&&, Predicate&&);

auto x = filter(std::make_tuple(1, 1.0, '1', "one"), [](auto&&) {
return std::rand() % 2;
});
```

Heck, the predicate doesn't even depend on the elements themselves, but still we
are unable to deduce the return type!

Alright, forget about returning values, let's forward results to a callback
function instead.

```.cpp
template
void filter(Tuple&&, Predicate&&, Callback&&);

auto predicate = [](auto&&) {
return std::rand() % 2;
};

auto callback = [](auto&&... /*args*/) { /* ... */ };

filter(std::make_tuple(1, 1.0, '1', "one"), predicate, callback);
```

That was easy after all... or was it? Let us not forget that we still need to
implement `filter`.

How can the standard library help us get there, you might have asked yourself,
and the answer is quite simple in fact: _It can't really._

Sure `std::apply` can help us extract the elements out of the tuple, but that's
about all the standard library can do for us, from then on we are on our own.

```.cpp
template
void filter(Tuple&& tuple, Predicate&& predicate, Callback&& callback) {
constexpr auto impl = [&predicate, &callback](auto&&... elements) {
// TODO: do the heavy lifting :(
};

std::apply(std::forward(tuple), impl);
}
```

We don't want to keep reinventing the wheel, we want to solve real problems, but
for that we need the building blocks.

```.cpp
auto tuple = std::make_tuple(1, 1.0, '1', "one");

auto predicate = [](auto&&) {
return std::rand() % 2;
};

auto callback = [](auto&&... /*args*/) { /* ... */ };

alloy::unpack(tuple) >> alloy::filter(predicate) >> callback;
```

We need Alloy.

## Quick Start

1. Download [alloy.hpp][releases]
2. `# include `
3. Embrace (post) modern C++

## Portability

Alloy requires C++17 and is known to work on GCC and Clang.

## License

This project is licensed under the [MIT][license].

[version]: https://badge.fury.io/gh/brunocodutra%2Falloy.svg
[semver]: https://semver.org

[godbolt.alloy]: https://godbolt.org/z/4P38zfnMh
[godbolt.badge]: https://img.shields.io/badge/try%20it-on%20godbolt-222266.svg

[license]: https://github.com/brunocodutra/alloy/blob/master/LICENSE
[releases]: https://github.com/brunocodutra/alloy/releases