Skip to content

Function.h basic functionality.

splinterofchaos edited this page Nov 24, 2012 · 2 revisions

All things in pure are defined in the pure namespace. Not all files expose their elements directly to pure, but Function.h does.

MakeT and ForwardT

Initialize<T> is a function object that constructs a T, however, because it must know T to be instantiated, it must be instantiated before calling.

struct Obj { int x, y; };
constexpr auto obj = Initialize<Obj>();
constexpr auto a = obj(1,2), b = obj(3,4);

After executing, a will have type Obj and the value {1,2}. Initialize works by defining its operator overload such that obj(x...) = Obj{x...}.

Make<T>, like Initialize, constructs a T, but forwards its arguments to T's constructor instead of using an initializer list.

struct Obj {
    int x, y;
    Obj(int x,int y) : x(x), y(y) { }
};
constexpr auto obj = Make<Obj>();
constexpr auto a = obj(1,2), b = obj(3,4);

Obj` is required to define a constructor.

MakeT<T> works for any type-dependent T. For example, std::pair and std::tuple could be defined like so:

constexpr auto make_pair = MakeT<std::pair>();
constexpr auto make_tuple = MakeT<std::tuple>();

constexpr auto a = make_pair(1,2), b = make_pair(3,4);
constexpr auto c = make_tuple(1,2,3,4);

ForwardT<T> works like MakeT except that it perfectly forwards the types.

constexpr auto fwd_pair = ForwardT<std::pair>();
int x = 2;
constexpr auto a = fwd_pair(1,x); // std::pair<int,int&>
constexpr auto b = fwd_pair(a,3); // std::pair< std::pair<int,int>&, int >

These types can be used to create helper function objects in one line. They are most helpful when used with auto and decltype.

Binary, Chainable, Transitive

Binary<F> is a base class for any binary function type, F, as long is it does not define a one-argument version. It defines two functions for partially applying the first and second arguments, however the base class must explicitly import the first. For example, to define a subtraction type:

constexpr struct Sub : Binary<Sub> {
    using Binary<Sub>::operator();

    template< class X, class Y >
    constexpr auto operator() ( X&& x, Y&& y ) 
        -> decltype( declval<X>() - declval<Y>() )
    {
        return forward<X>(x) - forward<Y>(y);
    }
} sub{};

constexpr auto oneMinus = sub(1); // Binary<Sub>::operator(X)
int negOne = oneMinus(2); // 1 - 2 = -1
constexpr auto dec = sub.with(1); // Binary<Sub>::with(X)
int one = dec(2); // 2-1 = 1

Chainable<F> extends Binary<F> to add overloads that accept any arbitrary number of parameters. If Sub inherited from Chainable instead of Binary, the fallowing would also work:

int zero = sub(10,5,3,2); // ((10 - 5) - 3) - 2 = (5 - 3) - 2 = 2 - 2 =0

Chainable's operator overload would call Sub on the arguments from left to right. In other words, Chainable generalizes left associativity. Any variadically-written left-associative function can be simplified by inheriting from it.

Some operations are not associative, but transitive, and so Transitive<F,Fold> is defined. F is the transitive function and Fold is the function that folds two values. For example, if Fold=And and F=Less (function objects of the operators && and <), and Less is derived from Transitive<Less,And>, then

bool True = less(2,5,10); // 2 < 5 and 5 < 10
bool False = less(10,5); // 10 < 5

It is worth noting that Function.h also defines MakeBinaryT, ForwardBinaryT, MakeChainableT, and ForwardChainableT.

Composition

Several forms of composition are defined.

auto plusFour = compose( plusTwo, plusTwo ); // plusFour(x) = x + 2 + 2
auto staySame = compose( plusTwo, minusTwo ); // staySame(x) = (x-2) + 2

auto sumOverTwo = ncompose( half, add ); // average(x...) = add(x...) / 2

auto pos_neg = bcompose( MakeT<std::pair>(), id, neg ); // pos_neg(x) = pair(x,-x)

and a few others. Note that compose(f,g) is defined as such:

constexpr auto compose = MakeChainableT<Composition>();

Where Composition is a function object containing two function objects. They can be chained, so compose(f,g,h) returns a Composition<Composition<F,G>,H>. ncompose and bcompose are defined similarly.

Utility functions

The fallowing associative (derived from Chainable) functions are defined: add, sub, mult, div, mod, rshift, and lshift. Each is a function object of the type of its name in CamelCase. (For example, Add, Sub.) Each also has an *Eq version. For example, addEq means += and has the type AddEq. And and Or do not have instances defined as their names are reserved.

eq, less and greater fill out the transitive operations.

Finally, Function.h defines Chainable instances of max and min.

Clone this wiki locally