Skip to content

tcp-software/matchable

 
 

Repository files navigation

MATCHABLE

Matchable provides string-convertable and flaggable enumerated types
These types can have properties, and these properties can have observers
Examples can be found under test/programs

Files Needed For Getting Started

Matchable is primarily a header-only library. To get started quickly you could just copy the following two files into your project:

  • include/matchable/matchable.h
  • include/matchable/matchable_fwd.h

If using cmake then you could instead install matchable, set matchable_DIR and use find_package() within your project.

Hello Matchable

The following program can be found at: test/programs/matchable_usage.cpp

#include <iostream>
#include "matchable/matchable.h"


MATCHABLE(
    // type
    DayOfWeek,

    // variants
    Monday,
    Tuesday,
    Wednessday,
    Thursday,
    Friday,
    Saturday,
    Sunday
)


MATCHABLE(
    Status,

    // escape codes can be used for characters that are invalid for identifiers
    success_bng_,              // Status::success_bng_::grab().as_string() == "success!"
    invalid_spc_input,         // Status::invalid_spc_input::grab().as_string() == "invalid input"
    timed_spc_out,             // Status::from_string("timed out") == Status::timed_spc_out::grab()
    nothing_spc_to_spc_do,
    insufficient_spc_memory,
    index_spc_out_spc_of_spc_bounds,

    // optionally, variants can be prefixed with esc_ as needed
    esc_17                     // Status::esc_17::grab().as_string() == "17"

    // see bottom of matchable/matchable.h for all the escape codes
    // also reference test/programs/escapable.cpp for more on this
);


Status::Type foo(std::string day_string)
{
    // create an instance of the DayOfWeek matchable
    DayOfWeek::Type day_of_week;

    // all matchable types have a hidden "nil" variant
    // at this point day_of_week == DayOfWeek::nil
    assert(day_of_week == DayOfWeek::nil);

    // nil is a valid and usable variant but its syntax is a bit different
    // notice how we don't "grab()" nil like we do for the other variants
    // also, to test for nil we could alternatively do:
    assert(day_of_week.is_nil());

    // lets try to assign the variant from the day_string parameter
    // note the complexity for from_string() is O(log N)
    // if day_string is equal to the output of "as_string()" for any defined variant,
    // then day_of_week will be set to that variant
    // if no variant is found then nil will be assigned instead
    day_of_week = DayOfWeek::from_string(day_string);
    if (day_of_week.is_nil())
        return Status::invalid_spc_input::grab();

    // to show how flags work, we'll create a flags instance and flag our favorite days
    DayOfWeek::Flags favorite_days;
    for (int i = 0; i < 3; ++i)
        favorite_days.set(DayOfWeek::from_index(i)); // note the complexity for from_index is O(1)

    // oops, actually we don't like Wednessday after all
    favorite_days.unset(DayOfWeek::Wednessday::grab());

    // to get all flagged variants we can use currently_set()
    for (auto day : favorite_days.currently_set())
        if (day == day_of_week)
            std::cout << day << " is one of my favorite days! ";

    // we can also use is_set() to check for flagged variants
    if (favorite_days.is_set(DayOfWeek::Friday::grab()))
        return Status::esc_17::grab();

    // we could also use MATCHABLE_INSTANCE_IN()
    if (!MATCHABLE_INSTANCE_IN(DayOfWeek, day_of_week, Monday, Tuesday))
        return Status::nothing_spc_to_spc_do::grab();

    return Status::success_bng_::grab();
}


int main()
{
    std::cout << "variant iteration by index:" << std::endl;
    for (auto day_of_week : DayOfWeek::variants_by_index())
        std::cout << "    " << day_of_week << std::endl;

    std::cout << "\nvariant iteration by string (alphabetic):" << std::endl;
    for (auto day_of_week : DayOfWeek::variants_by_string())
        std::cout << "    " << day_of_week << std::endl;

    std::cout << "\noutput of foo(\"bad input\") --> " << foo("bad input") << std::endl;
    std::cout << "output of foo(\"Saturday\") --> " << foo("Saturday") << std::endl;
    std::cout << "output of foo(\"Tuesday\") --> " << foo("Tuesday") << std::endl;

    return 0;
}

Output for the program above:

$  ./matchable_usage
variant iteration by index:
    Monday
    Tuesday
    Wednessday
    Thursday
    Friday
    Saturday
    Sunday

variant iteration by string (alphabetic):
    Friday
    Monday
    Saturday
    Sunday
    Thursday
    Tuesday
    Wednessday

output of foo("bad input") --> invalid input
output of foo("Saturday") --> nothing to do
output of foo("Tuesday") --> Tuesday is one of my favorite days! success!

Macro-API

Macros for creating new types

MATCHABLE(type, variant...)

Params:

  • type The new "matchable" to be created
  • variant... 0..108 comma separated variants of the new matchable type

PROPERTYx1_MATCHABLE(property_type, property_name, type, variant...)

Same as MATCHABLE(), but injects a property called property_name of type property_type
Params:

  • property_type type of injected property
  • property_name name of injected property
  • type name of the new "matchable" to be created
  • variant... 0..108 comma separated variants of the new matchable

The property is available both as a single value and as a vector with:

property_type const & as_property_name()
std::vector<property_type> const & as_property_name_vect()

void set_property_name(property_type const &)
void set_property_name_vect(std::vector<property_type> const &)

Examples:
test/programs/cards.cpp
test/programs/matchbox.cpp
test/programs/sorting.cpp

PROPERTYx*_MATCHABLE()

Similar to PROPERTYx1_MATCHABLE(), the following macros exist for injecting multiple properties when creating matchables.

PROPERTYx2_MATCHABLE()
PROPERTYx3_MATCHABLE()
PROPERTYx4_MATCHABLE()
PROPERTYx5_MATCHABLE()
PROPERTYx6_MATCHABLE()
PROPERTYx7_MATCHABLE()
PROPERTYx8_MATCHABLE()
PROPERTYx9_MATCHABLE()
PROPERTYx10_MATCHABLE()
PROPERTYx11_MATCHABLE()
PROPERTYx12_MATCHABLE()
PROPERTYx13_MATCHABLE()
PROPERTYx14_MATCHABLE()
PROPERTYx15_MATCHABLE()
PROPERTYx16_MATCHABLE()
PROPERTYx17_MATCHABLE()
PROPERTYx18_MATCHABLE()
PROPERTYx19_MATCHABLE()
PROPERTYx20_MATCHABLE()
PROPERTYx21_MATCHABLE()

Example: test/programs/relationships.cpp

Going beyond 108 variants

Although a matchable is initially defined with up to 108 variants, it may grow to achieve variant counts higher than 108.

GROW_MATCHABLE(type, variant...)

Add up to 108 new variants to type.
This may be repeated as needed...
Example: test/programs/max_variants.cpp

Macros for setting properties at link-time

MATCHABLE_VARIANT_PROPERTY_VALUE(type, variant, property_name, property_value)

Params:

  • type A matchable type
  • variant A variant of type
  • property_name A property available to type.
  • property_value The new value to be set

Calls type::variant::grab().set_property_name(property_value) at link-time.
Examples:
test/programs/cards.cpp
test/programs/relationships.cpp

MATCHABLE_VARIANT_PROPERTY_VALUES(type, variant, property_name, property_values...)

Params:

  • type A matchable type
  • variant A variant of type
  • property_name A property available to type
  • property_values... The new values to be set

Calls type::variant::grab().set_property_name_vect() with a vector formed by the given property_values...
Example: test/programs/relationships.cpp

Run-time macros

UNMATCHABLE(type, variant...)

Removes variants for the current scope. When the scope exits the removed variants are restored.
For matchables defined within namespaces the following macros are available:

  • NAMESPACEx1_UNMATCHABLE()
  • NAMESPACEx2_UNMATCHABLE()
  • NAMESPACEx3_UNMATCHABLE()
  • NAMESPACEx4_UNMATCHABLE()
  • NAMESPACEx5_UNMATCHABLE()

Further namespace nesting beyond 5 is supported by using the Unmatchable template directly (see example)
Example: test/programs/unmatchable.cpp

MATCHABLE_INSTANCE_IN(type, instance, variant...) -> bool

returns true if instance is one of the given variants or false otherwise
Example:

bool is_weekday(DayOfWeek::Type day)
{
    return MATCHABLE_INSTANCE_IN(DayOfWeek, day, Monday, Tuesday, Wednesday, Thursday, Friday);
}

Example: test/programs/relationships.cpp

Forward Declaring Matchables

To benchmark compile time performace with and without forward declarations run:

scripts/test_buildtimes.py

MATCHABLE_FWD(type)

Forward declare a matchable
Example:

#include "matchable/matchable_fwd.h"
MATCHABLE_FWD(DayOfWeek)

Building, Installing and Running Tests

scripts/build_and_install.py

build_and_install.py without arguments will from the project root do:

mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../install ..
make install
cd ..

refer to help for custom build/install directories and other options:

scripts/build_and_install.py -h
  • use of build_and_install.py is of course optional and serves as a reference or example workflow

Running Tests / Examples

Assuming workflow above with install directory under the project root (modify paths accordingly for your workflow).

List all tests:

install/share/matchable/test/bin/list.sh

Run Single Test (for example "max_variants"):

install/share/matchable/test/bin/run_test.sh max_variants

Run Quietly:

install/share/matchable/test/bin/run_test.sh max_variants quietly

Run All Tests:

install/share/matchable/test/bin/run_all.sh

Run All Tests Quietly

install/share/matchable/test/bin/run_all.sh quietly

Supported Systems

Artix Linux, amd64
Gentoo Linux, sparc64

Matchable should work on any unix-like system but only Artix and Gentoo are supported. OpenBSD, sparc64 is also known to work but you may need to use the "-c" option when building.

Authors

  • shtroizel

See also the list of contributors who participated in this project.

License

This project is licensed under the "BSD 3-Clause License" - see the LICENSE file for details

Donating To This Project

donations are greatly appreciated, thank you!

  • monero: 4BETuKtvLjkT7VBW85HLJ1XPqhr1TqQj11UmEMnphgG4DzNtSgJmK8b3ZmTudnbbjf7oHfNpCPxDG3BWnmVSCrtrNXMnhyv

About

MATCHABLE (forked from shtroizel/matchable)

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages

  • C++ 94.1%
  • Python 4.3%
  • CMake 1.3%
  • Shell 0.3%