├── .install_clang.sh ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── biicode.conf ├── include └── fu │ ├── README.md │ ├── basic.h │ ├── fu.h │ ├── functional.h │ ├── invoke.h │ ├── iseq.h │ ├── list.h │ ├── logic.h │ ├── logic │ ├── README.md │ └── logic.h │ ├── make │ ├── README.md │ └── make.h │ ├── meta.h │ ├── tuple.h │ ├── tuple │ ├── README.md │ ├── basic.h │ └── tuple.h │ └── utility.h ├── run-tests.sh └── test ├── functional.cpp ├── invoke.cpp ├── logic.cpp ├── meta.cpp ├── overload.cpp ├── tuple.cpp └── utility.cpp /.install_clang.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - 4 | sudo apt-add-repository 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.5 main' 5 | sudo apt-get -qq update 6 | sudo apt-get -qq --force-yes install clang-3.5 libc++-dev 7 | sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.5 1 8 | sudo rm /usr/local/clang-3.4/bin/clang++ 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | env: 3 | - CXX=g++ 4 | 5 | before_install: 6 | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y 7 | - sudo apt-get update -qq 8 | - if [ "$CXX" = "clang++" ]; then ./.install_clang.sh; fi 9 | - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.9; fi 10 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi 11 | - $CXX -v 12 | 13 | script: "./run-tests.sh" 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | ADD_BIICODE_TARGETS() 3 | TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE -std=c++14) 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/splinterofchaos/fu.svg?branch=travis)](https://travis-ci.org/splinterofchaos/fu) 2 | 3 | # fu 4 | Functional Utilities for C++14. 5 | 6 | Fu hopes to offer 7 | * Utilities for defining functions in terms of other ones. 8 | * A set of transparent function objects for common operations like `add`, `eq`, etc.. 9 | * Utilities for creating transparent function objects. 10 | 11 | github: https://github.com/splinterofchaos/fu 12 | biicode: http://www.biicode.com/splinterofchaos/fu 13 | 14 | # Transparent Function Objects 15 | It can be difficult in C++ to use template functions with higher order functions, like those defined in ``. For example: 16 | ```c++ 17 | #include 18 | 19 | template 20 | void print(const X& x) { 21 | std::cout << x; 22 | } 23 | 24 | int main() { 25 | auto v = {1,2,3,4}; 26 | std::for_each(std::begin(v), std::end(v), std::bind(print, std::placeholders::_1)); 27 | } 28 | ``` 29 | Not only must we explicitely specify the type of `print`'s arguments to avoid ambiguity, but `std::bind`'s syntax is overly verbose and inconvenient. Using `fu`, we can write... 30 | ```c++ 31 | #include 32 | #include 33 | #include 34 | 35 | auto print = fu::lshift(std::ref(std::cout)); 36 | 37 | int main() { 38 | auto v = {1,2,3,4,5}; 39 | std::for_each(std::begin(v), std::end(v), print); 40 | } 41 | ``` 42 | Here, `fu::lshift` is a function object representing `operator<<`, and by supplying just one argument, we create a partial function. By default, `fu` will copy arguments when constructing partial functions, but `std::ref` prevents that. 43 | 44 | FU also provides several projection functions such as `proj`, `split`, and `join` for use with ``. For more information, see "[Common `` patterns](http://yapb-soc.blogspot.com/2015/02/common-algorithm-patterns.html)". 45 | 46 | "Transparent function object" means a function object that represents an overload set or has a templated function call operator such that it can be sent to higher order functions, like `std::for_each`, without needing to specify the types of its arguments. fu supplies a set of function objects, mirroring those supplied in ``, like `add`, `sub`(tract), `less`, `eq`(ual), and more. Given one argument, they create a partial application. Given many, they apply the arguments from left to right. For exmple, `fu::add(1,2,3,4)` would be equivalent to writing `((1+2)+3)+4`, and `fu::add(1)(2)` would be equivalent to `fu::add(1,2)`. `fu::less(1,2,3)` is equivalent to `1 < 2 && 2 < 3`. 47 | 48 | # Defining Functions in Terms of Other Ones 49 | TODO 50 | 51 | # Creating generic function objects. 52 | A multary (or "multiple-arity") function is one that takes two or more arguments. `fu::multary` enables a function to return a partial application if given only one argument. 53 | ```c++ 54 | // GCD implementation from n4061 55 | constexpr int gcd_impl(int a, int b) { 56 | return b ? gcd_impl(b, std::abs(a) % std::abs(b)) : std::abs(a); 57 | } 58 | constexpr auto gcd = fu::multary(gcd_impl); 59 | constexpr auto gcd5 = gcd(5); 60 | constexpr int five = gcd5(10); // five == gcd(5, 10) 61 | ``` 62 | One might note that `gcd` is an associative operation; `gcd(x, y, z) == gcd(x, gcd(y, z))`. `fu::lassoc` (implying "left associativity") allows it to work on an arbitrary number of arguments. 63 | ```c++ 64 | constexpr auto gcd = fu::lassoc(fu::multary(gcd_impl)); 65 | constexpr int five = gcd(5, 10, 15, 20); 66 | ``` 67 | Having many layers of nesting parenthesies can make code both harder to write and read. `fu::pipe` allows one to chain operations, silimar to how the ranges proposal by Erin Neibler uses piping. 68 | ```c++ 69 | constexpr auto gcd = fu::pipe(gcd_impl, fu::lassoc, fu::multary); 70 | // Think of as: "gcd = gcd_impl | lassoc | multary;" 71 | ``` 72 | Note that most of fu's utilities are transparent function objects, so we can pass them directly to higher order functions without ambiguity. 73 | -------------------------------------------------------------------------------- /biicode.conf: -------------------------------------------------------------------------------- 1 | [paths] 2 | include 3 | 4 | [tests] 5 | test/* 6 | -------------------------------------------------------------------------------- /include/fu/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Documentation 3 | 4 | FU must be compiled with gcc 4.9 or later, or clang 3.5, with the flag 5 | `-std=c++14`. MSVC is not supported for lack of C++14 features. 6 | 7 | The subdirectories contain modules with their own documentation, but can be 8 | included from the associated file in the main directory. For example, 9 | `fu/tuple.h` will include `fu/tuple/basic.h` and `fu/tuple/tuple.h`. See 10 | `fu/tuple/README.md` for its documentation. 11 | 12 | # "fu/fu.h" 13 | 14 | This file includes all other FU headers. Use this is you don't know what 15 | components you need, or you need them all (albeit unlikely). 16 | 17 | ## "fu/invoke.h" 18 | 19 | This file contains an implementation of 20 | [n3727](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3727.html)'s `std::invoke`. 21 | 22 | `invoke(f,x...)` simply calls `f` with the arguments, `x...`. `f` may be a 23 | regular function, member function pointer, or member object pointer. 24 | `invoke(f)` will simply call `f()`. 25 | 26 | ```c++ 27 | void f(); 28 | invoke(f); // calls f() 29 | 30 | std::string s; 31 | invoke(std::string::size, s); // calls s.size() 32 | ``` 33 | 34 | # "fu/basic.h" 35 | 36 | This file contains miscellaneous utilities that the rest of the library builds 37 | off of. Components may be moved from this file into more granular units so it 38 | should not be included direction. The following are included as of this 39 | writing. 40 | 41 | ## identity(x) 42 | 43 | Returns `x` 44 | 45 | ## forwarder(f) 46 | 47 | If given a member pointer, `std::mem_fn` may be used to create a function 48 | object, but that doesn't help when one doesn't know if the function is a member 49 | pointer. `forwarder(f)` can be used to create an invokable object, whether `f` 50 | is a member pointer, regular function, or function object. 51 | 52 | ```c++ 53 | // Collecting the lengths of some strings: 54 | std::transform(strs.begin(), strs.end(), std::back_inserter(lengths), 55 | forwarder(&std::string::size)); 56 | ``` 57 | 58 | ## closure(f,x...), part(f,x...), rclosure(f,y...), rpart(f,y...) 59 | 60 | `closure(f,x...)` creates a partial function, `c`, such that `c(y...)` returns 61 | the result of `f(x..., y...)`. The arguments will be copied, but `std::ref` can 62 | be used to avoid this. 63 | 64 | `part(f,x...)` works in the same way, except that it captures its arguments by 65 | perfectly forwarding. Because `part` may create references to temporaries, 66 | `closure` should be preferred if one is not sure. 67 | 68 | `rpart` and `rclosure` apply the arguments at the right-hand side. 69 | 70 | ```c+ 71 | auto plus_one = closure(std::plus<>{}, 1); 72 | plus_one(1); // equals two 73 | 74 | int one = 1; 75 | auto plus_ref = closure(std::plus<>{}, std::ref(one)); 76 | plus_ref(1); // returns two 77 | one++; 78 | plus_ref(1); // returns three 79 | 80 | auto minus_one = rclosure(std::minus<>{}, 1); 81 | minus_one(10); // computes 10 - 1 82 | ``` 83 | 84 | ## multary(f) 85 | 86 | Creates a function, `m`, such that `m(x)` returns a partial application of `f` 87 | and `x`. Applying `m` with more than one argument will not partially apply. 88 | 89 | To create a function that will partially apply up to `n` arguments, use 90 | `multary_n(f)`. 91 | 92 | ```c++ 93 | auto plus = multary(std::plus<>{}); 94 | auto plus_one = plus(1); 95 | plus_one(1); // returns 2 96 | 97 | void _g(int, int, int); 98 | auto g = multary_n<2>(_g); 99 | auto g_one = g(1); // same as multary_n<1>(closure(g, 1)) 100 | auto g_one_two = g_one(2); // same as closure(g, 1, 2) 101 | g_one_two(3); // calls _g(1,2,3) 102 | ``` 103 | 104 | # "fu/functional.h" 105 | 106 | ## pipe(x, f, g, h...) 107 | 108 | Computes `h(g(f(x)))`, or: applies `x` to each function, left-to-right. 109 | 110 | ## lassoc(f) and rassoc(f) 111 | 112 | Makes `f` a left- or right-associative function so that it can be invoked with 113 | an arbitrary number of arguments. `lassoc(f,x,y)` will compute `f(x,y)`, but 114 | generally the partially applied form, `lassoc(f)` is the most useful. 115 | ```c++ 116 | auto sum = lassoc(std::plus<>{}); 117 | sum(1,2,3,4); // computes: ((1+2) + 3) + 4 118 | rassoc(std::minus<>{}, 1, 2, 2); // computes: 1 - (2 - 2) 119 | ``` 120 | 121 | ## transitive(binary, join = std::logical_and) 122 | 123 | Makes a function that preserves transitivity. The function, `binary` must 124 | preserve transitivity, `join` logically connects the results. FIXME: better 125 | explanation. 126 | ```c++ 127 | auto less = transitive(std::less<>{}); 128 | less(1,2,3,4); // computes: 1 < 2 && 2 < 3 && 3 < 4 129 | transitive(std::greater<>{}, 3, 2, 1); // computes: 3 > 2 && 2 > 1 130 | ``` 131 | 132 | ## overload(f,g...) and ranked_overload(f,g...) 133 | 134 | Constructs a function object overloaded on `f` and `g...`. Because this may 135 | cause ambiguities, `ranked_overload` can be used so that `f` will be chosen by 136 | default, and `g...` if SFINAE forbids. 137 | ```c++ 138 | auto o = overload([](int x) { return x + 10; }, 139 | [](std::string s) { return s + "1"; }); 140 | o(1); // returns: 11 141 | o("1"); // returns: "11" 142 | 143 | auto ro = ranked_overload([](auto x) { std::cout << x; }, 144 | [](auto x) { for (auto&& y : x) std::cout << y; }); 145 | ro(1); // prints "1" 146 | ro(std::vector{1,2,3}); // prints "123" 147 | ``` 148 | 149 | ## fix(f) 150 | 151 | `fix(f)` can be used to define a recursive function without having it 152 | refer to itself using a concept called "[fixed point combinator](http://llvm.org/bugs/show_bug.cgi?id=20090)". This can be useful for lambdas, which may not be referred to in the statement that declares them. 153 | 154 | 155 | Note that due to a [bug in clang](http://llvm.org/bugs/show_bug.cgi?id=20090), the result of `f` may not be constexpr. 156 | ```c++ 157 | constexpr auto pow2 = fix([](auto rec, int x) -> int { 158 | return x ? 2 * rec(x-1) : 1; 159 | }); 160 | pow2(1); // returns 2 161 | pow2(3); // returns 8 162 | 163 | constexpr auto fact = fix([](auto rec, int x) -> int { 164 | return x > 2 ? x * rec(x-1) : x; 165 | }); 166 | ``` 167 | 168 | ## compose(f,g), ucompose(f,g), compose_n(f,g) 169 | 170 | Many useful forms of composition exist, but `compose(f,g)` is the most general. 171 | It returns a function, `c`, that takes two tuples, `{x...}` and `{y...}`, such 172 | that `c({x...}, {y...})` computes `f(g(x...), y...)`. For most instances, 173 | `u = ucompose(f,g)` is much simpler; `u(x,y...)` computes `f(g(x), y...)`--it 174 | is short for "unary composition". It assumes that `g` is unary, but often it 175 | may not be. `compose_n(f,g)` sends the first `n` arguments to `g` and the 176 | rest to `f`. 177 | ```c++ 178 | void f(int, int, int); 179 | void g(int, int); 180 | constexpr auto fg = compose(f,g); 181 | constexpr auto fg2 = compose_n<2>(f,g) 182 | 183 | using fu::tpl::tuple; 184 | fg(tuple(1,2), tuple(3,4)); // computes: f(g(1,2), 3, 4) 185 | fg2(1,2,3,4); // computes: f(g(1,2), 3, 4) 186 | 187 | void h(int); 188 | constexpr auto fh = ucompose(f,h); 189 | fh(1,2,3); // computes: f(h(1), 2, 3) 190 | ``` 191 | 192 | ## proj(f,pf), proj_less(pless), rprog(f,pf) and lproj(f,pf) 193 | 194 | The `proj` family constructs projection functions taking a function, `f`, and a 195 | projection to `f`, `pf`. `l` and `rproj` only project the left- or right-hand 196 | arguments. Because it is the most common projection, `proj_less = 197 | proj(std::less<>{})`. 198 | ```c++ 199 | // To sort a list by applying `*it1 < *it2`: 200 | std::sort(first, last, proj(std::less<>{}, f)); 201 | // equivalent: 202 | std::sort(first, last, proj_less(f)); 203 | 204 | // Accumulate the sum of sizes of a list of std::strings. 205 | std::accumulate(first, last, 0, rproj(std::plus<>{}, &std::string::size)); 206 | ``` 207 | 208 | ## split(f,l,r) and join(f,l,r) 209 | 210 | `split(f,l,r)` returns a function that takes a single argument, `x`, and 211 | computes `f(l(x), r(x))`. `join(f,l,r)` takes exactly two arguments, `x` and 212 | `y`, and computes `f(l(x), r(y))`. 213 | 214 | ## flip(f) 215 | 216 | `flip(f)` reverses the order of arguments applied to `f`. 217 | ```c++ 218 | static_assert(fu::flip(fu::sub)(y,x) == fu::sub(x,y)); 219 | 220 | auto h(A, B, C, D); 221 | flip(h, d, c, b, a); // invokes: h(a,b,c,d) 222 | ``` 223 | 224 | ## constant(x) 225 | 226 | `constant(x)` returns a nullary function (takes no arguments) that always 227 | returns `x`, 228 | ```c++ 229 | constexpr auto one = constant(1); 230 | one(); // returns 1 231 | ``` 232 | 233 | # "fu/utility.h" 234 | 235 | Implements transparent function objects for operators like `+`, `-`, `%`, etc., 236 | as well as utilities for containers like `size`, `push_back`, etc.. 237 | 238 | ## size, index, back, front 239 | 240 | These functions take a container or array as arguments. 241 | 242 | ```c++ 243 | int xs[5] = {0,1,2,3,4}; 244 | std::list ys = {0,1,2,3,4}; 245 | 246 | fu::size(xs); // returns 5 247 | fu::size(ys); // returns 5 248 | fu::front(xs); // returns 0 249 | fu::back(ys); // returns 4 250 | ``` 251 | 252 | ## push_back, push_front, insert 253 | 254 | These functions call the associated member function, but take the object to 255 | insert as the first argument. 256 | 257 | ```c++ 258 | std::set ys(4); 259 | fu::insert(10, ys); // ys = {10} 260 | 261 | int xs[] = {0,1,2}; 262 | std::for_each(std::begin(ys), std::end(ys), fu::flip(fu::insert, std::ref(ys)); 263 | // ys = {0, 1, 2, 10} 264 | ``` 265 | 266 | ## min, max 267 | 268 | ```c++ 269 | auto five = fu::max(1,2,3,4,5); 270 | auto one = fu::min(1,2,3,4,5); 271 | 272 | constexpr auto one_or_more = fu::max(1); 273 | constexpr auto ten_or_less = fu::min(10); 274 | ``` 275 | 276 | ## add, sub(tract), mult(iply) div(ide), rem(ainder), lshift, rshift, or\_, and\_, xor\_ bit_or 277 | 278 | These functions are all both `multary` and left-associative. 279 | 280 | ```c++ 281 | constexpr auto plus_one = add(1); 282 | constexpr int two = plus_one(1); 283 | constexpr int ten = add(1,2,3,4); 284 | constexpr bool yes = or_(false, false, false, true); 285 | constexpr bool no = and_(true, false, true, false); 286 | ``` 287 | Also implemented: xor_ex, add_eq, sub_eq, mult_eq, div_eq, rem_eq, 288 | 289 | ## less, greater, eq, neq, less_eq, greater_eq 290 | 291 | These function are `multary` and `transitive`. 292 | ```c++ 293 | static_assert(less(1,2,3,4), "computes '1 < 2 && 2 < 3 && 3 < 4'"); 294 | static_assert(less_eq(1,2,2,3), "computes '1 <= 2 && 2 <= 2 && 2 <= 3'"); 295 | static_assert(eq(1)(1,1), "computes '1 == 1 && 1 == 1 && 1 == 1'"); 296 | ``` 297 | -------------------------------------------------------------------------------- /include/fu/basic.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /// This file includes the basic functionality used to build other modules. 13 | 14 | namespace fu { 15 | 16 | template 17 | using enable_if_t = typename std::enable_if::type; 18 | 19 | /// identity(x) = x 20 | /// identity(f, x...) = f(x...) 21 | constexpr struct identity_f { 22 | template 23 | constexpr X operator() (X&& x) const { 24 | return std::forward(x); 25 | } 26 | } identity{}; 27 | 28 | /// forwarder_f -- A function type-erasure. 29 | /// 30 | /// Base case: Function pointers and objects. 31 | /// Lifts functions pointers to objects and acts as a type erasure between 32 | /// function pointers and objects. 33 | template 34 | struct forwarder_f : public F { 35 | F f; 36 | constexpr forwarder_f(F f) : f(std::move(f)) { } 37 | 38 | template 39 | constexpr decltype(auto) operator() (X&&...x) const { 40 | return f(std::forward(x)...); 41 | } 42 | }; 43 | 44 | /// forwarder_f: Function reference specialization. 45 | template 46 | struct forwarder_f { 47 | // Function type must be converted to a pointer. 48 | using type = R(*)(X...); 49 | 50 | // Note: gcc 4.9 will not consider `type f` a constexpr. 51 | R(*f)(X...); 52 | constexpr forwarder_f(type f) : f(f) { } 53 | 54 | constexpr R operator() (X&&...x) const { 55 | return f(std::forward(x)...); 56 | } 57 | }; 58 | 59 | /// forwarder_f: Function pointer specialization. 60 | template 61 | struct forwarder_f : forwarder_f { 62 | using base = forwarder_f; 63 | using base::base; 64 | using base::operator(); 65 | }; 66 | 67 | /// Non-std mem_fn (Like std::mem_fn, but can be constexpr). 68 | template 69 | struct MemFn { 70 | F f; 71 | 72 | constexpr MemFn(F f) : f(f) { } 73 | 74 | template 75 | constexpr decltype(auto) operator() (O&& o, X&&...x) const { 76 | return invoke_member(f, std::forward(o), std::forward(x)...); 77 | } 78 | }; 79 | 80 | template 81 | struct MemFn { 82 | using F = R O::*; 83 | F f; 84 | 85 | constexpr MemFn(F f) : f(f) { } 86 | 87 | constexpr decltype(auto) operator() (O& o) const { 88 | return invoke_member(f, o); 89 | } 90 | 91 | constexpr decltype(auto) operator() (O&& o) const { 92 | return invoke_member(f, std::move(o)); 93 | } 94 | 95 | constexpr decltype(auto) operator() (const O& o) const { 96 | return invoke_member(f, o); 97 | } 98 | 99 | constexpr decltype(auto) operator() (const O&& o) const { 100 | return invoke_member(f, std::move(o)); 101 | } 102 | 103 | constexpr decltype(auto) operator() (O* o) const { 104 | return invoke_member(f, o); 105 | } 106 | 107 | constexpr decltype(auto) operator() (const O* o) const { 108 | return invoke_member(f, o); 109 | 110 | } 111 | }; 112 | 113 | /// Basic member function object given a member function, F, and an object 114 | /// type, O. 115 | template 116 | struct basic_mem_fn_f { 117 | F f; 118 | 119 | constexpr basic_mem_fn_f(F f) : f(f) { } 120 | 121 | template 122 | constexpr decltype(auto) operator()(O&& o, X&&...x) const { 123 | return invoke_member(f, std::forward(o), std::forward(x)...); 124 | } 125 | }; 126 | 127 | 128 | template 129 | struct MemFn : basic_mem_fn_f { 130 | using base = basic_mem_fn_f; 131 | using base::base; 132 | using base::operator(); 133 | }; 134 | 135 | template 136 | struct MemFn : basic_mem_fn_f { 137 | using base = basic_mem_fn_f; 138 | using base::base; 139 | using base::operator(); 140 | }; 141 | 142 | template 143 | struct MemFn : basic_mem_fn_f { 144 | using base = basic_mem_fn_f; 145 | using base::base; 146 | using base::operator(); 147 | }; 148 | 149 | template 150 | struct MemFn 151 | : basic_mem_fn_f 152 | { 153 | using base = basic_mem_fn_f; 154 | using base::base; 155 | using base::operator(); 156 | }; 157 | 158 | template 159 | struct MemFn 160 | : basic_mem_fn_f 161 | { 162 | using base = basic_mem_fn_f; 163 | using base::base; 164 | using base::operator(); 165 | }; 166 | 167 | template 168 | struct MemFn 169 | : basic_mem_fn_f 170 | { 171 | using base = basic_mem_fn_f; 172 | using base::base; 173 | using base::operator(); 174 | }; 175 | 176 | /// MemFn constructor. 177 | template 178 | constexpr MemFn mem_fn(F f) { 179 | return {f}; 180 | } 181 | 182 | /// forwarder: Ensures function, f, is an object. 183 | template 184 | constexpr F forwarder(F&& f) { return std::forward(f); } 185 | 186 | /// Function pointer overload: Lifts `f` to a function object. 187 | template 188 | constexpr forwarder_f forwarder(R(*f)(X...)) { return f; } 189 | 190 | /// Member function overload: Lifts `f` using MemFn. 191 | template 192 | constexpr auto forwarder(T O::*f) { return mem_fn(f); } 193 | 194 | /// Makes a function-object type out of `F`. 195 | template 196 | using ToFunctor = decltype(forwarder(std::declval())); 197 | 198 | /// Partial Application 199 | template 200 | struct Part { 201 | F f; 202 | 203 | std::tuple t; 204 | 205 | constexpr Part(F f, X...x) : f(std::move(f)) 206 | , t(std::forward(x)...) 207 | { 208 | } 209 | 210 | template> 211 | static constexpr Tuple args(Y&&...y) { 212 | return Tuple(std::forward(y)...); 213 | } 214 | 215 | // NOTE: due to gcc bug, decltype(auto) may not be used to define operator() 216 | // because the wrong overloads will be chosen. 217 | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64562 218 | #ifdef __clang__ 219 | # define RESULT(F) decltype(auto) 220 | #else 221 | # define RESULT(F) std::result_of_t 222 | #endif 223 | 224 | template 225 | constexpr RESULT(const F&) operator() (Y&&...y) const & { 226 | return tpl::apply(f, t, args(std::forward(y)...)); 227 | } 228 | 229 | template 230 | constexpr RESULT(const F&&) operator() (Y&&...y) && { 231 | return tpl::apply(std::move(f), std::move(t), args(std::forward(y)...)); 232 | } 233 | 234 | #ifdef __clang__ 235 | template 236 | constexpr RESULT(F&) operator() (Y&&...y) & { 237 | return tpl::apply(f, t, args(std::forward(y)...)); 238 | } 239 | 240 | template 241 | constexpr RESULT(const F&&) operator() (Y&&...y) const && { 242 | return tpl::apply(std::move(f), std::move(t), args(std::forward(y)...)); 243 | } 244 | #endif 245 | 246 | #undef RESULT 247 | }; 248 | 249 | /// Reversed-Partial Application 250 | template 251 | struct rpart_f { 252 | F f; 253 | 254 | std::tuple t; 255 | 256 | constexpr rpart_f(F f, X...x) : f(std::move(f)) 257 | , t(std::forward(x)...) 258 | { 259 | } 260 | 261 | template> 262 | static constexpr Tuple args(Y&&...y) { 263 | return Tuple(std::forward(y)...); 264 | } 265 | 266 | // NOTE: due to gcc bug, decltype(auto) may not be used to define operator() 267 | // because the wrong overloads will be chosen. 268 | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64562 269 | #ifdef __clang__ 270 | # define RESULT(F) decltype(auto) 271 | #else 272 | # define RESULT(F) std::result_of_t 273 | #endif 274 | 275 | template 276 | constexpr RESULT(const F&) operator() (Y&&...y) const & { 277 | return tpl::apply(f, args(std::forward(y)...), t); 278 | } 279 | 280 | template 281 | constexpr RESULT(const F&&) operator() (Y&&...y) && { 282 | return tpl::apply(std::move(f), args(std::forward(y)...), std::move(t)); 283 | } 284 | 285 | #ifdef __clang__ 286 | template 287 | constexpr RESULT(F&) operator() (Y&&...y) & { 288 | return tpl::apply(f, args(std::forward(y)...), t); 289 | } 290 | 291 | template 292 | constexpr RESULT(const F&&) operator() (Y&&...y) const && { 293 | return tpl::apply(std::move(f), args(std::forward(y)...), std::move(t)); 294 | } 295 | #endif 296 | 297 | #undef RESULT 298 | }; 299 | 300 | 301 | /// Closure: partial application by copying the parameters. 302 | /// Given f(x,y,z): 303 | /// closure(f,x) = g(y,z) = f(x,y,z) 304 | /// closure(f,x,y) = g(z) = f(x,y,z) 305 | constexpr auto closure = MakeT{}; 306 | 307 | /// Like closure, but forwards its arguments. 308 | constexpr auto part = ForwardT{}; 309 | 310 | /// Closure: reverse partial application by copying the parameters. 311 | /// Given f(x,y,z): 312 | /// closure(f,z) = g(x,y) = f(x,y,z) 313 | /// closure(f,y,z) = g(x) = f(x,y,z) 314 | constexpr auto rclosure = MakeT{}; 315 | 316 | /// Like closure, but forwards its arguments. 317 | constexpr auto rpart = ForwardT{}; 318 | 319 | /// A function that takes `n` or more arguments. If given only one argument, it 320 | /// will return a partial application. 321 | template 322 | struct multary_n_f : ToFunctor<_F> { 323 | using F = ToFunctor<_F>; 324 | 325 | constexpr multary_n_f(F f) : F(std::move(f)) { } 326 | 327 | // The result of applying this m arguments where m <= n. 328 | template 329 | using Partial = multary_n_f>; 330 | 331 | /// Too few arguments: Return another multary function. 332 | template> 333 | constexpr Partial operator() (X...x) const & { 334 | return Partial(closure(F(*this), std::move(x)...)); 335 | } 336 | 337 | /// Exactly n arguments: Partially apply. 338 | template> 339 | constexpr Part operator() (X...x) const & { 340 | return closure(F(*this), std::move(x)...); 341 | } 342 | 343 | /// More than n arguments: invoke. 344 | template n)>> 345 | constexpr decltype(auto) operator() (X&&...x) const & 346 | { 347 | return static_cast(*this)(std::forward(x)...); 348 | } 349 | }; 350 | 351 | template 352 | struct multary_n_f<0, F> : ToFunctor { 353 | using Fn = ToFunctor; 354 | using Fn::operator(); 355 | }; 356 | 357 | /// A function that takes two or more arguments. If given only one argument, it 358 | /// will return a partial application. 359 | constexpr struct multary_f { 360 | // TODO: Make the n an std::integer_constant so that 361 | // basic_multary = MakeT{}; 362 | // multary = part(basic_multary, Int<0>{}) 363 | template 364 | constexpr multary_n_f<1, F> operator() (F f) const { 365 | return multary_n_f<1, F>(std::move(f)); 366 | } 367 | } multary{}; 368 | 369 | /// A function that takes two or more arguments. If given only one argument, it 370 | /// will return a partial application. 371 | #ifdef __clang__ 372 | // For clang, experimentally define multary_n as a template variable. GCC 373 | // earlier than version 5 still cannot handle them. 374 | template 375 | struct make_multary_n_f { 376 | template 377 | constexpr auto operator() (F f) const -> multary_n_f 378 | { 379 | return {std::move(f)}; 380 | } 381 | 382 | template 383 | constexpr auto operator() (std::reference_wrapper f) const 384 | -> multary_n_f 385 | { 386 | return {std::move(f)}; 387 | } 388 | }; 389 | 390 | template 391 | constexpr auto multary_n = make_multary_n_f{}; 392 | #else 393 | template 394 | constexpr multary_n_f multary_n(F f) { 395 | return multary_n_f(std::move(f)); 396 | } 397 | #endif // __clang__ 398 | 399 | } // namspace fu 400 | -------------------------------------------------------------------------------- /include/fu/fu.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | -------------------------------------------------------------------------------- /include/fu/functional.h: -------------------------------------------------------------------------------- 1 | 2 | /// General function-object utilities. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace fu { 13 | 14 | /// Rank: Uses inheritance to communicate precedence in overloading. 15 | /// 16 | /// See: http://flamingdangerzone.com/cxx11/2013/03/11/overload-ranking.html 17 | template 18 | struct Rank : Rank { 19 | }; 20 | 21 | /// Rank base case: All Ranks inherit from Rank<0>. 22 | template<> 23 | struct Rank<0> { 24 | }; 25 | 26 | constexpr struct sequence_f { 27 | template 28 | constexpr decltype(auto) operator() (F&& f) const { 29 | return fu::invoke(std::forward(f)); 30 | } 31 | 32 | template 33 | constexpr decltype(auto) operator() (F&& f, G&& g, H&&...h) const { 34 | // Note the use of comma operator. 35 | return fu::invoke(std::forward(f)), (*this)(std::forward(g), 36 | std::forward(h)...); 37 | } 38 | } sequence{}; 39 | 40 | struct lassoc_f { 41 | template 42 | constexpr decltype(auto) operator() (F&& f, X&& x, Y&& y) const { 43 | return invoke(std::forward(f), std::forward(x), std::forward(y)); 44 | } 45 | 46 | template 0)>> 48 | constexpr decltype(auto) operator() (const F& f, X&& x, Y&& y, Z&&...z) const { 49 | return (*this)(f, 50 | invoke(f, std::forward(x), std::forward(y)), 51 | std::forward(z)...); 52 | } 53 | }; 54 | 55 | /// Right-associative application. 56 | struct rassoc_f { 57 | template 58 | constexpr decltype(auto) operator() (F&& f, X&& x, Y&& y) const { 59 | return invoke(std::forward(f), std::forward(x), std::forward(y)); 60 | } 61 | 62 | template 1)>> 64 | constexpr decltype(auto) operator() (const F& f, X&& x, Y&&...y) const { 65 | return invoke(f, std::forward(x), 66 | (*this)(f, std::forward(y)...)); 67 | } 68 | }; 69 | 70 | /// Left-associative application. 71 | /// 72 | /// Ex: lassoc(+)(1,2,3) = (1+2) + 3 73 | constexpr auto lassoc = multary(lassoc_f{}); 74 | 75 | struct transitive_f { 76 | /// trans(b,j,x,y) = b(x,y) 77 | template 78 | constexpr auto operator() (Binary&& b, const Join&, X&& x, Y&& y) const { 79 | return fu::invoke(std::forward(b), 80 | std::forward(x), std::forward(y)); 81 | } 82 | 83 | /// trans(b,j,x,y,z...) = j(b(x,y), b(y,z...)) 84 | template 0)>> 86 | constexpr auto operator() (const Binary& b, const Join& j, 87 | X&& x, const Y& y, Z&&...z) const 88 | { 89 | return fu::invoke(j, 90 | fu::invoke(b, std::forward(x), y), 91 | (*this)(b, j, y, std::forward(z)...)); 92 | } 93 | }; 94 | 95 | /// transitive(b,j) -- produces a function, t, such that 96 | /// t(x,y) <=> b(x,y) 97 | /// t(x,y,z) <=> j(b(x,y), b(y,z)) 98 | /// 99 | /// Ex: t = transitive(<, &&) 100 | /// t(x,y) == x < y 101 | /// t(x,y,z) == x < y && y < z 102 | constexpr auto transitive = multary_n<2>(transitive_f{}); 103 | 104 | /// A function object that lifts C++ overloading rules to a type class. 105 | template 106 | struct Overloaded : public ToFunctor, public ToFunctor { 107 | constexpr Overloaded(F f, G g) : ToFunctor(std::move(f)), 108 | ToFunctor(std::move(g)) 109 | { } 110 | 111 | using ToFunctor::operator(); 112 | using ToFunctor::operator(); 113 | }; 114 | 115 | /// Constructs and overloaded function. 116 | /// Ex: 117 | /// void f(int); 118 | /// void g(float); 119 | /// auto o = overload(f,g); 120 | /// o(1); // calls f(int) 121 | /// o(0.0f); // calls g(float) 122 | constexpr auto overload = lassoc(MakeT{}); 123 | 124 | /// Like Overloaded, but uses Rank to decide whether to call F (preferred) or G. 125 | template 126 | struct RankOverloaded { 127 | ToFunctor f; 128 | ToFunctor g; 129 | 130 | constexpr RankOverloaded(F f, G g) : f(std::move(f)) 131 | , g(std::move(g)) 132 | { } 133 | 134 | template 135 | constexpr auto call(Rank<1>, X&&...x) const 136 | -> decltype(f(std::declval()...)) 137 | { 138 | return f(std::forward(x)...); 139 | } 140 | 141 | template 142 | constexpr auto call(Rank<0>, X&&...x) const 143 | -> decltype(g(std::declval()...)) 144 | { 145 | return g(std::forward(x)...); 146 | } 147 | 148 | template 149 | constexpr auto operator() (X&&...x) const 150 | // NOTE: GCC fails to compile without the "this->". 151 | -> decltype(this->call(Rank<1>{}, std::declval()...)) 152 | { 153 | return call(Rank<1>{}, std::forward(x)...); 154 | } 155 | }; 156 | 157 | /// Like overload(), but uses Rank to dispatch which function to call, 158 | /// preferring the first over the last. 159 | constexpr auto ranked_overload = lassoc(MakeT{}); 160 | 161 | struct compose_f { 162 | /// Applies the inner function; returns a tuple so that it can be 163 | /// concatenated with the arguments for the outer function. 164 | template 165 | static constexpr decltype(auto) app1(G&& g, Tuple&& t) { 166 | return tpl::forward_tuple(tpl::apply(std::forward(g), 167 | std::forward(t))); 168 | } 169 | 170 | template 171 | constexpr auto operator() (F&& f, G&& g, TupleA&& a, TupleB&& b) const 172 | -> decltype(auto) 173 | { 174 | return tpl::apply(std::forward(f), 175 | app1(std::forward(g), std::forward(a)), 176 | std::forward(b)); 177 | } 178 | }; 179 | 180 | /// Unary Composition 181 | /// 182 | /// Lemma: 183 | /// (f . g)(x) = f(g(x)) 184 | /// (f . g)(x,y,z) = f(g(x), y, z) 185 | struct ucompose_f { 186 | template 187 | constexpr decltype(auto) operator() (F&& f, G&& g, X &&x, Y&&...y) const { 188 | return fu::invoke(std::forward(f), 189 | fu::invoke(std::forward(g), std::forward(x)), 190 | std::forward(y)...); 191 | } 192 | }; 193 | 194 | struct mcompose_f { 195 | template 196 | constexpr decltype(auto) operator() (F&& f, G&& g, X&&...x) const { 197 | return fu::invoke(std::forward(f), 198 | fu::invoke(std::forward(g), std::forward(x)...)); 199 | } 200 | }; 201 | 202 | /// Creates a unary composition: ucompose(f,g)(x) = f(g(x)) 203 | constexpr auto ucompose = lassoc(multary_n<2>(ucompose_f{})); 204 | 205 | /// Creates a generic composition. 206 | /// 207 | /// compose(f,g)({x...}, {y...}) = f(g(x...), y...) 208 | constexpr auto compose = multary_n<2>(compose_f{}); 209 | // FIXME: Should be `lassoc`, but the current definition won't work. 210 | 211 | /// Creates an M-arity composition. 212 | /// 213 | /// mcompose(f,g)(x) <=> ucompose(f,g)(x) 214 | /// mcompose(f,g)(x...) <=> f(g(x...)) 215 | /// mcompose(f,g,h)(x...) <=> f(g(h(x...))) 216 | constexpr auto mcompose = lassoc(multary_n<2>(mcompose_f{})); 217 | 218 | template 219 | struct compose_n_f { 220 | template 221 | static constexpr decltype(auto) do_invoke(std::index_sequence, 222 | std::index_sequence, 223 | F&& f, G&& g, Tuple&& t) { 224 | return invoke(std::forward(f), 225 | invoke(std::forward(g), 226 | std::get(std::forward(t))...), 227 | std::get(std::forward(t))...); 228 | } 229 | 230 | 231 | template 232 | static constexpr decltype(auto) split(F&& f, G&& g, Tuple&& t) { 233 | using is = decltype(iseq::make(t)); 234 | return do_invoke(iseq::take(is{}), iseq::drop(is{}), 235 | std::forward(f), 236 | std::forward(g), 237 | std::forward(t)); 238 | } 239 | 240 | template 241 | constexpr decltype(auto) operator() (F&& f, G&& g, X&&...x) const { 242 | return split(std::forward(f), std::forward(g), 243 | tpl::forward_tuple(std::forward(x)...)); 244 | } 245 | }; 246 | 247 | template 248 | constexpr auto compose_n(F f, G g) { 249 | return multary_n(closure(compose_n_f{}, std::move(f), std::move(g))); 250 | } 251 | 252 | struct fix_f { 253 | template 254 | constexpr auto rec(const F& f) const { 255 | return part(fix_f{}, f); 256 | } 257 | 258 | template 259 | 260 | #ifndef __clang__ 261 | constexpr 262 | #endif 263 | decltype(auto) operator() (const F& f, X&&...x) const 264 | { 265 | return f(rec(f), std::forward(x)...); 266 | } 267 | }; 268 | 269 | /// Fixed-point combinator: fix(f,x) <=> f(fix(f), x) 270 | /// http://en.wikipedia.org/wiki/Fixed-point_combinator 271 | /// 272 | /// Allows one to define a recursive function without referring to itself. 273 | /// `f` must accept a function as its first argument and call that function 274 | /// in order to recurse. `f` must not use `auto` as the return type to 275 | /// prevent "use of f before deduction of auto" errors. Due to a bug in 276 | /// clang (http://llvm.org/bugs/show_bug.cgi?id=20090), `f` may not be 277 | /// marked `constexpr`. 278 | /// 279 | /// Ex: 280 | /// constexpr int pow2 = fix([](auto rec, int x) -> int { 281 | /// return x ? 2 * rec(x-1) : 1; 282 | /// }); 283 | constexpr auto fix = multary(fix_f{}); 284 | 285 | struct proj_f { 286 | template 0)>> 288 | constexpr decltype(auto) operator() (F&& f, const ProjF& pf, X&&...x) const & 289 | { 290 | return invoke(std::forward(f), 291 | invoke(pf, std::forward(x))...); 292 | } 293 | }; 294 | 295 | struct lproj_f { 296 | template 297 | constexpr decltype(auto) operator() (F&& f, ProjF&& pf, X&& x, Y&& y) const { 298 | return invoke(std::forward(f), 299 | invoke(std::forward(pf), 300 | std::forward(x)), 301 | std::forward(y)); 302 | } 303 | }; 304 | 305 | /// proj(f,pf) -- constructs a projection, p, such that 306 | /// p(x,y) <=> f(pf(x), pf(y)) 307 | /// p(x) <=> ucompose(f, pf)(x) 308 | constexpr auto proj = multary_n<2>(proj_f{}); 309 | 310 | /// lproj(f, pf, x, y) <=> f(pf(x), y) 311 | constexpr auto lproj = multary_n<2>(lproj_f{}); 312 | 313 | struct _less_helper { 314 | template 315 | constexpr bool operator() (const X& x, const Y& y) const { 316 | return x < y; 317 | } 318 | }; 319 | 320 | /// proj_less(pf, x, y) <=> pf(x) < pf(y) 321 | /// 322 | /// Useful for use with sorting/comparing functions. 323 | constexpr auto proj_less = proj(_less_helper{}); 324 | 325 | struct join_f { 326 | template 327 | constexpr decltype(auto) operator() (F&& f, Left&& l, Right&& r, 328 | X&& x, Y&& y) const 329 | { 330 | return invoke(std::forward(f), 331 | invoke(std::forward(l), std::forward(x)), 332 | invoke(std::forward(r), std::forward(y))); 333 | } 334 | }; 335 | 336 | /// join(f,l,r) -- constructs a projection, j, such that 337 | /// j(x,y) <=> f(l(x), r(y)) 338 | // TODO: multary3? 339 | constexpr auto join = multary_n<3>(join_f{}); 340 | 341 | struct split_f { 342 | template 343 | constexpr decltype(auto) operator() (F&& f, Left&& l, Right&& r, X& x) const 344 | { 345 | return invoke(std::forward(f), 346 | invoke(std::forward(l), x), 347 | invoke(std::forward(r), x)); 348 | } 349 | }; 350 | 351 | /// split(f,l,r)(x) <=> f(l(x), r(x)) 352 | constexpr auto split = multary_n<3>(split_f{}); 353 | 354 | struct proj_arg_f { 355 | template 356 | constexpr X&& operator() (std::integral_constant, X&& x, const Y&...) const { 357 | return std::forward(x); 358 | } 359 | 360 | template 0)>> 362 | constexpr decltype(auto) operator() (std::integral_constant, const X&, Y&&...y) const 363 | { 364 | static_assert(i < sizeof...(Y) + 1, "too few arguments"); 365 | return (*this)(Integral{}, std::forward(y)...); 366 | } 367 | }; 368 | 369 | /// proj_arg(Size<0>{}, x, y) <=> x 370 | /// proj_arg(Size<1>{}, x, y) <=> y 371 | constexpr auto proj_arg = multary(proj_arg_f{}); 372 | 373 | template 374 | constexpr decltype(auto) proj_arg_n(X&&...x) { 375 | return proj_arg(Size{}, std::forward(x)...); 376 | } 377 | 378 | struct flip_f { 379 | template 380 | static constexpr decltype(auto) reverse(std::integer_sequence, 381 | F&& f, 382 | X&&...x) 383 | { 384 | return fu::invoke(std::forward(f), 385 | proj_arg_n(std::forward(x)...)...); 386 | } 387 | 388 | template 389 | constexpr decltype(auto) operator() (F&& f, X&&...x) const { 390 | return reverse(std::index_sequence_for{}, 391 | std::forward(f), 392 | std::forward(x)...); 393 | } 394 | }; 395 | 396 | constexpr auto flip = multary_n<2>(flip_f{}); 397 | 398 | /// pipe(x,f,g) <=> g(f(x)) 399 | /// pipe(x) <=> x 400 | constexpr auto pipe = overload(identity, lassoc(flip(invoke))); 401 | // Informal proof: 402 | // lassoc(flip(invoke), x, f, g) = 403 | // flip(invoke)(flip(invoke)(x, f), g) = | definition of lassoc 404 | // invoke(g, invoke(f,x)) = g(f(x)) | definition of flip 405 | 406 | /// rproj(f, pf, x, y) <=> f(x, pf(y)) 407 | constexpr auto rproj = multary_n<2>(ucompose(compose_n<2>(flip, lproj), flip)); 408 | // Informal proof: 409 | // compose(compose<2>(flip, lproj), flip))(f)(pf)(x, y) = 410 | // compose<2>(flip, lproj)(flip(f), pf)(x, y) = | definition of compose 411 | // flip(lproj(flip(f) pf))(x, y) = | definition of compose<2> 412 | // lproj(flip(f), pf, y, x) = | definition of flip 413 | // flip(f)(pf(y) x) = f(x, pf(y)) | definition of lproj 414 | 415 | /// Right-associative application. 416 | /// rassoc(f, x, y, z) <=> f(x, f(y,z)) 417 | /// 418 | /// Ex: rassoc(+)(1,2,3) = 1 + (2+3) 419 | constexpr auto rassoc = multary(ucompose(flip, lassoc, flip)); 420 | // Informal proof: 421 | // compose(flip, lassoc, flip)(f)(x,y,z) = 422 | // flip(lassoc(flip(f)))(x,y,z) = | definition of compose 423 | // lassoc(flip(f), z, y, x) = | definition if flip and invoke 424 | // flip(f)(flip(f,z,y), x) = | definition of lassoc 425 | // f(x, f(y,z)) 426 | // 427 | // The extra `invoke` is required to make sure `flip . lassoc . flip` doesn't 428 | // get applied more than one argument. 429 | 430 | templateclass Enabler, class F> 431 | struct Enabled_f { 432 | F f; 433 | 434 | constexpr Enabled_f(F f) : f(std::move(f)) { } 435 | 436 | // FIXME: Should be able to take more than one argument, but compiler 437 | // complains. 438 | template::value>> 439 | constexpr decltype(auto) operator() (X&& x) const { 440 | return invoke(f, std::forward(x)); 441 | } 442 | }; 443 | 444 | templateclass Enabler, class F> 445 | constexpr Enabled_f enable_if_f(F f) { 446 | return {std::move(f)}; 447 | } 448 | 449 | template 450 | struct Constant { 451 | X x; 452 | 453 | constexpr Constant(X x) : x(std::move(x)) { } 454 | 455 | const X& operator() () const& { return x; } 456 | X& operator() () & { return x; } 457 | X operator() () && { return std::move(x); } 458 | }; 459 | 460 | constexpr auto constant = MakeT{}; 461 | 462 | } // namespace fu 463 | -------------------------------------------------------------------------------- /include/fu/invoke.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include // forward, declval 5 | #include // is_member_object_pointer 6 | 7 | namespace fu { 8 | 9 | constexpr struct maybe_deref_f { 10 | template< class O 11 | , class = std::enable_if_t>{}>> 12 | constexpr O&& operator() (O&& o) const { return std::forward(o); } 13 | 14 | template< class O 15 | , class = std::enable_if_t>{}>> 16 | constexpr decltype(auto) operator() (O&& o) const { 17 | return *std::forward(o); 18 | } 19 | } maybe_deref{}; 20 | 21 | constexpr struct invoke_member_f { 22 | template{}>> 24 | constexpr decltype(auto) operator()(F f, O&& o, X&&...x) const { 25 | return (maybe_deref(std::forward(o)).*f)(std::forward(x)...); 26 | } 27 | 28 | template{}>> 30 | constexpr decltype(auto) operator() (F f, O&& o) const { 31 | return maybe_deref(std::forward(o)).*f; 32 | } 33 | } invoke_member{}; 34 | 35 | constexpr struct invoke_f { 36 | template< class F, class...X 37 | , bool IsMem = std::is_member_pointer>{} 38 | , class = std::enable_if_t> 39 | constexpr decltype(auto) operator() (F&& f, X&&...x) const 40 | { 41 | return std::forward(f)(std::forward(x)...); 42 | } 43 | 44 | // Member function overloads: 45 | 46 | template< class F, class...X 47 | , bool IsMem = std::is_member_pointer{} 48 | , class = std::enable_if_t> 49 | constexpr decltype(auto) operator() (F f, X&&...x) const { 50 | return invoke_member(f, std::forward(x)...); 51 | } 52 | } invoke{}; 53 | 54 | } // namespace fu 55 | -------------------------------------------------------------------------------- /include/fu/iseq.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | 8 | namespace fu { 9 | namespace iseq { 10 | 11 | // TODO: implement std::index_sequence_for for C++11 12 | 13 | template using Integer = std::integral_constant; 14 | 15 | template 16 | struct IseqOf; 17 | 18 | template 19 | struct IseqOf> { 20 | using type = std::index_sequence_for; 21 | }; 22 | 23 | template 24 | using IseqOf_t = typename IseqOf::type>::type; 25 | 26 | // TODO: C++11 version 27 | template> 28 | constexpr Iseq make(const T&) { 29 | return Iseq{}; 30 | } 31 | 32 | template 33 | constexpr auto push(std::integer_sequence, Integer) 34 | -> std::integer_sequence 35 | { 36 | return {}; 37 | } 38 | 39 | template::type> 41 | constexpr auto drop(std::integer_sequence i) { 42 | return i; 43 | } 44 | 45 | template 0)>::type> 47 | constexpr auto drop(std::integer_sequence) { 48 | static_assert(X <= sizeof...(M) + 1, "Index too high."); 49 | return drop(std::integer_sequence{}); 50 | } 51 | 52 | template::type> 54 | constexpr auto take(std::integer_sequence i, 55 | std::integer_sequence) { 56 | return i; 57 | } 58 | 59 | template 0)>::type> 61 | constexpr auto take(std::integer_sequence i, 62 | std::integer_sequence j) 63 | { 64 | static_assert(X <= sizeof...(Ms) + 1, "Index too high."); 65 | return take(push(i, Integer{}), drop<1>(j)); 66 | } 67 | 68 | template 69 | constexpr auto take(std::integer_sequence i) 70 | { return take(std::integer_sequence{}, i); 71 | } 72 | 73 | } // namespace iseq 74 | } // namespace fu 75 | -------------------------------------------------------------------------------- /include/fu/list.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | namespace fu { 5 | 6 | //template 7 | //struct Remap; 8 | // 9 | //template 10 | //struct Remap> { 11 | // template 12 | // using type = std::vector; 13 | //}; 14 | // 15 | //template 16 | //struct Remap> { 17 | // template 18 | // using type = std::vector; 19 | //}; 20 | 21 | struct transform_f { 22 | template 23 | Xs& operator() (const F& f, Xs& xs) const { 24 | for (auto& x : xs) x = f(x); 25 | return xs; 26 | } 27 | }; 28 | 29 | constexpr auto transform = multary(transform_f{}); 30 | 31 | struct foldl_f { 32 | template 33 | constexpr X operator() (const F& f, X x0, Xs&& xs) const { 34 | for (auto it = std::begin(xs); it != std::end(xs); it++) 35 | x0 = f(x0, *it); 36 | return std::move(x0); 37 | } 38 | }; 39 | 40 | constexpr auto foldl = multary(foldl_f{}); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /include/fu/logic.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | -------------------------------------------------------------------------------- /include/fu/logic/README.md: -------------------------------------------------------------------------------- 1 | 2 | # fu::logic 3 | 4 | `"fu/logic.h"` includes a number of functions that take advantage of the 5 | properties of logical operations. Consider `fu::transitive(binary, join)`. 6 | ```c++ 7 | // transitive(b,j)(x,y,z) <=> j(b(x,y), b(y,z)) 8 | auto less = fu::transitive(std::less<>{}, std::logical_and<>{}); 9 | less(x,y,z); // computes and(less(x,y), less(y,z)) 10 | ``` 11 | While this version of `less` is correct, `less(x,y)` and `less(y,z)` must both 12 | be computed, even if `less(x,y)` returns false--but `less(x,y) && less(y,z)` 13 | would short-circuit if this first one failed. `transitive` is too generic to 14 | make a good `less`. 15 | 16 | `fu::logic::transitive` can be used to create short-circuit logical 17 | evaluations, although its interface looks quite obscure. 18 | ```c++ 19 | auto less = fu::logic::transitive(false, identity, std::less<>{}); 20 | less(1,2,3); // computes 1 < 2 && 2 < 3 21 | less(3,2,1); // computes 3 < 2 and stops 22 | ``` 23 | ## logic::transitive 24 | 25 | All of the relational operations from `"fu/utility.h"` define themselves by 26 | `op = logical_transitive(identity, eval, predicate)`. `identity` represents the 27 | identity element of the operation (`true` for `and` and `false` for `or`), 28 | `eval` returns a boolean deciding whether to recurse or not, and `predicate` is 29 | the actual function evaluating the arguments. 30 | 31 | Given `op(a,b,c,d)`, first `eval(predicate(a,b))` will be computed. If false, 32 | then it returns `identity`, or otherwise will compute `eval(predicate(b,c))` 33 | and repeat until the argument list ends. 34 | 35 | *(See above for code example.)* 36 | 37 | ## logical_project(identity, eval, pred), all, and any 38 | 39 | `logical_project` is an overly-generic function that will test every argument, 40 | `x_i`, with `eval(pred(x_i))` and returns `identity` when false. With only one 41 | argument, it returns `eval(x)`. As it happens, `all(f)` and `any(f)` can be 42 | defined in terms of `logic::project`. 43 | ```c++ 44 | /// all(pred)(x....) <=> pred(x) && ... 45 | constexpr auto all = logic::project(false, identity); 46 | 47 | /// any(pred)(x...) <=> pred(x) || ... 48 | constexpr auto any = logic::project(true, not_); 49 | 50 | auto big = [](int x) { return x > 5; }; 51 | assert(any(big, 3, 4, 5, 6)); 52 | assert(all(big, 6, 7, 8, 9)); 53 | ``` 54 | 55 | ## either, both 56 | 57 | `fu::logic::either(p1,p2)` returns a predicate, `e` such that `e(x,y)` computes 58 | `p1(x, y) || p2(x, y)`. `fu::logic::both` works the same way, but uses `and`. 59 | -------------------------------------------------------------------------------- /include/fu/logic/logic.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | 5 | namespace fu { 6 | namespace logic { 7 | 8 | constexpr struct basic_not_f { 9 | constexpr bool operator() (bool b) const { return !b; } 10 | } basic_not{}; 11 | 12 | /// Logical function projection. 13 | /// Invokes short-circuit logical operations on a predicate, p. 14 | /// 15 | /// logic::project(ident,ok,p,x,y) <=> ok(p(x)) ? p(y) : ident 16 | /// logic::project(false,id,p,x,y) <=> p(x) ? p(y) : false 17 | /// logic::project(true,(!),p,x,y) <=> !p(x) ? p(y) : true 18 | struct project_f { 19 | template 20 | constexpr decltype(auto) operator() (const Identity&, const Ok&, 21 | Pred&& p, X&& x) const { 22 | return fu::invoke(std::forward(p), std::forward(x)); 23 | } 24 | 25 | template 0)>> 27 | constexpr decltype(auto) operator() (Identity&& id, Ok&& ok, 28 | Pred&& p, X&& x, Y&&...y) const { 29 | return fu::invoke(ok, fu::invoke(p, std::forward(x))) 30 | ? (*this)(std::forward(id), std::forward(ok), 31 | std::forward(p), std::forward(y)...) 32 | : std::forward(id); 33 | } 34 | }; 35 | 36 | struct transitive_f { 37 | template 38 | constexpr decltype(auto) operator() (const Identity&, const Ok&, 39 | Pred&& p, X&& x, Y&& y) const { 40 | return fu::invoke(std::forward(p), 41 | std::forward(x), std::forward(y)); 42 | } 43 | 44 | template 0)>> 46 | constexpr decltype(auto) operator() (Identity&& id, Ok&& ok, 47 | Pred&& p, X&& x, Y&& y, Z&&...z) const { 48 | return fu::invoke(ok, fu::invoke(p, std::forward(x), y)) 49 | ? (*this)(std::forward(id), std::forward(ok), 50 | std::forward(p), 51 | std::forward(y), std::forward(z)...) 52 | : std::forward(id); 53 | } 54 | }; 55 | 56 | constexpr auto transitive = multary_n<4>(transitive_f{}); 57 | constexpr auto project = multary_n<3>(project_f{}); 58 | 59 | /// all(pred)(x....) <=> pred(x) && ... 60 | constexpr auto all = fu::logic::project(false, identity); 61 | 62 | /// any(pred)(x...) <=> pred(x) || ... 63 | constexpr auto any = fu::logic::project(true, basic_not); 64 | 65 | /// none(pred)(x...) <=> !pred(x) && ... 66 | constexpr auto none = multary(mcompose(basic_not, any)); 67 | 68 | struct either_f { 69 | template 70 | constexpr decltype(auto) operator() (F&& f, G&& g, X&&...x) const { 71 | return fu::invoke(std::forward(f), x...) 72 | || fu::invoke(std::forward(g), std::forward(x)...); 73 | } 74 | }; 75 | 76 | constexpr auto either = multary_n<2>(either_f{}); 77 | 78 | struct both_f { 79 | template 80 | constexpr decltype(auto) operator() (F&& f, G&& g, X&&...x) const { 81 | return fu::invoke(std::forward(f), x...) 82 | && fu::invoke(std::forward(g), std::forward(x)...); 83 | } 84 | }; 85 | 86 | constexpr auto both = multary_n<2>(both_f{}); 87 | 88 | } // namespace logic 89 | } // namespace fu 90 | -------------------------------------------------------------------------------- /include/fu/make/README.md: -------------------------------------------------------------------------------- 1 | 2 | # General object construction: 3 | 4 | The standard "make_" functions have generally all the same property. 5 | 6 | ```c++ 7 | std::tuple t = std::make_tuple(1, x, std::ref(y)); 8 | std::pair p = std::make_pair(1, std::ref(x)); 9 | std::optional o = std::make_optional(0); 10 | ``` 11 | 12 | The function takes template arguments (`f(X,Y)`) and produces its type based on 13 | those arguments (`tuple`). `MakeT` generalizes this principle. 14 | 15 | ```c++ 16 | constexpr auto make_pair = MakeT{}; 17 | constexpr auto make_tuple = MakeT{}; 18 | 19 | constexpr int one = 1; 20 | constexpr std::pair p = make_pair(one, std::ref(one)); 21 | ``` 22 | 23 | `MakeT` converts `std::reference_wrapper` objects into normal references, but 24 | otherwise decays the type of its argument. 25 | 26 | `TieT` and `ForwardT` work similarly. `TieT` constructs an object of references 27 | based on its parameters and `ForwardT` passes the parameters exactly. 28 | 29 | ```c++ 30 | constexpr auto tie = TieT{}; 31 | constexpr auto forward_tuple = ForwardT{}; 32 | 33 | int x; 34 | const int y; 35 | std::tuple t = tie(x, y); 36 | 37 | // Note: std::forward_as_tuple would return an std::tuple, 38 | // which would make this hold a reference to a temporary. 39 | std::tuple u = forward_tuple(0, x); 40 | ``` 41 | -------------------------------------------------------------------------------- /include/fu/make/make.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace fu { 8 | 9 | /// A type-class maker. 10 | /// Ex: 11 | /// std::make_pair <=> MakeT{}; 12 | /// std::make_tuple <=> MakeT{}; 13 | template class T> struct MakeT { 14 | /// Performs basic type transformations. 15 | template 16 | struct Ty { using type = X; }; 17 | 18 | /// Converts reference wrappers to references. 19 | template 20 | struct Ty> { using type = X&; }; 21 | 22 | template 23 | using Ty_t = typename Ty>::type; 24 | 25 | template 26 | constexpr auto operator() (X&& ...x) const { 27 | return T...>(std::forward(x)...); 28 | } 29 | }; 30 | 31 | /// Like MakeT, but using references, like std::tie. 32 | /// 33 | /// Ex: TieT{} <=> std::tie 34 | template class T> struct TieT { 35 | template 36 | constexpr auto operator() ( X& ...x ) const { 37 | return T(x...); 38 | } 39 | }; 40 | 41 | /// Like TieT, but using perfect forwarding. 42 | /// 43 | /// Ex: ForwardT{} <=> std::forward_as_tuple 44 | template class T> struct ForwardT { 45 | template 46 | constexpr auto operator() ( X&& ...x ) const { 47 | return T(std::forward(x)...); 48 | } 49 | }; 50 | 51 | } // namespace fu 52 | -------------------------------------------------------------------------------- /include/fu/meta.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace fu { 11 | 12 | // Define these outside meta because they can be used anywhere. 13 | 14 | template using Integral = std::integral_constant; 15 | template using Bool = Integral; 16 | template using Char = Integral; 17 | template using Int = Integral; 18 | template using UInt = Integral; 19 | template using Size = Integral; 20 | 21 | namespace meta { 22 | 23 | namespace detail { 24 | template struct all; 25 | template struct all : all { }; 26 | template<> struct all : Bool { }; 27 | template<> struct all<> : Bool { }; 28 | } 29 | 30 | template using all = detail::all; 31 | 32 | templateclass F, class...X> 33 | using Apply = F; 34 | 35 | template 36 | using ApplyT = Apply; 37 | 38 | template 39 | using Type = typename T::type; 40 | 41 | template 42 | constexpr auto value() { 43 | return T::value; 44 | } 45 | 46 | templateclass T, class...X> 47 | struct Part { 48 | template 49 | using type = T; 50 | }; 51 | 52 | templateclass T, templateclass U> 53 | struct UCompose { 54 | // FIXME: This should be able to take more arguments, but type = T,Y...> 55 | // does not compile when Y... is empty. 56 | template 57 | using type = T>; 58 | }; 59 | 60 | templateclass Binary, 61 | templateclass T, templateclass U = T> 62 | struct BCompose { 63 | template 64 | using type = Binary, U>; 65 | }; 66 | 67 | } // namespace meta 68 | } // namespace fu 69 | -------------------------------------------------------------------------------- /include/fu/tuple.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | -------------------------------------------------------------------------------- /include/fu/tuple/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Basic operations: 3 | 4 | FU defines `fu::tuple` as equivalent to `std::make_tuple`, `fu::tie` as `std::tie`, and `fu::forward_tuple` as similar to `std::forward_as_tuple`. `fu::forward_tuple` allows passing temporaries to create a non-temporary `tuple`. In general, `fu` uses the name of a type for the function that constructs it rather than supplying many "make_" functions. 5 | 6 | ```c++ 7 | std::tuple t = fu::tpl::tuple(1, x, std::ref(y)); 8 | std::tuple u = fu::tpl::forward_tuple(1, x, y); 9 | std::tuple w = fu::tpl::tie(x, y); 10 | ``` 11 | 12 | It also provides simple functions to retrieve tuple elements. 13 | 14 | ```c++ 15 | std::tuple t; 16 | 17 | using fu::tpl::_0; 18 | using fu::tpl::_1; 19 | using fu::tpl::last; 20 | _0(t); // eqivilant to std::get<0>(t) 21 | _1(t); // eqivilant to std::get<1>(t) 22 | 23 | constexpr auto _2 = fu::tpl::get_f<2>{}; // or use fu::tpl::_2 24 | _2(t); // equivalent to std::get<2>(t) 25 | 26 | last(t); // eqivilant to std::get{} - 1>(t) 27 | ``` 28 | 29 | To concatenate two tuples, one can use `fu::tpl::concat` as the polymorphic object form of `std::tuple_cat`. 30 | 31 | ```c++ 32 | using namespace fu::tpl; 33 | std::tuple t = concat(tuple(0), tuple(0)); 34 | ``` 35 | 36 | `init` and `tail` can be used to remove the first or last element. 37 | 38 | ```c++ 39 | using namespace fu::tpl; 40 | std::tuple t(x, y, z); 41 | std::tuple u = init(t); 42 | std::tuple v = tail(t); 43 | ``` 44 | 45 | # Application 46 | 47 | ``` 48 | 49 | | | | | | | | | | | | | | 50 | \|/ | | \| |/ \| |/ 51 | apply(f) map(f) zip(f) ap() 52 | | | | / \ / \ 53 | | / \ / \ | | 54 | f(x, y, z) 55 | ``` 56 | 57 | N3915 proposes `std::apply`, however neither gcc nor clang supplies it. `fu::tpl::apply` serves the same purpose, but implements a polymorphic function object. 58 | 59 | ```c++ 60 | using fu::tpl::apply; 61 | auto sum = apply(fu::add); 62 | 63 | std::tuple t(1,1); 64 | auto x = sum(t); // x == 2 65 | auto y = apply(fu::add, t); // y == 2 66 | ``` 67 | 68 | `fu::tpl::map` can be used to apply a function to each tuple element, and can be used on multiple tuples. 69 | 70 | ```c++ 71 | using namespace fu::tpl; 72 | 73 | // t = <0, 2.0> 74 | std::tuple t = map(fu::add(1), tuple(1, 1.0)); 75 | 76 | // u = <0+0, 0+2.0, 2.0+0, 2.0+2.0> = <0, 2.0, 2.0, 4.0> 77 | auto u = map(fu::add, t, t); 78 | ``` 79 | 80 | If a function returned a tuple, then `map(f)` would be a function returning atuple of tuples. `fu::tpl::concat` can be used to flatten the tuple. 81 | 82 | ```c++ 83 | using namespace fu::tpl; 84 | 85 | std::tuple t = tuple(x, x); 86 | std::tuple, std::tuple> u = map(tuple, t); 87 | std::tuple v = apply(concat, u); 88 | 89 | // More suscinctly: 90 | auto v2 = apply(concat, map(tuple, tuple(x,x))); 91 | ``` 92 | 93 | `fu::tpl::zip_with` can be used to join two or more tuples over a given function. `zip` will simply apply the function, `make_tuple`. 94 | 95 | ```c++ 96 | using namespace fu::tpl; 97 | 98 | using Vec = std::tuple 99 | auto a = tuple(x0, y0); 100 | auto b = tuple(x1, y1); 101 | auto dist = zip_with(fu::sub, b, a); 102 | assert(dist == tuple(x1 - x0, y1 - y0)); 103 | 104 | auto ab = zip(a,b); 105 | assert(ab == tuple(tuple(x0,x1), tuple(y0,y1))); 106 | ``` 107 | 108 | `fu::tpl::foldl` and `::foldr` allow one to fold a tuple by a binary function. 109 | 110 | ```c++ 111 | using namespace fu::tpl; 112 | 113 | auto sum = foldl(fu::add); 114 | int five = sum(tuple(2, 2, 1)); // five = 2 + (2+1) 115 | int five2 = foldr(fu::add, tuple(2, 2, 1)); // five2 = (2+2) + 1 116 | ``` 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /include/fu/tuple/basic.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace fu { 11 | namespace tpl { 12 | 13 | /// apply(f, {x...}) = f(x...) 14 | constexpr struct apply_f { 15 | template 16 | using Elem = decltype(std::get(std::declval())); 17 | 18 | template 19 | constexpr decltype(auto) operator() (std::integer_sequence, 20 | F&& f, Tuple&& t) const 21 | { 22 | return fu::invoke(std::forward(f), 23 | std::get(std::forward(t))...); 24 | } 25 | 26 | template 27 | constexpr decltype(auto) invoke1(F&& f, Tuple&& t) const { 28 | using IS = decltype(iseq::make(t)); 29 | return (*this)(IS{}, std::forward(f), std::forward(t)); 30 | } 31 | 32 | template 33 | constexpr auto operator() (F&& f, Tuple&&...t) const 34 | { 35 | // TODO: don't use temporary tuple 36 | return invoke1(std::forward(f), std::tuple_cat(std::forward(t)...)); 37 | } 38 | 39 | // Since apply_f is used to define generic partial application, it must 40 | // define the partial application of itself with a single function. 41 | template 42 | struct apply_1_f { 43 | F f; 44 | constexpr apply_1_f(F f) : f(std::move(f)) { } 45 | 46 | template 47 | constexpr decltype(auto) operator() (Tuple&& t) const { 48 | return apply_f{}(f, std::forward(t)); 49 | } 50 | }; 51 | 52 | template 53 | constexpr apply_1_f operator() (F f) const { 54 | return {std::move(f)}; 55 | } 56 | } apply{}; 57 | 58 | /// Constructs a tuple, similar to std::make_tuple. 59 | constexpr auto tuple = fu::MakeT{}; 60 | 61 | /// Constructs a tuple of references, similar to std::tie. 62 | constexpr auto tie = fu::TieT{}; 63 | 64 | /// Constructs a tuple, similar to std::forward_as_tuple. 65 | constexpr auto forward_tuple = fu::ForwardT{}; 66 | 67 | template 68 | constexpr std::integral_constant 69 | size(const std::tuple&) { return {}; } 70 | 71 | template 72 | constexpr decltype(fu::tpl::size(std::declval())) 73 | size() { return {}; } 74 | 75 | template 76 | struct get_f { 77 | template 78 | constexpr decltype(auto) operator() (Tuple&& t) const { 79 | return std::get(std::forward(t)); 80 | } 81 | }; 82 | 83 | template 84 | struct rget_f { 85 | template 86 | constexpr decltype(auto) operator() (Tuple&& t) const { 87 | return std::get() - i - 1>(std::forward(t)); 88 | } 89 | }; 90 | 91 | constexpr auto _0 = get_f<0>{}; 92 | constexpr auto _1 = get_f<1>{}; 93 | constexpr auto _2 = get_f<2>{}; 94 | constexpr auto _3 = get_f<3>{}; 95 | constexpr auto _4 = get_f<4>{}; 96 | 97 | constexpr auto last = rget_f<0>{}; 98 | 99 | } // namespace tpl 100 | } // namespace fu 101 | -------------------------------------------------------------------------------- /include/fu/tuple/tuple.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace fu { 12 | namespace tpl { 13 | 14 | constexpr struct concat_f { 15 | template 16 | constexpr auto operator() (Tuple&&...t) const { 17 | return std::tuple_cat(std::forward(t)...); 18 | } 19 | } concat{}; 20 | 21 | /// A perfect-forwarding version of std::tuple_element 22 | template 23 | using Elem = decltype(std::get(std::declval())); 24 | 25 | template 26 | constexpr auto applyI(F&& f, Tuple&&...t) { 27 | return invoke(f, std::get(t)...); 28 | } 29 | 30 | template 31 | struct applyI_f { 32 | template 33 | constexpr auto operator() (X&&...x) const { 34 | return applyI(std::forward(x)...); 35 | } 36 | }; 37 | 38 | struct apply_rows_f { 39 | template 40 | constexpr auto operator() (std::index_sequence, F&& f, T&&...t) const { 41 | return tuple(applyI(f, std::forward(t)...)...); 42 | } 43 | }; 44 | constexpr auto apply_rows = multary(apply_rows_f{}); 45 | 46 | /// map(f, {x...}) = {f(x)...} 47 | struct map_f { 48 | template 49 | constexpr auto do_map(std::index_sequence, 50 | const F& f, Tuple&& t) const { 51 | 52 | return tuple(applyI(f, std::forward(t))...); 53 | } 54 | template> 56 | constexpr auto do_map(std::index_sequence, 57 | const F& f, Tuple&& t, TupleB&&...tb) const { 58 | // let gi = part(f, xi) where xi is the i'th element of the tuple, t. 59 | // let ti = map(gi, tb...) 60 | // Since map(g) returns a tuple, our result is obtained by concatenating 61 | // the result of each ti. 62 | using namespace std; 63 | return concat((*this)(closure(f, get(forward(t))), 64 | forward(tb)...)...); 65 | } 66 | 67 | template 68 | constexpr auto operator() (std::index_sequence is, 69 | const F& f, Tuple&&...t) const { 70 | return do_map(is, f, std::forward(t)...); 71 | } 72 | 73 | template 74 | constexpr auto operator() (const F& f, Tuple&& t, Tpls&&...ts) const { 75 | using Size = std::tuple_size>; 76 | return do_map(std::make_index_sequence{}, 77 | f, 78 | std::forward(t), 79 | std::forward(ts)...); 80 | } 81 | }; 82 | 83 | constexpr auto map = multary(map_f{}); 84 | 85 | /// zip_with(f, {x,y,z}, {a,b,c}) = {f(x,a), f(y,b), f(z,c)} 86 | struct zip_with_f { 87 | template 88 | constexpr auto do_zip(std::index_sequence is, F&& f, T&&...t) const { 89 | return apply_rows(is, f, std::forward(t)...); 90 | } 91 | 92 | template 93 | constexpr auto operator() (F&& f, T&& t, U&&...u) const { 94 | static_assert(meta::all() == size()...>{}, 95 | "cannot zip tuples of varying size"); 96 | return do_zip(std::make_index_sequence()>{}, 97 | std::forward(f), 98 | std::forward(t), 99 | std::forward(u)...); 100 | } 101 | }; 102 | 103 | constexpr auto zip_with = multary(zip_with_f{}); 104 | constexpr auto zip = zip_with(tuple); 105 | 106 | /// ap({f,g,h}, {x,y,z}, {a,b,c}) = {f(x,a), g(y,b), h(z,c)} 107 | struct ap_f { 108 | template 109 | constexpr auto operator() (std::index_sequence is, 110 | Fs&& fs, Xs&&...xs) const { 111 | return apply_rows(is, invoke, fs, std::forward(xs)...); 112 | } 113 | 114 | template 115 | constexpr auto operator() (Fs&& fs, Xs&&...xs) const { 116 | using Size = std::tuple_size>; 117 | return (*this)(std::make_index_sequence{}, 118 | std::forward(fs), 119 | std::forward(xs)...); 120 | } 121 | }; 122 | 123 | constexpr auto ap = multary(ap_f{}); 124 | // FIXME: Should be definable by this equation: 125 | //constexpr auto ap = zip(invoke); 126 | 127 | struct foldl_f { 128 | template 129 | constexpr decltype(auto) operator() (const F& f, X&& acc, Tuple&& t, 130 | std::integer_sequence) const { 131 | return invoke(f, 132 | std::forward(acc), 133 | std::get(std::forward(t))); 134 | } 135 | 136 | template 0)>> 138 | constexpr decltype(auto) operator() (const F& f, X&& acc, Tuple&& t, 139 | std::integer_sequence) const 140 | { 141 | return (*this)(f, 142 | invoke(f, 143 | std::forward(acc), 144 | std::get(std::forward(t))), 145 | std::forward(t), 146 | std::integer_sequence{}); 147 | } 148 | 149 | /// foldl(f, x, {a,b,c}) = f(f(f(x,a), b), c) 150 | template 151 | constexpr decltype(auto) operator() (const F& f, X&& acc, Tuple&& t) const { 152 | return (*this)(f, std::forward(acc), std::forward(t), 153 | iseq::make(t)); 154 | } 155 | 156 | /// foldl(f, {a, b, c}) = f(f(a,b), c) 157 | template 158 | constexpr decltype(auto) operator() (const F& f, Tuple&& t) const { 159 | return (*this)(f, std::get<0>(std::forward(t)), 160 | std::forward(t), iseq::drop<1>(iseq::make(t))); 161 | } 162 | }; 163 | 164 | constexpr auto foldl = multary(foldl_f{}); 165 | 166 | struct foldr_f { 167 | template 168 | constexpr decltype(auto) operator() (const F& f, X&& acc, Tuple&& t, 169 | std::integer_sequence) const { 170 | return invoke(f, 171 | std::forward(acc), 172 | std::get(std::forward(t))); 173 | } 174 | 175 | template 0)>> 177 | constexpr decltype(auto) operator() (const F& f, X&& acc, Tuple&& t, 178 | std::integer_sequence) const { 179 | return invoke(f, 180 | (*this)(f, std::forward(acc), std::forward(t), 181 | std::integer_sequence{}), 182 | std::get(std::forward(t))); 183 | } 184 | 185 | /// foldr(f, x, {a,b,c}) = f(f(f(x,c), b), a) 186 | template 187 | constexpr decltype(auto) operator() (const F& f, X&& acc, Tuple&& t) const { 188 | return (*this)(f, std::forward(acc), std::forward(t), 189 | iseq::make(t)); 190 | } 191 | 192 | /// foldr(f, {a, b, c}) = f(f(c,b), a) 193 | template 194 | constexpr decltype(auto) operator() (const F& f, Tuple&& t) const { 195 | return (*this)(f, std::get<0>(std::forward(t)), 196 | std::forward(t), iseq::drop<1>(iseq::make(t))); 197 | } 198 | }; 199 | 200 | constexpr auto foldr = multary(foldr_f{}); 201 | 202 | constexpr struct init_f { 203 | /// init({x..., y}) = {x...} 204 | template 205 | constexpr auto operator() (Tuple&& t) const { 206 | using Size = std::tuple_size>; 207 | return map(iseq::take(iseq::make(t)), 208 | identity, std::forward(t)); 209 | } 210 | } init{}; 211 | 212 | constexpr struct tail_f { 213 | /// tail({y, x...}) = {x...} 214 | template 215 | constexpr auto operator() (Tuple&& t) const { 216 | return map(iseq::drop<1>(iseq::make(t)), 217 | identity, std::forward(t)); 218 | } 219 | } tail{}; 220 | 221 | constexpr struct rot_f { 222 | template 223 | static constexpr decltype(auto) do_rot(std::index_sequence<0, i...>, 224 | Tuple&& t) 225 | { 226 | return forward_tuple(std::get(std::forward(t))..., 227 | std::get<0>(std::forward(t))); 228 | } 229 | 230 | template 231 | constexpr decltype(auto) operator() (Tuple&& t) const { 232 | return do_rot(iseq::make(t), std::forward(t)); 233 | } 234 | } rot{}; 235 | 236 | constexpr struct rrot_f { 237 | template 238 | static constexpr decltype(auto) do_rot(std::index_sequence, 239 | Tuple&& t) 240 | { 241 | static_assert(sizeof...(i) == size() - 1, ""); 242 | return forward_tuple(std::get() - 1>(std::forward(t)), 243 | std::get(std::forward(t))...); 244 | } 245 | 246 | template 247 | constexpr decltype(auto) operator() (Tuple&& t) const { 248 | return do_rot(iseq::drop<1>(iseq::make(t)), 249 | std::forward(t)); 250 | } 251 | } rrot{}; 252 | 253 | } // namespace tpl 254 | } // namespace fu 255 | -------------------------------------------------------------------------------- /include/fu/utility.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace fu { 8 | 9 | /// Decorates a binary operation as multary and left-associative, and with an 10 | /// identity element. 11 | template 12 | constexpr auto numeric_binary(F f) { 13 | return pipe(std::move(f), lassoc, multary); 14 | } 15 | 16 | 17 | // Helper to define binary operations with identity elements. 18 | #define DECL_BIN_OP(name, op) \ 19 | struct name##_f { \ 20 | template \ 21 | constexpr auto operator() (X&& x, Y&& y) const \ 22 | -> decltype(auto) \ 23 | { return std::forward(x) op std::forward(y); } \ 24 | }; \ 25 | constexpr auto name = numeric_binary(name##_f{}); 26 | 27 | // Mathematical and arithmetic operators 28 | DECL_BIN_OP(add, +); 29 | DECL_BIN_OP(sub, -); 30 | DECL_BIN_OP(mult, *); 31 | DECL_BIN_OP(div, /); 32 | DECL_BIN_OP(rem, %); 33 | DECL_BIN_OP(add_eq, +=); 34 | DECL_BIN_OP(sub_eq, -=); 35 | DECL_BIN_OP(mult_eq, *=); 36 | DECL_BIN_OP(div_eq, /=); 37 | DECL_BIN_OP(rem_eq, %=); 38 | 39 | DECL_BIN_OP(lshift, <<); 40 | DECL_BIN_OP(rshift, >>); 41 | DECL_BIN_OP(lshift_eq, <<=); 42 | DECL_BIN_OP(rshift_eq, >>=); 43 | 44 | // Logical operators 45 | DECL_BIN_OP(or_, ||); 46 | DECL_BIN_OP(and_, &&); 47 | DECL_BIN_OP(xor_, ^); 48 | DECL_BIN_OP(bit_or, |); 49 | DECL_BIN_OP(xor_eq_, ^=); 50 | 51 | // TODO: Why does the macro fail on bit_and? 52 | //DECl_BIN_OP(bit_and, &, true); 53 | struct bit_and_f { 54 | template 55 | constexpr decltype(auto) operator() (X&& x, Y&& y) const { 56 | return std::forward(x) & std::forward(y); 57 | } 58 | }; 59 | constexpr auto bit_and = numeric_binary(bit_and_f{}); 60 | 61 | // Helper to define unary operators. 62 | #define DECL_UNARY(name, op) \ 63 | constexpr struct name##_f { \ 64 | template \ 65 | constexpr decltype(auto) operator() (X&& x) const \ 66 | { return op std::forward(x); } \ 67 | } name{}; 68 | 69 | DECL_UNARY(pos, +); 70 | DECL_UNARY(neg, -); 71 | DECL_UNARY(not_, !); 72 | DECL_UNARY(deref, *); 73 | DECL_UNARY(addr, &); // TODO: Use std::address_of(). 74 | 75 | constexpr struct numeric_relational_f { 76 | template 77 | constexpr auto operator() (Binary b, Identity ident=false, Ok ok = Ok{}) const 78 | { 79 | return logic::transitive(ident, ok, b); 80 | } 81 | } numeric_relational{}; 82 | 83 | // Helper to define binary relations. 84 | #define DECL_REL_OP(name, op) \ 85 | struct name##_f { \ 86 | template \ 87 | constexpr auto operator() (X&& x, Y&& y) const \ 88 | -> decltype(auto) \ 89 | { return std::forward(x) op std::forward(y); } \ 90 | }; \ 91 | constexpr auto name = numeric_relational(name##_f{}); 92 | 93 | // Relational operators 94 | DECL_REL_OP(less, <); 95 | DECL_REL_OP(greater, >); 96 | DECL_REL_OP(eq, ==); 97 | DECL_REL_OP(neq, !=); 98 | DECL_REL_OP(less_eq, <=); 99 | DECL_REL_OP(greater_eq, >=); 100 | 101 | #undef DECL_BIN_OP 102 | #undef DECL_REL_OP 103 | #undef DECL_UNARY 104 | 105 | constexpr auto inc = add(1); 106 | 107 | /// inc, but modifies its argument; returns a reference. 108 | constexpr struct pre_inc_f { 109 | template Number& operator() (Number& n) const { 110 | return ++n; 111 | } 112 | } pre_inc{}; 113 | 114 | constexpr struct pre_dec_f { 115 | template Number& operator() (Number& n) const { 116 | return --n; 117 | } 118 | } pre_dec{}; 119 | 120 | /// inc, but modifies its argument; returns the previous value. 121 | constexpr struct post_inc_f { 122 | template Number operator() (Number& n) const { 123 | return n++; 124 | } 125 | } post_inc{}; 126 | 127 | constexpr struct post_dec_f { 128 | template Number operator() (Number& n) const { 129 | return n--; 130 | } 131 | } post_dec{}; 132 | 133 | struct max_f { 134 | // FIXME: This should return decltype(auto), but GCC 4.9 deduces that the 135 | // call, max(1,2,3) returns a reference to temporary from Part::operator(). 136 | // Changing Part::args to act like std::forward_as_tuple fixes this, but 137 | // makes the expression non-constexpr. 138 | template 139 | constexpr auto operator() (X&& x, Y&& y) const { 140 | return x < y ? std::forward(y) : std::forward(x); 141 | } 142 | }; 143 | 144 | struct min_f { 145 | template 146 | constexpr auto operator() (X&& x, Y&& y) const { 147 | return x < y ? std::forward(x) : std::forward(y); 148 | } 149 | }; 150 | 151 | constexpr auto max = multary(lassoc(max_f{})); 152 | constexpr auto min = multary(lassoc(min_f{})); 153 | 154 | constexpr struct size_f { 155 | template 156 | constexpr std::size_t operator() (X (&)[N]) const { 157 | return N; 158 | } 159 | 160 | template 161 | constexpr auto operator() (const X& x) const { 162 | return x.size(); 163 | } 164 | } size{}; 165 | 166 | struct index_f { 167 | template 168 | constexpr decltype(auto) operator() (Index i, X&& x) const { 169 | return std::forward(x)[i]; 170 | } 171 | }; 172 | 173 | constexpr auto index = multary(index_f{}); 174 | 175 | constexpr struct back_f { 176 | template 177 | constexpr X& operator() (X (&arr)[N]) const { 178 | return arr[N-1]; 179 | } 180 | 181 | template 182 | constexpr decltype(auto) operator() (Container&& c) const { 183 | return std::forward(c).back(); 184 | } 185 | } back{}; 186 | 187 | constexpr struct front_f { 188 | template 189 | constexpr X& operator() (X* arr) const { 190 | return arr[0]; 191 | } 192 | 193 | template 194 | constexpr decltype(auto) operator() (Container&& c) const { 195 | return std::forward(c).front(); 196 | } 197 | } front{}; 198 | 199 | // TODO: While taking the element to insert first matches the functional style 200 | // better, does it make sense for C++? 201 | 202 | struct push_back_f { 203 | template 204 | Container&& operator() (X&& x, Container&& c) const { 205 | c.push_back(std::forward(x)); 206 | return std::forward(c); 207 | } 208 | }; 209 | 210 | constexpr auto push_back = multary(push_back_f{}); 211 | 212 | struct push_front_f { 213 | template 214 | Container&& operator() (X&& x, Container&& c) const { 215 | c.push_front(std::forward(x)); 216 | return std::forward(c); 217 | } 218 | }; 219 | 220 | constexpr auto push_front = multary(push_front_f{}); 221 | 222 | // TODO: emplace? 223 | 224 | struct insert_f { 225 | template 226 | auto operator() (X&& x, Container&& c) const { 227 | return c.insert(std::forward(x)); 228 | } 229 | }; 230 | 231 | constexpr auto insert = multary(insert_f{}); 232 | 233 | /// Function-object form of std::ref 234 | struct ref_f { 235 | template 236 | auto operator() (X&& x) const { 237 | return std::ref(x); 238 | } 239 | } ref{}; 240 | 241 | /// Function-object form of std::cref 242 | struct cref_f { 243 | template 244 | auto operator() (const X& x) const { 245 | return std::cref(x); 246 | } 247 | } cref{}; 248 | 249 | } // namespace fu 250 | -------------------------------------------------------------------------------- /run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "$CXX" = "clang++" ]; then export EXTRA="-stdlib=libc++ -I/usr/include/c++/v1"; fi 4 | 5 | for file in test/* 6 | do 7 | echo "compiling ${file}..." 8 | $CXX $file -std=c++14 -Iinclude -Wall -Wextra -Werror $EXTRA || exit 1 9 | ./a.out || exit 1 10 | done 11 | -------------------------------------------------------------------------------- /test/functional.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "fu/fu.h" 4 | 5 | constexpr int add3(int x, int y, int z) { 6 | return x + y + z; 7 | } 8 | 9 | constexpr int add_half(int x, int y) { 10 | return (x + y) / 2; 11 | } 12 | 13 | constexpr int half(int x) { return x / 2; } 14 | 15 | constexpr struct unfixed_pow2_f { 16 | template 17 | #ifndef __clang__ 18 | constexpr 19 | #endif 20 | std::size_t operator() (Rec r, std::size_t x) const { 21 | return x ? 2 * r(x-1) : 1; 22 | } 23 | } unfixed_pow2{}; 24 | 25 | constexpr int divide(int x, int y) { 26 | return x / y; 27 | } 28 | 29 | constexpr int ok() { 30 | static_assert(true, ""); 31 | return 0; 32 | } 33 | 34 | constexpr int five() { 35 | return 5; 36 | } 37 | 38 | int main() { 39 | static_assert(fu::sequence(ok, ok, five) == 5, ""); 40 | 41 | static_assert(fu::rassoc(divide, 10,2) == 10/2, ""); 42 | static_assert(fu::rassoc(divide, 10,8,4) == 10/(8/4), ""); 43 | static_assert(fu::rassoc(divide, 10,8,8,2) == 10/(8/(8/2)), ""); 44 | static_assert(fu::rassoc(divide)(10,8,8,2) == 10/(8/(8/2)), ""); 45 | 46 | // FIXME: gcc cannot evaluate some tests as constexpr, although clang can, 47 | // and it's vice versa for other tests. 48 | #ifdef __clang__ 49 | # define CLANG_STATIC_ASSERT(expr) static_assert(expr, "") 50 | # define GCC_STATIC_ASSERT(expr) assert(expr) 51 | #else 52 | # define CLANG_STATIC_ASSERT(expr) assert(expr) 53 | # define GCC_STATIC_ASSERT(expr) static_assert(expr, "") 54 | #endif 55 | using fu::inc; 56 | CLANG_STATIC_ASSERT((fu::ucompose(inc,inc,inc,inc)(1) == 5)); 57 | CLANG_STATIC_ASSERT((fu::mcompose(inc,inc,inc,inc)(1) == 5)); 58 | 59 | constexpr auto f = fu::compose(add3, add_half); 60 | 61 | using fu::tpl::tuple; 62 | static_assert(f(tuple(1,1), tuple(1,1)) == 3, ""); 63 | static_assert(f(tuple(2,2), tuple(1,1)) == 4, ""); 64 | 65 | #ifdef __clang__ 66 | // Assert that multary_n can be passed to higher order functions. 67 | constexpr auto _add3 = fu::pipe(add3, fu::multary_n<2>); 68 | static_assert(_add3(1)(1)(1) == 3, ""); 69 | #endif 70 | 71 | constexpr auto add_inc_half = fu::mcompose(half, inc, add3); 72 | static_assert(add_inc_half(1,2,3) == (6+1)/2, ""); 73 | 74 | static_assert(fu::rproj(fu::mult)(fu::add(1))(1,0) == 1, ""); 75 | static_assert(fu::rproj(fu::mult, fu::add(1))(1,0) == 1, ""); 76 | 77 | constexpr auto g = fu::compose_n<2>(add3, add_half); 78 | static_assert(g(1,1,1,1) == 3, ""); 79 | static_assert(g(2,2,1,1) == 4, ""); 80 | 81 | static_assert(fu::flip(g)(1,1,2,2) == 4, ""); 82 | static_assert(fu::flip(g,1,1,2,2) == 4, ""); 83 | 84 | static_assert(fu::flip(fu::sub, 5, 10) == 5, "flip(-,5,10) <=> 10 - 5"); 85 | static_assert(fu::flip(fu::less, 5, 4, 3, 2, 1), ""); 86 | 87 | constexpr auto pow2 = fu::fix(unfixed_pow2); 88 | GCC_STATIC_ASSERT(pow2(0) == 1); 89 | GCC_STATIC_ASSERT(pow2(1) == 2); 90 | GCC_STATIC_ASSERT(pow2(2) == 4); 91 | GCC_STATIC_ASSERT(pow2(3) == 8); 92 | 93 | static_assert(fu::rpart(fu::less, 10)(5), ""); 94 | static_assert(fu::rpart(fu::less, 5, 6, 7)(2,3,4), ""); 95 | } 96 | -------------------------------------------------------------------------------- /test/invoke.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum { 9 | INT, CHAR, FLOAT, DOUBLE, 10 | 11 | // Member functions 12 | REGULAR, REF, RVALUE, 13 | CONST_REF, CONST_RVALUE, 14 | MEMBER 15 | }; 16 | 17 | struct X { 18 | int x = MEMBER; 19 | 20 | constexpr int f() { return REGULAR; } 21 | constexpr int f_r() & { return REF; } 22 | constexpr int f_rr() && { return RVALUE; } 23 | constexpr int f_cr() const & { return CONST_REF; } 24 | constexpr int f_crr() const && { return CONST_RVALUE; } 25 | 26 | constexpr X() { } 27 | 28 | static constexpr const X Const() { return X(); } 29 | }; 30 | 31 | struct Y { 32 | int y = MEMBER; 33 | }; 34 | 35 | constexpr int f(double ) { return DOUBLE; } 36 | 37 | constexpr int g(float) { return FLOAT; } 38 | 39 | struct Int { 40 | constexpr int operator() (int) const { return INT; } 41 | }; 42 | 43 | struct Char { 44 | constexpr int operator() (char) const { return CHAR; } 45 | }; 46 | 47 | template 48 | constexpr R app1(R(*f)(X), X&& x) { 49 | return f(std::forward(x)); 50 | } 51 | 52 | constexpr int ord(char c) { return c - '0'; } 53 | 54 | template 55 | using Same = std::is_same; 56 | 57 | int main() { 58 | using fu::invoke; 59 | 60 | static_assert(invoke(f,0) == DOUBLE, ""); 61 | static_assert(invoke(Int{}, 0) == INT, ""); 62 | 63 | // Test passes as long as this compiles. 64 | constexpr X x; 65 | X nonConst; 66 | invoke(&X::f, nonConst); 67 | invoke(&X::f_r, nonConst); 68 | invoke(&X::f_rr, X()); 69 | invoke(&X::f_cr, x); 70 | invoke(&X::f_crr, X::Const()); 71 | invoke(&X::x, x); 72 | 73 | using Ref = decltype(invoke(&X::x, nonConst)); 74 | using CRef = decltype(invoke(&X::x, x)); 75 | using RVal = decltype(invoke(&X::x, X())); 76 | using CRVal = decltype(invoke(&X::x, std::move(x))); 77 | 78 | using PRef = decltype(invoke(&X::x, &nonConst)); 79 | using PCRef = decltype(invoke(&X::x, &x)); 80 | 81 | static_assert(Same::value, ""); 82 | static_assert(Same::value, ""); 83 | static_assert(Same::value, ""); 84 | static_assert(Same::value, ""); 85 | static_assert(Same::value, ""); 86 | static_assert(Same::value, ""); 87 | 88 | static_assert(fu::proj(fu::add, ord, '2', '3') == 5, ""); 89 | } 90 | -------------------------------------------------------------------------------- /test/logic.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | constexpr struct basic_less_f { 5 | template 6 | constexpr bool operator() (const X& x, const Y& y) const { 7 | return x < y; 8 | } 9 | } basic_less{}; 10 | 11 | int main() { 12 | using namespace fu::logic; 13 | 14 | constexpr auto larger_than_10 = fu::part(basic_less, 10); 15 | constexpr auto less_than_10 = fu::rpart(basic_less, 10); 16 | static_assert(all(larger_than_10, 11, 14, 20), ""); 17 | static_assert(!all(larger_than_10, 11, 14, 9), ""); 18 | static_assert(!all(larger_than_10, 1, 14, 11), ""); 19 | 20 | static_assert(any(larger_than_10, 11, 14, 20), ""); 21 | static_assert(any(larger_than_10, 11, 4, 9), ""); 22 | static_assert(!any(larger_than_10, 1, 4, 5), ""); 23 | 24 | static_assert(none(larger_than_10, 1, 3, 4), ""); 25 | static_assert(none(larger_than_10)(1, 3, 4), ""); 26 | static_assert(!none(larger_than_10, 1, 11, 4), ""); 27 | 28 | static_assert(either(larger_than_10, less_than_10, 9), ""); 29 | static_assert(!either(larger_than_10, less_than_10, 10), ""); 30 | static_assert(either(larger_than_10, less_than_10, 11), ""); 31 | 32 | constexpr auto less_than_20 = fu::rpart(basic_less, 20); 33 | static_assert(!both(larger_than_10, less_than_10, 15), ""); 34 | static_assert(both(larger_than_10, less_than_20, 15), ""); 35 | } 36 | -------------------------------------------------------------------------------- /test/meta.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | struct Double { 10 | template 11 | constexpr auto operator() (X x) const { 12 | return x * X(2); 13 | } 14 | }; 15 | 16 | template struct T{}; 17 | 18 | int main() { 19 | using namespace fu::meta; 20 | 21 | using Eq = BCompose; 22 | static_assert(ApplyT::value, ""); 23 | 24 | using TI = Apply::type, int>; 25 | static_assert(ApplyT>::value, ""); 26 | } 27 | -------------------------------------------------------------------------------- /test/overload.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum { 9 | INT, CHAR, FLOAT, DOUBLE, 10 | 11 | // Member functions 12 | REGULAR, REF, RVALUE, 13 | CONST_REF, CONST_RVALUE, 14 | MEMBER 15 | }; 16 | 17 | struct X { 18 | constexpr int f() { return REGULAR; } 19 | constexpr int f_r() & { return REF; } 20 | constexpr int f_rr() && { return RVALUE; } 21 | constexpr int f_cr() const & { return CONST_REF; } 22 | constexpr int f_crr() const && { return CONST_RVALUE; } 23 | 24 | constexpr X() { } 25 | 26 | static constexpr const X Const() { return X{}; } 27 | }; 28 | 29 | struct Y { 30 | int y = MEMBER; 31 | }; 32 | 33 | constexpr int f(double ) { return DOUBLE; } 34 | 35 | constexpr int g(float) { return FLOAT; } 36 | 37 | struct Int { 38 | constexpr int operator() (int) const { return INT; } 39 | }; 40 | 41 | struct Char { 42 | constexpr int operator() (char) const { return CHAR; } 43 | }; 44 | 45 | template 46 | constexpr R app1(R(*f)(X), X&& x) { 47 | return f(std::forward(x)); 48 | } 49 | 50 | int main() { 51 | constexpr auto set = 52 | fu::overload(Int{} ,Char{} 53 | ,f ,g 54 | ,&X::f ,&X::f_crr 55 | ); 56 | 57 | static_assert(set(0) == INT, ""); 58 | static_assert(fu::part(set,0)() == INT, ""); 59 | static_assert(set('x') == CHAR, ""); 60 | static_assert(set(0.0f) == FLOAT, ""); 61 | static_assert(set(0.0) == DOUBLE, ""); 62 | 63 | 64 | using IsInt = fu::meta::Part; 65 | using IsIntD = fu::meta::UCompose; 66 | constexpr auto onInt = 67 | fu::overload(fu::enable_if_f(Int{}) ,f); 68 | constexpr int zero = 0; 69 | static_assert(onInt(zero) == INT, ""); 70 | static_assert(onInt(0.0) != INT, ""); 71 | 72 | // BUG: gcc cannot deduce member function calls as constexpr. 73 | X x; 74 | assert(set(x) == REGULAR); 75 | assert(set(X::Const()) == CONST_RVALUE); 76 | 77 | // Use a ranked overload to dispatch between subtlety different values. 78 | constexpr auto mem_overload = 79 | // FIXME: since mem_fn(f) no longer perfect forwards the object parameter, 80 | // fu::overload should be usable for X's many member functions. But gcc 81 | // complains that this creates "ambiguous base classes". It does not have 82 | // this problem with fu::ranked_overload. Perhaps because of its use of 83 | // decltype? 84 | #ifndef __clang__ 85 | fu::ranked_overload; 86 | #else 87 | fu::overload; 88 | #endif 89 | constexpr auto mems = mem_overload( &X::f_r 90 | , &X::f_rr 91 | , &X::f_crr 92 | , &X::f_cr 93 | , &Y::y 94 | ); 95 | 96 | assert(mems(x) == REF); 97 | 98 | #ifdef __clang__ 99 | // FIXME: GCC invalidly tries to call X::f_r (requires non-const ref). It 100 | // compiles, but the assertions fail at runtime. 101 | static_assert(mems(X()) == RVALUE, ""); 102 | static_assert(mems(static_cast(x)) == CONST_REF, ""); 103 | static_assert(mems(X::Const()) == CONST_RVALUE, ""); 104 | #endif 105 | } 106 | -------------------------------------------------------------------------------- /test/tuple.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | struct Double { 8 | template 9 | constexpr auto operator() (X x) const { 10 | return x * X(2); 11 | } 12 | }; 13 | 14 | constexpr struct Add { 15 | template 16 | constexpr auto operator() (X x, Y y) const { 17 | return x + y; 18 | } 19 | } add{}; 20 | 21 | struct Int { 22 | int x; 23 | constexpr Int(int x) : x(x) { } 24 | constexpr int times(int y) const { return x * y; } 25 | }; 26 | 27 | int main() { 28 | using namespace fu::tpl; 29 | constexpr int one = 1; 30 | constexpr auto t1 = tuple(one, 2.5); 31 | 32 | using T1 = const std::tuple; 33 | 34 | static_assert(std::is_same::value, ""); 35 | constexpr auto t2 = map(Double(), t1); 36 | static_assert(std::is_same::value, "map changed the type"); 37 | 38 | static_assert(_0(t2) == 2, ""); 39 | static_assert(_1(t2) == 5.0, ""); 40 | static_assert(last(t2) == 5.0, ""); 41 | 42 | static_assert(map(fu::add, tuple(0,1), tuple(2,4)) == tuple(2,4,3,5), ""); 43 | 44 | static_assert(apply(add, t2) == 7.0, ""); 45 | static_assert(apply(add)(t2) == 7.0, ""); 46 | static_assert(foldl(add, 0, t2) == 7.0, ""); 47 | 48 | // TODO: Pull in the GCC_ and CLANG_STATIC_ASSERTs from 49 | // test/functional.cpp 50 | #ifdef __clang__ 51 | static_assert(apply(&Int::x, tuple(Int{1})) == 1, ""); 52 | static_assert(apply(&Int::times, tuple(Int{2}, 5)) == 10, ""); 53 | constexpr Int two{2}; 54 | static_assert(apply(&Int::times, tuple(&two, 5)) == 10, ""); 55 | #endif 56 | 57 | // These lines cause gcc 4.9 to error and suggest sending a bug report. 58 | // TODO: Create minimal test case. 59 | static_assert(foldl(add)(0, t2) == 7.0, ""); 60 | static_assert(fu::closure(foldr, add, 0)(t2) == 7.0, ""); 61 | 62 | static_assert(foldl(add, t2) == 7.0, ""); 63 | static_assert(foldr(add, 0, t2) == 7.0, ""); 64 | static_assert(foldr(add, t2) == 7.0, ""); 65 | static_assert(init(t2) == tuple(2), ""); 66 | static_assert(tail(t2) == tuple(5.0), ""); 67 | 68 | constexpr auto apTest = ap( tuple(fu::sub, fu::add) 69 | , tuple( 3, 1) 70 | , tuple( 2, -1) 71 | , tuple( 1, 1)); 72 | static_assert(apTest == tuple(0,1), ""); 73 | 74 | constexpr auto zipTest = zip_with(fu::add, tuple(0, 1), tuple(1, 2), tuple(2, 3)); 75 | static_assert(zipTest == tuple(3,6), ""); 76 | static_assert(zip(tuple(1)) == tuple(tuple(1)), ""); 77 | static_assert(zip(tuple(1,2), tuple(1,2)) == tuple(tuple(1,1), tuple(2,2)), ""); 78 | 79 | int x = 1; 80 | auto ref = tuple(std::ref(x), std::cref(x)); 81 | static_assert(std::is_same>::value, ""); 82 | 83 | //// std::forward_as_tuple works differently than tpl::forward_tuple by 84 | //// because forward_as_tuple does not work on constexpr literals/creates 85 | //// dangling references. 86 | //auto fwd1 = forward_tuple(5, x, std::move(t2), std::ref(x)); 87 | //auto fwd2 = std::forward_as_tuple(5, x, std::move(t2), std::ref(x)); 88 | //static_assert(std::is_same::value, ""); 89 | 90 | const int cx = 1; 91 | auto tie1 = tie(x, cx); 92 | auto tie2 = std::tie(x, cx); 93 | static_assert(std::is_same::value, ""); 94 | 95 | static_assert(rot(tuple(1,2,3)) == tuple(2,3,1), ""); 96 | static_assert(rrot(tuple(1,2,3)) == tuple(3,1,2), ""); 97 | } 98 | -------------------------------------------------------------------------------- /test/utility.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | int main() { 7 | static_assert(fu::add(1)(2) == 3, ""); 8 | static_assert(fu::add(1,2,3,4) == 10, ""); 9 | static_assert(fu::sub(10, 3, 2) == 5, ""); 10 | static_assert(fu::bit_or(1,2,4,8) == 0xf, ""); 11 | static_assert(fu::lshift(1,1) == 2, ""); 12 | static_assert(fu::eq(10, 10) == true, ""); 13 | static_assert(fu::less(5, 10) == true, ""); 14 | static_assert(fu::not_(false), ""); 15 | static_assert(fu::neg(-1) == 1, ""); 16 | static_assert(fu::and_(true, true, true), ""); 17 | static_assert(!fu::and_(true, false, true), ""); 18 | static_assert(fu::less(1,2,3,4,5), ""); 19 | static_assert(fu::less(1)(2,3,4,5), ""); 20 | static_assert(!fu::less(1,2,5,4,3), ""); 21 | static_assert(!fu::less(5,4,3,2,1), ""); 22 | static_assert(!fu::less(5)(4,3,2,1), ""); 23 | static_assert(fu::eq(1,1,1,1), ""); 24 | static_assert(!fu::eq(1,2,2,2), ""); 25 | 26 | static_assert(fu::max(0,4,2) == 4, ""); 27 | static_assert(fu::min(0,4,2) == 0, ""); 28 | 29 | { 30 | constexpr int xs[3] = {0,1,2}; 31 | static_assert(fu::size(xs) == 3, ""); 32 | static_assert(fu::index(1, xs) == 1, ""); 33 | static_assert(fu::front(xs) == 0, ""); 34 | static_assert(fu::back(xs) == 2, ""); 35 | } 36 | 37 | { 38 | // Test passes as long as this compiles. 39 | std::list xs; 40 | fu::push_back(1, xs); 41 | fu::push_front(1, xs); 42 | fu::ref(xs).get().front() = 2; 43 | } 44 | } 45 | --------------------------------------------------------------------------------