Add simple_match

Add some python build scripts.
This commit is contained in:
Syoyo Fujita
2022-08-08 20:13:57 +09:00
parent 580a2f7b55
commit 10983a50ae
11 changed files with 1293 additions and 71 deletions

View File

@@ -454,6 +454,8 @@ foreach(TINYUSDZ_LIB_TARGET ${TINYUSDZ_LIBS})
# PRIVATE ${PROJECT_SOURCE_DIR}/src/external/ryu/)
target_include_directories(${TINYUSDZ_LIB_TARGET}
PRIVATE ${PROJECT_SOURCE_DIR}/src/external/fast_float/include)
target_include_directories(${TINYUSDZ_LIB_TARGET}
PRIVATE ${PROJECT_SOURCE_DIR}/src/external/simple_match/include)
target_include_directories(
${TINYUSDZ_LIB_TARGET} PRIVATE ${PROJECT_SOURCE_DIR}/src/external/jsonhpp/)
@@ -542,8 +544,12 @@ if(TINYUSDZ_WITH_PYTHON AND (NOT TINYUSDZ_WITH_BLENDER_ADDON))
nanobind_add_module(${BUILD_TARGET_PY} ${TINYUSDZ_PYTHON_BINDING_SOURCES})
add_sanitizers(${BUILD_TARGET_PY})
target_link_libraries(${BUILD_TARGET_PY} PRIVATE ${TINYUSDZ_TARGET_STATIC})
# strip "py" prefix
set_target_properties(${BUILD_TARGET_PY} PROPERTIES OUTPUT_NAME "tinyusdz")
# copy python binding .so file to python/
# For developer
add_custom_command(
TARGET ${BUILD_TARGET_PY}
POST_BUILD
@@ -552,6 +558,9 @@ if(TINYUSDZ_WITH_PYTHON AND (NOT TINYUSDZ_WITH_BLENDER_ADDON))
COMMENT "copying python module file to python/"
VERBATIM)
# For pypi packaging
install(TARGETS ${BUILD_TARGET_PY} LIBRARY DESTINATION tinyusdz)
endif()
if(TINYUSDZ_WITH_TOOL_USDA_PARSER AND TINYUSDZ_WITH_MODULE_USDA_READER)

View File

@@ -301,3 +301,4 @@ TinyUSDZ is licensed under MIT license.
* ubench.h: Unlicense. https://github.com/sheredom/ubench.h
* thelink2012/any : BSL-1.0 license. https://github.com/thelink2012/any
* better-enums : BSD-2 license. https://aantron.github.io/better-enums/
* simple_match : BSL-1.0 license. https://github.com/jbandela/simple_match

7
pyproject.toml Normal file
View File

@@ -0,0 +1,7 @@
[build-system]
requires = ["setuptools>=42", "wheel", "scikit-build", "cmake>=3.16", "ninja"]
build-backend = "setuptools.build_meta"
[tool.pytest.ini_options]
testpaths = [ "tests" ]

View File

@@ -1,5 +1,5 @@
Python binding of tinyusdz.
This folder is used for packaging Python binding of TinyUSDZ
Core part is deletegated to native module(pytinyusd)
Core part is deletegated to native module(tinyusd)
W.I.P.

View File

@@ -1,69 +0,0 @@
# based on https://stackoverflow.com/questions/42585210/extending-setuptools-extension-to-use-cmake-in-setup-py
import os
import pathlib
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext as build_ext_orig
class CMakeExtension(Extension):
def __init__(self, name):
# don't invoke the original build_ext for this special extension
super().__init__(name, sources=[])
class build_ext(build_ext_orig):
def run(self):
for ext in self.extensions:
self.build_cmake(ext)
super().run()
def build_cmake(self, ext):
cwd = pathlib.Path().absolute()
print("cwd = ", cwd)
# these dirs will be created in build_py, so if you don't have
# any python sources to bundle, the dirs will be missing
build_temp = pathlib.Path(self.build_temp)
build_temp.mkdir(parents=True, exist_ok=True)
extdir = pathlib.Path(self.get_ext_fullpath(ext.name))
extdir.mkdir(parents=True, exist_ok=True)
config = 'RelWithDebInfo'
cmake_args = [
'-S{}'.format(os.path.join(cwd, "..")),
'-B{}'.format(build_temp),
'-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + str(extdir.parent.absolute()),
'-DCMAKE_BUILD_TYPE=' + config
]
# example of build args
build_args = [
'--config', config,
'--', '-j4'
]
print("cmake_args = ", cmake_args)
#os.chdir(str(build_temp))
self.spawn(['cmake'] + cmake_args)
if not self.dry_run:
self.spawn(['cmake', '--build', str(build_temp)] + build_args)
# Troubleshooting: if fail on line above then delete all possible
# temporary CMake files including "CMakeCache.txt" in top level dir.
os.chdir(str(cwd))
setup(
name='pytinyusdz',
version='0.8.0rc0',
packages=['pytinyusdz'],
long_description=open("../README.md", 'r').read(),
ext_modules=[CMakeExtension('pytinyusdz')],
license='MIT',
cmdclass={
'build_ext': build_ext,
}
)

72
setup.py Normal file
View File

@@ -0,0 +1,72 @@
# based on https://stackoverflow.com/questions/42585210/extending-setuptools-extension-to-use-cmake-in-setup-py
import os
import pathlib
from skbuild import setup
#from setuptools.command.build_ext import build_ext as build_ext_orig
#class CMakeExtension(Extension):
#
# def __init__(self, name):
# # don't invoke the original build_ext for this special extension
# super().__init__(name, sources=[])
#
#
#class build_ext(build_ext_orig):
#
# def run(self):
# for ext in self.extensions:
# self.build_cmake(ext)
# super().run()
#
# def build_cmake(self, ext):
# cwd = pathlib.Path().absolute()
# print("cwd = ", cwd)
#
# # these dirs will be created in build_py, so if you don't have
# # any python sources to bundle, the dirs will be missing
# build_temp = pathlib.Path(self.build_temp)
# build_temp.mkdir(parents=True, exist_ok=True)
# extdir = pathlib.Path(self.get_ext_fullpath(ext.name))
# extdir.mkdir(parents=True, exist_ok=True)
#
# config = 'RelWithDebInfo'
# cmake_args = [
# '-S{}'.format(os.path.join(cwd, "..")),
# '-B{}'.format(build_temp),
# '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + str(extdir.parent.absolute()),
# '-DCMAKE_BUILD_TYPE=' + config
# ]
#
# # example of build args
# build_args = [
# '--config', config,
# '--', '-j4'
# ]
#
# print("cmake_args = ", cmake_args)
#
# #os.chdir(str(build_temp))
# self.spawn(['cmake'] + cmake_args)
# if not self.dry_run:
# self.spawn(['cmake', '--build', str(build_temp)] + build_args)
# # Troubleshooting: if fail on line above then delete all possible
# # temporary CMake files including "CMakeCache.txt" in top level dir.
# os.chdir(str(cwd))
setup(
name='tinyusdz',
version='0.8.0rc0',
author='Light Transport Entertainment Inc.',
author_email='lighttransport.eth@mail3.me',
packages_dir=['python'], # Use `python` folder
cmake_args=['-DTINYUSDZ_WITH_PYTHON=1'],
long_description=open("./README.md", 'r').read(),
#ext_modules=[CMakeExtension('pytinyusdz')],
license='MIT',
#cmdclass={
# 'build_ext': build_ext,
#}
)

View File

@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

371
src/external/simple_match/README.md vendored Normal file
View File

@@ -0,0 +1,371 @@
# Simple, Extensible C++ Pattern Matching Library
I have recently been looking at Haskell and Rust. One of the things I wanted in C++ from those languages is pattern matching.
Here is an example from the Rustlang Book (http://static.rust-lang.org/doc/master/book/match.html)
```rust
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
4 => println!("four"),
5 => println!("five"),
_ => println!("something else"),
}
```
There is currently a C++ Library Mach7 that does pattern matching (https://github.com/solodon4/Mach7), however it is big, complicated, and uses a lot of macros. I wanted to see if I could use C++14 to write a simple implementation without macros.
This library is the result of that effort. If you are familiar with C++14 especially variadic templates, forwarding, and tuples, this library and implementation should be easy for you to understand and extend.
## Usage
You will need a C++14 compiler. I have used the latest Visual C++ 2015 CTP, GCC 4.9.2, and Clang 3.5 to test this library.
The library consists of 2 headers. `simple_match.hpp` which is the core of the library, and `some_none.hpp` which contains code that lets you match on raw pointers, and unique_ptr, and shared_ptr.
Here is a simple excerpt. Assume you have included simple_match.hpp
```cpp
using namespace simple_match;
using namespace simple_match::placeholders;
int x = 0;
while (true) {
std::cin >> x;
match(x,
1, []() {std::cout << "The answer is one\n"; },
2, []() {std::cout << "The answer is two\n"; },
_x < 10, [](auto&& a) {std::cout << "The answer " << a << " is less than 10\n"; },
10 < _x < 20, [](auto&& a) {std::cout << "The answer " << a << " is between 10 and 20 exclusive\n"; },
_, []() {std::cout << "Did not match\n"; }
);
}
```
## Example Files
There are 2 files under the test directory: `test.cpp` and `cppcon-matching.cpp`. `test.cpp` contains just some simple tests of matching. `cppcon-matching.cpp`contains the example from Mach7 that was presented at cppcon.
## Extending
There are 2 points of customization provided in namespace `simple_matcher::customization`. They are
```cpp
template<class T, class U>
struct matcher;
```
and
```cpp
template<class Type>
struct tuple_adapter;
```
## License
Licensed under the Boost Software License.
## Tutorial
We are going to assume you have the following at the top of your file
```cpp
#include "simple_match/simple_match.hpp"
using namespace simple_match;
using namespace simple_match::placeholders;
```
Here is how to match exactly
```cpp
int i = 0;
match(i,
1, [](){std::cout << "The answer is one";}
2, [](){std::cout << "The answer is two";}
otherwise, [](){std::cout << "Did not match"}
);
```
The match function will try matching from top to bottom and run the lamba corresponding to the first successful match. `otherwise` always matches, and therefore you should have it at the end. If you find `otherwise` too long, you can also use `_`. It is located in the namespace `simple_match::placeholders`
Match also works for strings.
```
std::string s = "";
match(s,
"Hello", [](){ std::cout << " You said hello\n";},
_, [](){std::cout << "I do not know what you said\n";} // _ is the same as otherwise
);
```
You can even return values from a match
```cpp
char digit = '0';
int value = match(digit,
'0', [](){return 0;},
'1', [](){return 1;},
'2', [](){return 2;},
'3', [](){return 3;},
'4', [](){return 4;},
'5', [](){return 5;},
// and so on
);
```
We can also do comparisons, and ranges. To do so use `_x` from the `simple_match::placeholders` namespace.
```cpp
int i = 0;
match(i,
_x < 0, [](int x){std::cout << x << " is a negative number\n";},
1 < _x < 10, [](int z){std::cout << z << " is between 1 and 10\n"},
_x, [](int x){std::cout << x << " is the value\n";}
);
```
There are a some items of interest in the above example. When `_x` is used, it passes its value to the lambda. If `_x` is used without any comparison, it will pass the value to the lambda. Also, because of the way it is overloaded, it is very easy to make ranges using the `<` or `<=` operator as seen in the match above.
### Tuples
Now we can even have more fun! Let's represent a 2d point as a tuple.
```cpp
std::tuple<int,int> p(0,0);
match(p,
ds(0,0), [](){std::cout << "Point is at the origin";},
ds(0,_), [](){std::cout << "Point is on the horizontal axis";},
ds(_,0), [](){std::cout << "Point is on the vertical axis";}.
ds(_x < 10,_), [](int x){std::cout << x << " is less than 10";},
ds(_x,_x), [](int x, int y){ std::cout << x << "," << y << " Is not on an axis";}
);
```
`ds` stands for de-structure and splits a tuple into its parts. Notice you can use the same expressions as you could without tuples. As before `_x` results in a value being passed to the lambda. `_` matches anything and ignores it, so no corresponding variable is passed to the lambda.
We can actually use `ds` to deconstruct our own `struct`s and `class`es .
First we have to specialize `simple_match::customization::tuple_adapter` for our type.
```cpp
struct point {
int x;
int y;
point(int x_,int y_):x(x_),y(y_){}
};
// Adapting point to be used with ds
namespace simple_match {
namespace customization {
template<>
struct tuple_adapter<point>{
enum { tuple_len = 2 };
template<size_t I, class T>
static decltype(auto) get(T&& t) {
return std::get<I>(std::tie(t.x,t.y));
}
};
}
}
```
Then we can use `ds` like we did with a tuple.
```cpp
point p{0,0};
match(p,
ds(0,0), [](){std::cout << "Point is at the origin";},
ds(0,_), [](){std::cout << "Point is on the horizontal axis";},
ds(_,0), [](){std::cout << "Point is on the vertical axis";}.
ds(_x < 10,_), [](int x){std::cout << x << " is less than 10";},
ds(_x,_x), [](int x, int y){ std::cout << x << "," << y << " Is not on an axis";}
);
```
### Pointers as option types
Sometimes we have pointer that we want to get a value safely out of. To do this we can use `some` and `none` . To do this we have to include `simple_match/some_none.hpp`
Let us use the same `point` as before
```cpp
point* pp = new point(0,0);
match(pp,
some(), [](point& p){std::cout << p.x << " is the x-value";}
none(), [](){std::cout << "Null pointer\n";}
);
```
Notice how `some()` converted the pointer to a reference and passed it to us.
Now, that is now how we should allocate memory with a naked new. We would probably use a `std::unique_ptr`. `some` has built in support for `unique_ptr` and `shared_ptr`. So we can write it like this.
```cpp
auto pp = std::make_unique<point>(0,0);
match(pp,
some(), [](point& p){std::cout << p.x << " is the x-value";}
none(), [](){std::cout << "Null pointer\n";}
);
```
Notice, how our match code did not change.
We can do better because `some` composes. Since we specialized `tuple_adapter` we can use `ds` with `point`.
```cpp
auto pp = std::make_unique<point>(0,0);
match(pp,
some(ds(0,0)), [](){std::cout << "Point is at the origin";},
some(ds(0,_)), [](){std::cout << "Point is on the horizontal axis";},
some(ds(_,0)), [](){std::cout << "Point is on the vertical axis";}.
some(ds(_x < 10,_)), [](int x){std::cout << x << " is less than 10";},
some(ds(_x,_x)), [](int x, int y){ std::cout << x << "," << y << " Is not on an axis";},
none(), [](){std::cout << "Null pointer";}
);
```
Notice how `some` and `ds` compose. If we wanted to to, we could have pointers in tuples, and tuples in pointers and it would just work.
`some` can also use RTTI to do downcasting.
Here is an example. We will now make `point` a base class and have point2d, and point3d as subclasses, and adapt them.
```cpp
struct point{
virtual ~point(){}
};
struct point2d:point{
int x;
int y;
point2d(int x_,int y_):x(x_),y(y_){}
};
struct point3d:point{
int x;
int y;
int z;
point3d(int x_,int y_, int z_):x(x_),y(y_),z(z_){}
};
// Adapting point2d and point3d to be used with ds
namespace simple_match {
namespace customization {
template<>
struct tuple_adapter<point2d>{
enum { tuple_len = 2 };
template<size_t I, class T>
static decltype(auto) get(T&& t) {
return std::get<I>(std::tie(t.x,t.y));
}
};
template<>
struct tuple_adapter<point3d>{
enum { tuple_len = 3 };
template<size_t I, class T>
static decltype(auto) get(T&& t) {
return std::get<I>(std::tie(t.x,t.y,t.z));
}
};
}
}
```
Then we can use it like this
```cpp
std::unique_ptr<point> pp(new point2d(0,0));
match(pp,
some<point2d>(ds(_x,_x)), [](int x, int y){std::cout << x << "," << y;},
some<point3d>(ds(_x,_x,_x)), [](int x, int y, int z){std::cout << x << "," << y << "," << z;},
some(), [](point& p){std::cout << "Unknown point type\n"},
none(), [](){std::cout << "Null pointer\n"}
);
```
Notice how we can safely downcast, and use `ds` to destructure the `point`. Everything composes nicely.
# Implementation Details
simple_match actually was easier to implement than I thought it would be. I used the apply sample implementation from http://isocpp.org/files/papers/N3915.pdf to call a function with a tuple as arguments.
Here is the core of the implementation
```cpp
template<class T, class U>
bool match_check(T&& t, U&& u) {
using namespace customization;
using m = matcher<std::decay_t<T>, std::decay_t<U>>;
return m::check(std::forward<T>(t), std::forward<U>(u));
}
template<class T, class U>
auto match_get(T&& t, U&& u) {
using namespace customization;
using m = matcher<std::decay_t<T>, std::decay_t<U>>;
return m::get(std::forward<T>(t), std::forward<U>(u));
}
template<class T, class A1, class F1>
auto match(T&& t, A1&& a, F1&& f) {
if (match_check(std::forward<T>(t), std::forward<A1>(a))) {
return detail::apply(f, match_get(std::forward<T>(t), std::forward<A1>(a)));
}
else {
throw std::logic_error("No match");
}
}
template<class T, class A1, class F1, class A2, class F2, class... Args>
auto match(T&& t, A1&& a, F1&& f, A2&& a2, F2&& f2, Args&&... args) {
if (match_check(t, a)) {
return detail::apply(f, match_get(std::forward<T>(t), std::forward<A1>(a)));
}
else {
return match(t, std::forward<A2>(a2), std::forward<F2>(f2), std::forward<Args>(args)...);
}
}
```
`match` is a variadic function that takes the value to be matched, and then parameters for the match criteria, and lambda to be executed if the criteria succeeds. It goes through calling `match_check` until it returns true. Then it calls `match_get` to get a tuple of the values that need to be forwarded to the lambda, and uses apply to call the lambda.
The match types are implemented by specializing `simple_match::customization::matcher`
```cpp
namespace customization {
template<class T, class U>
struct matcher;
}
```
For an example, here is how the matcher that matches to values is implemented. Note, that it does not pass any values to the lambda and so returns an empty tuple.
```cpp
// Match same type
template<class T>
struct matcher<T, T> {
static bool check(const T& t, const T& v) {
return t == v;
}
static auto get(const T&, const T&) {
return std::tie();
}
}
```
I hope you enjoy using this code, as much as I have enjoyed writing it. Please give me any feedback you may have.
-- John R. Bandela, MD
> Written with [StackEdit](https://stackedit.io/).

View File

@@ -0,0 +1,369 @@
// Copyright 2015 John R. Bandela
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#ifndef SIMPLE_MATCH_SOME_NONE_HPP_JRB_2015_03_21
#define SIMPLE_MATCH_SOME_NONE_HPP_JRB_2015_03_21
#include <memory>
#include <type_traits>
namespace simple_match {
namespace customization {
template<class Type>
struct pointer_getter {};
}
namespace detail {
template<class T, class C>
struct cv_helper_imp;
template<class T, class C>
struct cv_helper_imp<T*, C> {
using type = std::add_pointer_t<C>;
};
template<class T, class C>
struct cv_helper_imp<const T*, C> {
using type = std::add_pointer_t<std::add_const_t<C>>;
};
template<class T, class C>
struct cv_helper_imp<volatile T*, C> {
using type = std::add_pointer_t<std::add_volatile_t<C>>;
};
template<class T, class C>
struct cv_helper_imp<const volatile T*, C> {
using type = std::add_pointer_t<std::add_cv_t<C>>;
};
}
namespace utils {
template<class T, class C>
using cv_helper = typename detail::cv_helper_imp<T, C>::type;
}
namespace detail{
template<class Class, class Matcher>
struct some_t{
Matcher m_;
template<class T>
bool check(T&& t) {
auto ptr = customization::pointer_getter<std::decay_t<T>>::template get_pointer<Class>(std::forward<T>(t));
if (!ptr) {
return false;
}
return match_check(*ptr, m_);
}
template<class T>
auto get(T&& t) {
auto ptr = customization::pointer_getter<std::decay_t<T>>::template get_pointer<Class>(std::forward<T>(t));
return match_get(*ptr, m_);
}
};
template<class Matcher>
struct some_t<void,Matcher> {
Matcher m_;
template<class T>
bool check(T&& t) {
// If you get an error here, this means that some() without a type is not supported
// Examples of this are variants and boost::any
auto ptr = customization::pointer_getter<std::decay_t<T>>::get_pointer_no_cast(std::forward<T>(t));
if (!ptr) {
return false;
}
return match_check(*ptr, m_);
}
template<class T>
auto get(T&& t) {
auto ptr = (customization::pointer_getter<std::decay_t<T>>::get_pointer_no_cast(std::forward<T>(t)));
return match_get(*ptr, m_);
}
};
template<class Class>
struct some_t<Class,void> {
template<class T>
bool check(T&& t) {
auto ptr = customization::pointer_getter<std::decay_t<T>>::template get_pointer<Class>(std::forward<T>(t));
if (!ptr) {
return false;
}
return true;
}
template<class T>
auto get(T&& t) {
auto ptr = customization::pointer_getter<std::decay_t<T>>::template get_pointer<Class>(std::forward<T>(t));
return std::tie(*ptr);
}
};
template<>
struct some_t<void, void> {
template<class T>
bool check(T&& t) {
// If you get an error here, this means that some() without a type is not supported
// Examples of this are variants and boost::any
auto ptr = customization::pointer_getter<std::decay_t<T>>::get_pointer_no_cast(std::forward<T>(t));
if (!ptr) {
return false;
}
return true;
}
template<class T>
auto get(T&& t) {
auto ptr = customization::pointer_getter<std::decay_t<T>>::get_pointer_no_cast(std::forward<T>(t));
return std::tie(*ptr);
}
};
struct none_t{
template<class T>
bool check(T&& t) {
// If you get an error here, this means that none() is not supported
// Example is boost::variant which has a never empty guarantee
return customization::pointer_getter<std::decay_t<T>>::is_null(std::forward<T>(t));
}
template<class T>
auto get(T&&) {
return std::tie();
}
};
}
namespace customization {
template<class Type, class Class, class Matcher>
struct matcher<Type,detail::some_t<Class,Matcher>> {
template<class T, class U>
static bool check(T&& t, U&& u) {
return u.check(std::forward<T>(t));
}
template<class T, class U>
static auto get(T&& t, U&& u) {
return u.get(std::forward<T>(t));
}
};
template<class Type>
struct matcher<Type,detail::none_t> {
template<class T, class U>
static bool check(T&& t, U&& u) {
return u.check(std::forward<T>(t));
}
template<class T, class U>
static auto get(T&& t, U&& u) {
return u.get(std::forward<T>(t));
}
};
}
namespace customization {
template<class Type>
struct pointer_getter<Type*> {
template<class To>
static auto get_pointer(Type* t) {
return dynamic_cast<utils::cv_helper<decltype(t),To>>(t);
}
static auto get_pointer_no_cast(Type* t) {
return t;
}
static auto is_null(Type* t) {
return !t;
}
};
template<class Type>
struct pointer_getter<std::shared_ptr<Type>> {
template<class To, class T>
static auto get_pointer(T&& t) {
return dynamic_cast<utils::cv_helper<decltype(t.get()),To>>(t.get());
}
template<class T>
static auto get_pointer_no_cast(T&& t) {
return t.get();
}
template<class T>
static auto is_null(T&& t) {
return !t;
}
};
template<class Type, class D>
struct pointer_getter<std::unique_ptr<Type, D>> {
template<class To, class T>
static auto get_pointer(T&& t) {
return dynamic_cast<utils::cv_helper<decltype(t.get()),To>>(t.get());
}
template<class T>
static auto get_pointer_no_cast(T&& t) {
return t.get();
}
template<class T>
static auto is_null(T&& t) {
return !t;
}
};
}
inline detail::none_t none() { return detail::none_t{}; }
inline detail::some_t<void, void> some() { return detail::some_t<void, void>{}; }
// Make Matcher... a variadic template so it is a worse match than some<T>(T())
template<class... Matcher>
detail::some_t<void, Matcher...> some(Matcher&&... m) { return detail::some_t<void, Matcher...> { std::forward<Matcher>(m)... }; }
template<class Class, class Matcher>
detail::some_t<Class, Matcher> some(Matcher&& m) {
return detail::some_t<Class, Matcher> { std::forward<Matcher>(m) };
}
template<class Class>
detail::some_t<Class, void> some() {
return detail::some_t<Class, void>{ };
}
// exhaustiveness
namespace detail {
template<class T, class Tuple>
struct type_in_tuple {
static const bool value = false;
};
template<class T, class First, class... Rest>
struct type_in_tuple<T, std::tuple<First, Rest...>> {
static const bool value = type_in_tuple<T, std::tuple<Rest...>>::value;
};
template<class T, class... Rest>
struct type_in_tuple<T, std::tuple<T, Rest...>> {
static const bool value = true;
};
template<class T>
struct type_in_tuple<T, std::tuple<>> {
static const bool value = false;
};
template<class ArgTypes>
struct has_otherwise {
static const bool value = type_in_tuple<otherwise_t, ArgTypes>::value;
};
template<bool, class ArgTypes,class... RequiredTypes>
struct some_exhaustiveness_helper {
static const bool value = true;
};
template<class Tuple>
struct get_some_classes {};
template<>
struct get_some_classes<std::tuple<>> {
using type = std::tuple<>;
};
template<class First, class...Rest>
struct get_some_classes<std::tuple<First, Rest...>> {
using type = typename get_some_classes<std::tuple<Rest...>>::type;
};
template<class Class, class Matcher, class...Rest>
struct get_some_classes<std::tuple<some_t<Class,Matcher>, Rest...>> {
using type = cat_tuple_t<std::tuple<Class>, typename get_some_classes<std::tuple<Rest...>>::type>;
};
template<class SomeClasses, class... Required>
struct all_in {};
template<bool b, class Type>
struct not_in_match_asserter {
static const bool value = b;
static_assert(value, "This type is not in the match");
};
template<class SomeClasses, class First, class... Rest>
struct all_in<SomeClasses,First,Rest...> {
static const bool myvalue = type_in_tuple<First, SomeClasses>::value;
static const bool value = not_in_match_asserter<myvalue, First>::value&& all_in<SomeClasses, Rest...>::value;
};
template<class SomeClasses, class First>
struct all_in<SomeClasses,First> {
static const bool myvalue = type_in_tuple<First, SomeClasses>::value;
// static_assert(type_in_tuple<First, SomeClasses>::value, "This classes is not in the match");
static const bool value = not_in_match_asserter<myvalue, First>::value;
};
template<class ArgTypes, class... RequiredTypes>
struct some_exhaustiveness_helper<false,ArgTypes,RequiredTypes...> {
using some_classes = typename get_some_classes<ArgTypes>::type;
using a = all_in<some_classes, RequiredTypes...>;
static const bool value = a::value;
};
}
template<class... Types>
struct some_exhaustiveness {
template<class ArgTypes>
struct type {
static const bool v = detail::has_otherwise<ArgTypes>::value;
using seh = detail::some_exhaustiveness_helper<v, ArgTypes, Types...>;
static const bool value = seh::value;
};
};
}
#endif

View File

@@ -0,0 +1,404 @@
// Copyright 2015 John R. Bandela
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#ifndef SIMPLE_MATCH_HPP_JRB_2015_03_21
#define SIMPLE_MATCH_HPP_JRB_2015_03_21
//#include <stdexcept>
#include <tuple>
namespace simple_match {
using std::size_t;
namespace customization {
template<class T, class U>
struct matcher;
}
// Apply adapted from http://isocpp.org/files/papers/N3915.pdf
namespace detail {
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, std::integer_sequence<size_t, I...>) {
using namespace std;
return std::forward<F>(f)(get<I>(std::forward<Tuple>(t))...);
}
template <typename F, typename Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
using namespace std;
using Indices = make_index_sequence<tuple_size<decay_t<Tuple>>::value>;
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices{});
}
}
// exhaustiveness
namespace detail {
template<class TA, class TB>
struct cat_tuple {};
template<class... A, class... B>
struct cat_tuple<std::tuple<A...>,std::tuple<B...>> {
using type = std::tuple<A..., B...>;
};
template<class TA, class TB>
using cat_tuple_t = typename cat_tuple<TA, TB>::type;
template<class... A>
struct arg_types {
};
template<class A, class F>
struct arg_types<A,F> {
using type = std::tuple<std::decay_t<A>>;
};
template<class A1, class F1, class A2, class F2, class... Args>
struct arg_types<A1, F1, A2, F2, Args...>{
using type = cat_tuple_t<std::tuple<std::decay_t<A1>, std::decay_t<A2>>, typename arg_types<Args...>::type>;
};
template<>
struct arg_types<>{
using type = std::tuple<>;
};
}
struct empty_exhaustiveness {
template<class ArgTypes>
struct type{
static const bool value = true;
};
};
namespace customization {
template<class T>
struct exhaustiveness_checker {
using type = empty_exhaustiveness;
};
}
// end exhaustiveness
template<class T, class U>
bool match_check(T&& t, U&& u) {
using namespace customization;
using m = matcher<std::decay_t<T>, std::decay_t<U>>;
return m::check(std::forward<T>(t), std::forward<U>(u));
}
template<class T, class U>
auto match_get(T&& t, U&& u) {
using namespace customization;
using m = matcher<std::decay_t<T>, std::decay_t<U>>;
return m::get(std::forward<T>(t), std::forward<U>(u));
}
//struct no_match :std::logic_error {
// no_match() :logic_error{ "simple_match did not match" } {}
//};
namespace detail {
template<class T, class A1, class F1>
auto match_helper(T&& t, A1&& a, F1&& f) {
if (match_check(std::forward<T>(t), std::forward<A1>(a))) {
return detail::apply(f, match_get(std::forward<T>(t), std::forward<A1>(a)));
}
else {
//throw no_match{};
// tinyusdz: todo: report an error in another way.
}
}
template<class T, class A1, class F1, class A2, class F2, class... Args>
auto match_helper(T&& t, A1&& a, F1&& f, A2&& a2, F2&& f2, Args&&... args) {
if (match_check(t, a)) {
return detail::apply(f, match_get(std::forward<T>(t), std::forward<A1>(a)));
}
else {
return match_helper(t, std::forward<A2>(a2), std::forward<F2>(f2), std::forward<Args>(args)...);
}
}
}
template<class T, class... Args>
auto match(T&& t, Args&&... a) {
using atypes = typename detail::arg_types<Args...>::type;
using ec = typename customization::exhaustiveness_checker<std::decay_t<T>>::type;
using ctypes = typename ec::template type<atypes>;
static_assert(ctypes::value, "Not all types are tested for in match");
return detail::match_helper(std::forward<T>(t), std::forward<Args>(a)...);
}
struct otherwise_t {};
namespace placeholders {
const otherwise_t otherwise{};
const otherwise_t _{};
}
namespace customization {
// Match same type
template<class T>
struct matcher<T, T> {
static bool check(const T& t, const T& v) {
return t == v;
}
static auto get(const T&, const T&) {
return std::tie();
}
};
// Match string literals
template<class T>
struct matcher<T, const char*> {
static bool check(const T& t, const char* str) {
return t == str;
}
static auto get(const T&, const T&) {
return std::tie();
}
};
// Match otherwise
template<class Type>
struct matcher<Type, otherwise_t> {
template<class T>
static bool check(T&&, otherwise_t) {
return true;
}
template<class T>
static auto get(T&&, otherwise_t) {
return std::tie();
}
};
}
template<class F>
struct matcher_predicate {
F f_;
};
template<class F>
matcher_predicate<F> make_matcher_predicate(F&& f) {
return matcher_predicate<F>{std::forward<F>(f)};
}
namespace customization {
template<class Type, class F>
struct matcher<Type, matcher_predicate<F>> {
template<class T, class U>
static bool check(T&& t, U&& u) {
return u.f_(std::forward<T>(t));
}
template<class T, class U>
static auto get(T&& t, U&&) {
return std::tie(std::forward<T>(t));
}
};
}
namespace placeholders {
const auto _u = make_matcher_predicate([](auto&&) {return true; });
const auto _v = make_matcher_predicate([](auto&&) {return true; });
const auto _w = make_matcher_predicate([](auto&&) {return true; });
const auto _x = make_matcher_predicate([](auto&&) {return true; });
const auto _y = make_matcher_predicate([](auto&&) {return true; });
const auto _z = make_matcher_predicate([](auto&&) {return true; });
// relational operators
template<class F, class T>
auto operator==(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x == t; });
}
template<class F, class T>
auto operator!=(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x != t; });
}
template<class F, class T>
auto operator<=(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x <= t; });
}
template<class F, class T>
auto operator>=(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x >= t; });
}
template<class F, class T>
auto operator<(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x < t; });
}
template<class F, class T>
auto operator>(const matcher_predicate<F>& m, const T& t) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && x > t; });
}
template<class F>
auto operator!(const matcher_predicate<F>& m) {
return make_matcher_predicate([m](const auto& x) {return !m.f_(x); });
}
template<class F, class F2>
auto operator&&(const matcher_predicate<F>& m, const matcher_predicate<F2>& m2) {
return make_matcher_predicate([m, m2](const auto& x) {return m.f_(x) && m2.f_(x); });
}
template<class F, class F2>
auto operator||(const matcher_predicate<F>& m, const matcher_predicate<F2>& m2) {
return make_matcher_predicate([m, m2](const auto& x) {return m.f_(x) || m2.f_(x); });
}
template<class F, class T>
auto operator==(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t == x; });
}
template<class F, class T>
auto operator!=(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t != x; });
}
template<class F, class T>
auto operator<=(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t <= x; });
}
template<class F, class T>
auto operator>=(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t >= x; });
}
template<class F, class T>
auto operator<(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t < x; });
}
template<class F, class T>
auto operator>(const T& t, const matcher_predicate<F>& m) {
return make_matcher_predicate([m, &t](const auto& x) {return m.f_(x) && t > x; });
}
}
namespace detail {
// We use this class, so we can differentiate between matcher <T,T> and matchter <T,std::tuple<T...>
struct tuple_ignorer {};
}
namespace customization {
template<class... A>
const std::tuple<A...>& simple_match_get_tuple(const std::tuple<A...>& t) {
return t;
}
template<class... A>
std::tuple<A...>& simple_match_get_tuple(std::tuple<A...>& t) {
return t;
}
template<class Type>
struct tuple_adapter {
template<size_t I, class T>
static decltype(auto) get(T&& t) {
using namespace simple_match::customization;
return std::get<I>(simple_match_get_tuple(std::forward<T>(t)));
}
};
template<class Type, class... Args>
struct matcher<Type, std::tuple<Args...>> {
using tu = tuple_adapter<Type>;
enum { tuple_len = sizeof... (Args) - 1};
template<size_t pos, size_t last>
struct helper {
template<class T, class A>
static bool check(T&& t, A&& a) {
return match_check(tu::template get<pos>(std::forward<T>(t)), std::get<pos>(std::forward<A>(a)))
&& helper<pos + 1, last>::check(std::forward<T>(t), std::forward<A>(a));
}
template<class T, class A>
static auto get(T&& t, A&& a) {
return std::tuple_cat(match_get(tu::template get<pos>(std::forward<T>(t)), std::get<pos>(std::forward<A>(a))),
helper<pos + 1, last>::get(std::forward<T>(t), std::forward<A>(a)));
}
};
template<size_t pos>
struct helper<pos, pos> {
template<class T, class A>
static bool check(T&& t, A&& a) {
return match_check(tu::template get<pos>(std::forward<T>(t)), std::get<pos>(std::forward<A>(a)));
}
template<class T, class A>
static auto get(T&& t, A&& a) {
return match_get(tu::template get<pos>(std::forward<T>(t)), std::get<pos>(std::forward<A>(a)));
}
};
template<class T, class A>
static bool check(T&& t, A&& a) {
return helper<0, tuple_len - 1>::check(std::forward<T>(t), std::forward<A>(a));
}
template<class T, class A>
static auto get(T&& t, A&& a) {
return helper<0, tuple_len - 1>::get(std::forward<T>(t), std::forward<A>(a));
}
};
}
// destructure a tuple or other adapted structure
template<class... A>
auto ds(A&& ... a) {
return std::make_tuple(std::forward<A>(a)..., detail::tuple_ignorer{});
}
}
#include "implementation/some_none.hpp"
#endif

View File

@@ -0,0 +1,35 @@
#pragma once
// Copyright 2015 John R. Bandela
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#ifndef SIMPLE_MATCH_UTILITY_HPP_JRB_2015_09_11
#define SIMPLE_MATCH_UTILITY_HPP_JRB_2015_09_11
#include "simple_match.hpp"
namespace simple_match {
// tagged_tuple
template<class Type, class... Args>
struct tagged_tuple :std::tuple<Args...> {
using base = std::tuple<Args...>;
template<class... A>
tagged_tuple(A&&... a) :base{ std::forward<A>(a)... } {}
};
// inheriting_tagged_tuple
template<class Base, class Type, class... Args>
struct inheriting_tagged_tuple :Base,tagged_tuple<Type,Args...> {
using base = tagged_tuple<Type,Args...>;
template<class... A>
inheriting_tagged_tuple(A&&... a) :base{ std::forward<A>(a)... } {}
};
}
#endif