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
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.
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!
Params:
- type The new "matchable" to be created
- variant... 0..108 comma separated variants of the new matchable type
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
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
Although a matchable is initially defined with up to 108 variants, it may grow to achieve variant counts higher than 108.
Add up to 108 new variants to type.
This may be repeated as needed...
Example: test/programs/max_variants.cpp
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
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
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
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
To benchmark compile time performace with and without forward declarations run:
scripts/test_buildtimes.py
Forward declare a matchable
Example:
#include "matchable/matchable_fwd.h"
MATCHABLE_FWD(DayOfWeek)
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
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
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.
- shtroizel
See also the list of contributors who participated in this project.
This project is licensed under the "BSD 3-Clause License" - see the LICENSE file for details
donations are greatly appreciated, thank you!
- monero: 4BETuKtvLjkT7VBW85HLJ1XPqhr1TqQj11UmEMnphgG4DzNtSgJmK8b3ZmTudnbbjf7oHfNpCPxDG3BWnmVSCrtrNXMnhyv