LIBF++ 0.2 DOCUMENTATION


Types

Modules


Union

namespace F
{
    template <typename T1, ..., typename Tn>
    struct Union
    {
        ...
        template <typename U> Union(const U &x);
        template <typename U> operator const U&() const;
        template <typename U> constexpr unsigned index();
    };

    template <typename T1, ..., typename Tn>
        pure unsigned index(Union<T1, ..., Tn> u);
}

A discriminated union between n child types.

A Union object can be constructed from an object of any child type using an up-cast, e.g.:

    Ti x = ...;
    auto u = (Union<T1, ..., Tn>)x;
This will create a copy of x and assumes Ti is copy constructible.

The child object can be retrieved via a downcast, e.g.:

    auto &y = (Ti &)u;
    auto y  = (Ti)u;   // Alternative
Note that it is undefined (nullptr dereference) if u was not constructed from a Ti object.

The type of a Union object can be retrieved at runtime via the index function, e.g.:

    switch(index(u)) { ... }
The index function returns 0 if u is the first child type, 1 for the second child type, etc. The index of a child type can be determined via the static function:
    Union<T1, ..., Tn>::index<Ti>() = i-1

A Union object is compact, i.e.

    sizeof(Union<T1, ..., Tn>) = sizeof(void *)
This means that Union objects can be passed-by-copy without performance loss. A Union object is implemented internally as tagged pointers, and can support up to 16 child types.

See the documentation on value functions for more information.

Example 1

We can use a Union to define a simple Maybe type as follows:

    struct Nothing { };
    template <typename T>
    using Maybe = F::Union<Nothing, T>;
    #define NOTHING Maybe<void>::index<Nothing>()
    #define JUST    Maybe<void>::index<void>()
This is analogous to the Maybe type from Haskell.

We can define Maybe comparison as follows:

    template <typename T>
    int compare(Maybe<T> x, Maybe<T> y)
    {
        switch (index(x))
        {
        case NOTHING:
            switch (index(y))
            {
            case NOTHING:
                return 0;
            case JUST:
                return 1;
            }
        case JUST:
            switch (index(y))
            {
            case NOTHING:
                return -1;
            case JUST:
                return compare((T)x, (T)y);
            }
        }
    }

Example 2

We can define a List type as follows:

    template <typename T> struct ListNode;
    struct ListEmpty;

    template <typename T>
    using List = F::Union<ListNode<T>, ListEmpty>;

    template <typename T>
    struct ListNode
    {
        T elem;
        List<T> next;
    };

    #define LIST_EMPTY List<void>::index<ListEmpty>()
    #define LIST_NODE  List<void>::index<ListNode<void>>()

We can calculate the length of a list as follows:

    template <typename T>
    int length(List<T> xs)
    {
        int len = 0;
        while (true)
        {
            switch (index(xs))
            {
            case LIST_EMPTY:
                return len;
            case LIST_NODE:
                len++;
                auto &node = (ListNode &)xs;
                xs = node.next;
            }
        }
    }

Result

namespace F
{
    template <typename T1, ..., typename Tn>
    struct Result
    {
        ...
    };
}

A specialized tuple of objects. Unlike the related Tuple type, a Result can be used to return multiple values from a function without relying on heap allocated memory. Furthermore, Result can be matched using C++17's structure binding feature:

    Result<float, float, float> get_point_3D(...);
    auto [x, y, z] = get_point_3D(...);
This provides a convenient way to extract multiple return values from a function call.

An object of type Result is not necessarily compact.


Tuple

namespace F
{
    template <typename T1, ..., typename Tn>
    struct Tuple
    {
        ...
    };
}

A tuple of objects. Unlike the related Result type, a a Tuple is a compact object and relies on heap allocation.

See the documentation on tuple functions for more information.


Optional

namespace F
{
    template <typename T>
    struct Optional
    {
        ...
    };
}

An optional object that may or may not hold the underlying value. Unlike the related Maybe type, an Optional is not a compact object.

See the documentation on value functions for more information.


Maybe

namespace F
{
    struct Nothing
    {
        // Empty
    };

    template <typename T>
    using Maybe = Union<Nothing, T>;

    enum
    {
        NOTHING = Maybe<void>::index<Nothing>(),
        JUST    = Maybe<void>::index<void>()
    };
}

A maybe type that may or may not hold a value. Unlike the related Optional type, a Maybe is a compact object and is more likely to rely on heap allocation.

See the documentation on maybe functions for more information.


List

namespace F
{
    struct Nil
    {
        // Empty
    };
    template <typename T>
    struct Node;

    template <typename T>
    using List = Union<Nil, Node<T>>;

    template <typename T>
    struct Node
    {
        Value<T> elem;
        List<T> next;
    };

    enum
    {
        NIL  = List<void>::index<Nil>(),
        NODE = List<void>::index<Node<void>>()
    };
}

A (singularly-linked) list type. See the documentation on list functions for more information.


Map

namespace F
{
    template <typename K, typename V>
    struct Map
    {
        ...
    };
}

A key-value map type. See the documentation on map functions for more information.


Set

namespace F
{
    template <typename T>
    struct Set
    {
        ...
    };
}

A set type. See the documentation on set functions for more information.


Vector

namespace F
{
    template <typename T>
    struct Vector
    {
        ...
    };
}

A vector type. See the documentation on vector functions for more information.


String

namespace F
{
    struct String
    {
        ...
    };
}

A unicode string type. See the documentation on string functions for more information.