Skip to content

Commit

Permalink
Issue #153: Rewrite the begining of the quick start guide
Browse files Browse the repository at this point in the history
  • Loading branch information
pfultz2 committed Mar 28, 2016
1 parent be717fd commit 19a05c7
Showing 1 changed file with 45 additions and 22 deletions.
67 changes: 45 additions & 22 deletions doc/src/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,23 @@ Quick Start
Function Objects
----------------

In C++, a function object is just a class that overrides the call operator like this:
The Fit library can work with all different types of functions in C++, including static functions, member functions, lambdas, and function objects. Function pointer(or member function pointers) can only refer to one function in an overload set of functions, and it requires explicit casting to select that overload:

template<class T, class U>
auto sum(T x, U y)
{
return x + y;
}

auto sum_int = (int (*)(int, int))&sum;
// Call integer overload
int i = sum_int(1, 2);
// Or pass to an algorithm
std::vector<int> v = { 1, 2, 3 };
int total = std::accumulate(v.begin(), v.end(), 0, sump);


Instead, a function object allows the ability to encapsulate an entire overload set into one object. This can be done by defining a class that overrides the call operator like this:

// A sum function object
struct sum_f
Expand All @@ -18,32 +34,49 @@ In C++, a function object is just a class that overrides the call operator like

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_`](mutable.md) 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);
auto sum = sum_f();
// Call sum function
auto three = sum(1, 2);
// Or pass to an algorithm
std::vector<int> v = { 1, 2, 3 };
int total = std::accumulate(v.begin(), v.end(), 0, sum);

Because the function is templated, it can be called on any type that has the plus `+` operator, not just integers. Futhermore, the `sum` variable can be used to refer to the entire overload set.

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`](function.md) 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:
Now, this is useful for local functions. However, many times we want to write functions and make them available for others to use. The Fit library provides [`FIT_STATIC_FUNCTION`](function.md) to declare the function object at global or namespace scope:

FIT_STATIC_FUNCTION(sum) = sum_f();

The `FIT_STATIC_FUNCTION` declares a global variable following the best practices as outlined in [N4381](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html). This includes initializing the function object compile-time to avoid the [static initialization order fiasco](https://isocpp.org/wiki/faq/ctors#static-init-order) and the address of the function object is the same across translation units to avoid possible One-Definition-Rule(ODR) violations.

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`](pipable.md) adaptor:
Now we have defined the function as a function object, we can add new "enhancements" to the function. One enhancement is to write "exentsion" methods. The proposal [N4165](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4165.pdf) for Unified Call Syntax(UFCS) in C++17 would have allowed a function call of `x.f(y)` to become `f(x, y)`. Instead we can use pipable function which would transform `x | f(y)` into `f(x, y)`. We could make the `sum` function pipable using the [`pipable`](pipable.md) adaptor:

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

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

auto three = 1 | sum(2);

Or we could make it an infix named operator as well using the [`infix`](infix.md) adaptor:
Pipable function can be chained mutliple times just like the `.` operator:

auto four = 1 | sum(2) | sum(1);

FIT_STATIC_FUNCTION(sum) = infix_adaptor<sum_f>();
Alternatively, instead of using the `|` operator, pipable functions can be chaned together using the [`flow`](flow.md) adaptor:

auto four = flow(sum(2), sum(1))(1);

Another enhancement that can be done to functions is defining named infix operators using the [`infix`](infix.md) adaptor:

FIT_STATIC_FUNCTION(sum) = infix(sum_f());

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:
In addition, adaptors are provided that support simple functional operations such as partial application and function composition:

auto add_1 = partial(sum)(1);
auto add_2 = compose(add_1, add_1);
Expand All @@ -52,31 +85,21 @@ Additionally each of the adaptors have a corresponding function version without
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`](lambda.md) to initialize them at compile time:
Writing function objects can be a little verbose. C++ provides lambdas which have a much terser syntax for defining functions. Of course, lambdas can work with all the adaptors in the library, however, if we want to declare a function using lambdas, [`FIT_STATIC_FUNCTION`](function.md) won't work. Instead, [`FIT_STATIC_LAMBDA_FUNCTION`](lambda.md) can be used to the declare the lambda as a function instead, this will initialize the function at compile-time and avoid possible ODR violations:

FIT_STATIC_FUNCTION(sum) = FIT_STATIC_LAMBDA(auto x, auto y)
FIT_STATIC_LAMBDA_FUNCTION(sum) = [](auto x, auto y)
{
return x + y;
};

And we can apply the same adaptors as well:

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

We can also use [`FIT_STATIC_LAMBDA_FUNCTION`](lambda.md) so we dont have to repeat [`FIT_STATIC_LAMBDA`](lambda.md) for adaptors, and it can help avoid possible ODR violations as well:
Additionally, adaptors can be used, so the pipable version of `sum` can be written like this:

// Pipable sum
FIT_STATIC_LAMBDA_FUNCTION(sum) = pipable([](auto x, auto y)
{
return x + y;
});

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

Overloading
-----------

Expand Down

0 comments on commit 19a05c7

Please sign in to comment.