Skip to content

Commit

Permalink
Update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
pfultz2 committed Apr 28, 2016
1 parent 88c28bf commit a9f81ca
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 209 deletions.
227 changes: 35 additions & 192 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
Introduction
============

Fit is a header-only C++11/C++14 library that provides utilities for functions and function objects.
Fit is a header-only C++11/C++14 library that provides utilities for functions and function objects, which can solve many problems with much simpler constructs than whats traditionally been done with metaprogramming.

Fit is:

Expand All @@ -18,213 +16,58 @@ Fit is divided into three components:
Github: [http://github.com/pfultz2/Fit](http://github.com/pfultz2/Fit)

Documentation: [http://fit.readthedocs.org](http://fit.readthedocs.org)
Quick Start
===========

Function Objects
----------------

In C++, a function object is just a class that overrides the call operator like this:

// A sum function object
struct sum_f
{
template<class T, class U>
auto operator()(T x, U y) const
{
return x + y;
}
};

There are few things to note about this. First, the call operator member function is always declared `const`, which is generally required to be used with Fit.(Note: The [`mutable_`](http://fit.readthedocs.org/en/latest/mutable/index.html) adaptor can be used to make a mutable function object have a `const` call operator, but this should generally be avoided). Secondly, the `sum_f` class must be constructed first before it can be called:

auto three = sum_f()(1, 2);

We can make it behave like a regular function if we construct the class as a global variable. The Fit library provides [`FIT_STATIC_FUNCTION`](http://fit.readthedocs.org/en/latest/function/index.html) to properly initialize the the function object at compile-time to avoid the [static initialization order fiasco](https://isocpp.org/wiki/faq/ctors#static-init-order) and possible ODR violations:

FIT_STATIC_FUNCTION(sum) = sum_f();

Adaptors
--------

Now we have defined the function as a function object, we can add new "enhancements" to the function. We could make the function pipable using the [`pipable`](http://fit.readthedocs.org/en/latest/pipable/index.html) adaptor:

FIT_STATIC_FUNCTION(sum) = pipable_adaptor<sum_f>();

This allows the parameters to piped into it, like this:

auto three = 1 | sum(2);

Or we could make it an infix named operator as well using the [`infix`](http://fit.readthedocs.org/en/latest/infix/index.html) adaptor:
Motivation
==========

FIT_STATIC_FUNCTION(sum) = infix_adaptor<sum_f>();
- Improve the expressiveness and capabilities of functions, including first-class citzens for function overload set, extension methods, infix operators and much more.
- Simplify constructs in C++ that have generally required metaprogramming
- Enable point-free style programming
- Workaround the limitations of lambdas in C++14

And it could be called like this:

auto three = 1 <sum> 2;

Additionally each of the adaptors have a corresponding function version without the `_adaptor` suffix. So we could pass `sum` as a variable to the adaptors to make new functions. So we can do things like partial application and function composition if we wanted to:

auto add_1 = partial(sum)(1);
auto add_2 = compose(add_1, add_1);
auto three = add_2(1);

Lambdas
-------

Instead of writing function objects which can be a little verbose, we can write the functions as lambdas instead. However, by default lambdas cannot be statically initialized at compile time. So we can use the [`FIT_STATIC_LAMBDA`](http://fit.readthedocs.org/en/latest/lambda/index.html) to initialize them at compile time:

FIT_STATIC_FUNCTION(sum) = FIT_STATIC_LAMBDA(auto x, auto y)
{
return x + y;
};
Requirements
============

And we can apply the same adaptors as well:
This requires a C++11 compiler. There are no third-party dependencies. This has been tested on clang 3.4-3.7, gcc 4.6-4.9, and Visual Studio 2015. Gcc 5.1 is not supported at all.

// Pipable sum
FIT_STATIC_FUNCTION(sum) = pipable(FIT_STATIC_LAMBDA(auto x, auto y)
{
return x + y;
});
Contexpr support
----------------

We can also use [`FIT_STATIC_LAMBDA_FUNCTION`](http://fit.readthedocs.org/en/latest/lambda.md) so we dont have to repeat [`FIT_STATIC_LAMBDA`](lambda/index.html) for adaptors, and it can help avoid possible ODR violations as well:
Both MSVC and gcc 4.6 have limited constexpr support due to many bugs in the implementation of constexpr. However, constexpr initialization of functions is supported when using the `FIT_STATIC_FUNCTION` and `FIT_STATIC_LAMBDA_FUNCTION` constructs.
Building
========

// Pipable sum
FIT_STATIC_LAMBDA_FUNCTION(sum) = pipable([](auto x, auto y)
{
return x + y;
});
The Fit library uses cmake to build. To configure with cmake create a build directory, and run cmake:

As we will see, this can help make it cleaner when we are defining several lambdas, such as for overloading.
mkdir build
cd build
cmake ..

Overloading
-----------
Installing
----------

Now, Fit provides two ways of doing overloading. The [`match`](http://fit.readthedocs.org/en/latest/match/index.html) adaptor will call a function based on C++ overload resolution, which tries to find the best match, like this:
To install the library just run the `install` target:

FIT_STATIC_LAMBDA_FUNCTION(print) = match(
[](int x)
{
std::cout << "Integer: " << x << std::endl;
},
[](const std::string& x)
{
std::cout << "String: " << x << std::endl;
}
);
cmake --build . --target install

However, when trying to do overloading involving something more generic, it can lead to ambiguities. So the [`conditional`](http://fit.readthedocs.org/en/latest/conditional/index.html) adaptor will pick the first function that is callable. This allows ordering the functions based on which one is more important. Say we would like to write a `print` function that can print not only using `cout` but can also print the values in ranges. We could write something like this:
Tests
-----

The tests can be built and run by using the `check` target:

FIT_STATIC_LAMBDA_FUNCTION(print) = conditional(
[](const auto& x) -> decltype(std::cout << x, void())
{
std::cout << x << std::endl;
},
[](const auto& range)
{
for(const auto& x:range) std::cout << x << std::endl;
}
);
cmake --build . --target check

So the `-> decltype(std::cout << x, void())` will only make the function callable if `std::cout << x` is callable. Then the `void()` is used to return `void` from the function. We can constrain the second overload as well, but we will need some helper function in order to call `std::begin` using ADL lookup:
Documentation
-------------

namespace adl {
The documentation is built using mkdocs. Mkdocs can be easily installed by using `pip`:

using std::begin;
pip install mkdocs

template<class R>
auto adl_begin(R&& r) -> FIT_RETURNS(begin(r));
}
Then the docs can be built using these commands:

FIT_STATIC_LAMBDA_FUNCTION(print) = conditional(
[](const auto& x) -> decltype(std::cout << x, void())
{
std::cout << x << std::endl;
},
[](const auto& range) -> decltype(std::cout << *adl::adl_begin(range), void())
{
for(const auto& x:range) std::cout << x << std::endl;
}
);

Tuples
------

We could extend this to printing tuples as well. We will need to combine a couple of functions to make a `for_each_tuple`, which let us call a function for each element. First, the [`by`](http://fit.readthedocs.org/en/latest/by.md) adaptor will let us apply a function to each argument passed in, and the [`unpack`](unpack/index.html) adaptor will unpack the elements to a tuple and apply them to the argument:

FIT_STATIC_LAMBDA_FUNCTION(for_each_tuple) = [](const auto& sequence, auto f)
{
return unpack(by(f))(sequence)
};

So now we can add an overload for tuples:

FIT_STATIC_LAMBDA_FUNCTION(print) = conditional(
[](const auto& x) -> decltype(std::cout << x, void())
{
std::cout << x << std::endl;
},
[](const auto& range) -> decltype(std::cout << *adl::adl_begin(range), void())
{
for(const auto& x:range) std::cout << x << std::endl;
},
[](const auto& tuple) -> decltype(for_each_tuple(tuple, identity), void())
{
return for_each_tuple(tuple, [](const auto& x)
{
std::cout << x << std::endl;
});
}
);

Since we can't use a lambda inside of `decltype` we just put [`identity`](http://fit.readthedocs.org/en/latest/identity/index.html) instead.

Recursive
---------

Even though we are using lambdas, we can easily make this recursive using the [`fix`](http://fit.readthedocs.org/en/latest/fix/index.html) adaptor. This implements a fix point combinator, which passes the function(ie itself) in as the first argument, so we could write this:

FIT_STATIC_LAMBDA_FUNCTION(print) = fix(conditional(
[](auto, const auto& x) -> decltype(std::cout << x, void())
{
std::cout << x << std::endl;
},
[](auto self, const auto& range) -> decltype(self(*adl::adl_begin(range)), void())
{
for(const auto& x:range) self(x);
},
[](auto self, const auto& tuple) -> decltype(for_each_tuple(tuple, self), void())
{
return for_each_tuple(tuple, self);
}
));

Variadic
--------

We can also make this `print` function varidiac, so it prints every argument passed into it. We just rename our original `print` function to `simple_print`:

FIT_STATIC_LAMBDA_FUNCTION(simple_print) = fix(conditional(
[](auto, const auto& x) -> decltype(std::cout << x, void())
{
std::cout << x << std::endl;
},
[](auto self, const auto& range) -> decltype(self(*adl::adl_begin(range)), void())
{
for(const auto& x:range) self(x);
},
[](auto self, const auto& tuple) -> decltype(for_each_tuple(tuple, self), void())
{
return for_each_tuple(tuple, self);
}
));

And then apply the [`by`](http://fit.readthedocs.org/en/latest/by/index.html) adaptor to `simple_print`:

FIT_STATIC_LAMBDA_FUNCTION(print) = by(simple_print);
python setup.py
mkdocs build

Requirements
============
The final docs will be in the `doc/html` folder.

This requires a C++11 compiler. There are no third-party dependencies. This has been tested on clang 3.4-3.7, gcc 4.6-4.9, and Visual Studio 2015. Gcc 5.1 is not supported at all.
2 changes: 1 addition & 1 deletion doc/genreadme
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash

cat src/index.md src/quickstart.md src/requirements.md | sed -e 's,\](\(.*\)\.md),](http://fit.readthedocs.org/en/latest/\1/index.html\),g'
cat src/index.md src/building.md | sed -e 's,\](\(.*\)\.md),](http://fit.readthedocs.org/en/latest/\1/index.html\),g'
15 changes: 0 additions & 15 deletions doc/src/example_print.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,6 @@ However, there is lot of things that don't print directly to `std::cout` such as
Overloading
-----------

Now, Fit provides two ways of doing overloading. The [`match`](match.md) adaptor will call a function based on C++ overload resolution, which tries to find the best match, like this:

FIT_STATIC_LAMBDA_FUNCTION(print) = match(
[](int x)
{
std::cout << "Integer: " << x << std::endl;
},
[](const std::string& x)
{
std::cout << "String: " << x << std::endl;
}
);

However, when trying to do overloading involving something more generic, it can lead to ambiguities.

The Fit library provides several ways to do overloading. One of the ways is with the [`conditional`](conditional.md) adaptor which will pick the first function that is callable. This allows ordering the functions based on which one is more important. So then the first function will print to `std::cout` if possible otherwise we will add an overload to print a range:


Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ theme: readthedocs
pages:
- Home: 'index.md'
- Introduction:
- 'Getting started': 'quickstart.md'
- 'Getting started': 'gettingstarted.md'
- 'Point-free style' : 'point_free.md'
- 'Definitions' : 'definitions.md'
- 'Basic Concepts' : 'concepts.md'
Expand Down

0 comments on commit a9f81ca

Please sign in to comment.