test_named_parameters.cpp

Back to Named parameters

test/pastel/sys/

// Description: Testing for named parameters
// Documentation: named_parameter.txt

#include "test/test_init.h"

#include <pastel/sys/function/identity_function.h>

#include <string>

namespace
{

    enum class Enum
    {
        Off,
        On
    };

    struct Offset
    {
        explicit Offset(integer id_ = 0)
            : amount(id_)
        {
        }

        Offset(const Offset& that)
            : amount(0)
        {
        }

        Offset(Offset&& that)
            : amount(that.amount)
        {
            that.amount = 0;
        }

        integer amount;
    };

    struct Euclidean_Metric
    {
        template <typename Type>
        decltype(auto) operator()(Type x, Type y) const
        {
            return ((x - y) * (x - y));
        }
    };

    struct Manhattan_Metric
    {
        template <typename Type>
        decltype(auto) operator()(Type x, Type y) const
        {
            return std::abs(x - y);
        }
    };

    template <
        typename Point,
        typename... ArgumentSet,
        Requires<
            std::is_integral<Point>
        > = 0
    >
    float distance(
        const Point& a, const Point& b, 
        ArgumentSet&&... argumentSet)
    {
        float scaling = 
            PASTEL_ARG(
                scaling,
                [](){return 1.0;},
                [](auto input) 
                {
                    return std::is_convertible<decltype(input), float>();
                }
            );

        auto&& metric = 
            PASTEL_ARG(
                metric, 
                [](){return Euclidean_Metric();}, 
                [](auto input) {return
                    implicitArgument(Or<
                        std::is_same<decltype(input), Euclidean_Metric>,
                        std::is_same<decltype(input), Manhattan_Metric>
                    >());} 
            );

        bool negate = 
            PASTEL_ARG(
                negate, 
                [](){return false;},
                [](auto input) {return std::is_same<decltype(input), bool>();} 
            );

        auto offset =
            PASTEL_ARG_S(offset, Offset());

        Enum enumValue =
            PASTEL_ARG_ENUM(enumValue, Enum::Off);

        return metric(a, b) * scaling * (negate ? -1 : 1) + offset.amount + (integer)enumValue;
    }

    struct A {};

}

TEST_CASE("Tag (named_parameter)")
{
    // The tag hashing uses the Fowler-Noll-Vo 1a hash-function 
    // (FNV-1a) for 32-bit integers. Check the hashes of some
    // known strings. Initially I had a bug in the implementation 
    // which I noticed only because I had a hash collision between
    // these strings.
    PASTEL_STATIC_ASSERT(tagHash("translation") == 3419592236UL);
    PASTEL_STATIC_ASSERT(tagHash("orientation") == 3309681697UL);

    PASTEL_STATIC_ASSERT("translation"_tag == 3419592236UL);
    PASTEL_STATIC_ASSERT("orientation"_tag == 3309681697UL);

    // These are the only english words which collide under the
    // FNV1a hash function.
    PASTEL_STATIC_ASSERT(tagHash("liquid") == tagHash("costarring"));
    PASTEL_STATIC_ASSERT("liquid"_tag == "costarring"_tag);
}

TEST_CASE("Empty (Empty)")
{
    REQUIRE(distance(1, 6) == 5 * 5);
}

TEST_CASE("Explicit (named_parameter)")
{
    REQUIRE(distance(1, 6, PASTEL_TAG(scaling), 2.0) == 2 * 5 * 5);
    REQUIRE(distance(1, 6, PASTEL_TAG(scaling), 3.5) == 3.5 * 5 * 5);
    REQUIRE(distance(1, 6, PASTEL_TAG(metric), Manhattan_Metric()) == 5);
    REQUIRE(distance(1, 6, PASTEL_TAG(scaling), 3.5, PASTEL_TAG(metric), Manhattan_Metric()) == 3.5 * 5);
}

TEST_CASE("Enum (named_parameter)")
{
    REQUIRE(distance(1, 6, Enum::Off) == 5 * 5 + 0);
    REQUIRE(distance(1, 6, Enum::On) == 5 * 5 + 1);
}

TEST_CASE("Forwarding (named_parameter)")
{
    // Test that arguments are perfectly forwarded
    {
        Offset offset(543);
        REQUIRE(distance(1, 6, PASTEL_TAG(offset), std::move(offset)) == 5 * 5 + 543);
        REQUIRE(offset.amount == 0);
    }

    {
        Offset offset(543);
        REQUIRE(distance(1, 6, PASTEL_TAG(offset), offset) == 5 * 5 + 0);
        REQUIRE(offset.amount == 543);
    }
}

TEST_CASE("Flag (named_parameter)")
{
    REQUIRE(distance(1, 6, PASTEL_TAG(negate)) == (-1) * 5 * 5);
    REQUIRE(distance(1, 6, PASTEL_TAG(scaling)) == 1 * 5 * 5);
    REQUIRE(distance(1, 6, PASTEL_TAG(scaling), 2.0, PASTEL_TAG(negate), PASTEL_TAG(metric), Manhattan_Metric()) == 2 * (-1) * 5);
}

TEST_CASE("Implicit (named_parameter)")
{
    REQUIRE(distance(1, 6, Manhattan_Metric()) == 5);
    // Since 'negate' is not an implicit parameter,
    // the 'true' will not bind to it.
    REQUIRE(distance(1, 6, Manhattan_Metric(), true) == 5);
}

TEST_CASE("Erroneous (named_parameter)")
{
    // These should give errors at compile-time.

    // Error: Multiple arguments for 'negate'.
    //distance(1, 6, PASTEL_TAG(negate), PASTEL_TAG(negate));
    //distance(1, 6, PASTEL_TAG(scaling));
    //distance(1, 6, true, true);

    // Error: 'metric' is required to be either Manhattan_Metric or Euclidean_Metric.
    //distance(1, 6, PASTEL_TAG(metric), A());
    //distance(1, 6, PASTEL_TAG(metric));

    // Error: 'negate' is required to be of type bool.
    //distance(1, 6, PASTEL_TAG(negate), 4.0f);
}