dobiasd / functionalplus Goto Github PK
View Code? Open in Web Editor NEWFunctional Programming Library for C++. Write concise and readable C++ code.
Home Page: http://www.editgym.com/fplus-api-search/
License: Boost Software License 1.0
Functional Programming Library for C++. Write concise and readable C++ code.
Home Page: http://www.editgym.com/fplus-api-search/
License: Boost Software License 1.0
Some functions (like fold_left
) do already have a short textual description of what they do.
For some functions this perhaps is not really needed, since the type signature or the example are sufficient.
But some functions could need a description.
enumerate
, generate
and is_sorted_on
are just some that come to my mind right now.
Is anybody interested in browsing trough parts of the code and adding documentation where sensible? I think this would be very helpful, especially for helping users new to the library.
Hi =) adjacent_pairs advances iterator it2 beyond xs.end(), which crashes in debug mode on MSVC 2017 (V 15.2).
#include "fplus/fplus.hpp"
int main() {
std::vector<int> vec = {1, 2, 4, 5 };
auto x = fplus::adjacent_pairs(vec);
}
Proposal for a fix: PR
I have seen your math functions like min_2
, min_3
etc. and I wondered why not use variadic templates to remove duplicated code?
Inspired by http://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html I have made some benchmarks and it came out that variadic versions are faster on -g
and on pair with optimizations with your versions.
#include <vector>
#include <functional>
#include <utility>
#include <type_traits>
#include <iostream>
#include <chrono>
template <typename TimeT = std::chrono::milliseconds>
struct measure
{
template <typename F, typename... Args>
static typename TimeT::rep execution(F func, Args&&... args)
{
auto start = std::chrono::system_clock::now();
// Now call the function with all the parameters you need.
func(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast<TimeT>(std::chrono::system_clock::now() - start);
return duration.count();
}
};
template <typename U, typename... V>
auto min(const U& u, const V&... v)
{
using rettype = typename std::common_type_t<U, V...>;
rettype result = static_cast<rettype>(u);
(void)std::initializer_list<bool>{((v < result) ? (result = static_cast<rettype>(v), false) : false)...};
return result;
}
// API search type: min_2 : (a, a) -> a
// Minimum of two values.
template <typename X>
const X& min_2(const X& a, const X& b)
{
return std::min(a, b);
}
// API search type: min_3 : (a, a, a) -> a
// Minimum of three values.
template <typename X>
const X& min_3(const X& a, const X& b, const X& c)
{
return min_2(min_2(a, b), c);
}
// API search type: min_4 : (a, a, a, a) -> a
// Minimum of four values.
template <typename X>
const X& min_4(const X& a, const X& b, const X& c, const X& d)
{
return min_2(min_3(a, b, c), d);
}
// API search type: min_5 : (a, a, a, a, a) -> a
// Minimum of five values.
template <typename X>
const X& min_5(const X& a, const X& b, const X& c, const X& d, const X& e)
{
return min_3(min_3(a, b, c), d, e);
}
int main()
{
constexpr int COUNT = 50000000;
std::vector<int> v, v1;
v.reserve(COUNT);
v1.reserve(COUNT);
std::cout << "variadic version: " << '\n';
std::cout << measure<std::chrono::milliseconds>::execution([&v]()
{
for (int i = -COUNT; i < COUNT; ++i)
v.emplace_back(min(1, -14, 3, -1, i));
}) << "ms\n";
std::cout << "functional plus version: " << '\n';
std::cout << measure<std::chrono::milliseconds>::execution([&v1]()
{
for (int i = -COUNT; i < COUNT; ++i)
v1.emplace_back(min_5(1, -14, 3, -1, i));
}) << "ms\n";
}
It both depends on data and optimizations but I have received the following results (not the measure of all times but hey... :) ):
g++ -g -Wall -Wpedantic -Wextra --std=c++14 main.cpp && ./a.out
variadic version:
2240ms
functional plus version:
4335ms
g++ -O1 -Wall -Wpedantic -Wextra --std=c++14 main.cpp && ./a.out
variadic version:
333ms
functional plus version:
307ms
g++ -O2 -Wall -Wpedantic -Wextra --std=c++14 main.cpp && ./a.out
variadic version:
326ms
functional plus version:
347ms
g++ -O3 -Wall -Wpedantic -Wextra --std=c++14 main.cpp && ./a.out
311ms
functional plus version:
321ms
The variadic version is only worse on O1
no idea why...
If anyone is happy with this type of approach I will be more than happy to submit a pull request for some of the functions in numeric which can be easily handled with variadic templates/initializer_list.
The Awesome repo needs an awesome description.
So, I have two feature requests:
After all, it's just a suggestion, so feel free to close it if you think it's not necessary.
FunctionlPlus
is trying to offer. Functional Programming Library for C++FunctionalPlus
.FunctionalPlus
, you have to search "Concise C++ Code".By Foogle I mean the API search website http://www.editgym.com/fplus-api-search/
It is truly awesome and super helpful, but it's currently hidden deeply in the README.
In order to go to the Foogle from this GitHub,
you have to go through 3 steps.
The following code does not compile any more. I guess it has something to do with PR #87.
const std::function<int(int)> double_int = [](int x) -> int
{
return 2 * x;
};
typedef std::pair<int, int> IntPair;
fplus::transform_pair(double_int, double_int, IntPair({2,3}));
@theodelrieu, could you please have a look at it?
you use many symbols like "std ::" , adding one line that using namespace std will be ok!
When compiling with C++14 the build fails on some Clang versions.
SO says one should use -stdlib=libc++
. Do I simply have to remove libc++-dev
in .travis.yml
or replace it with something?
@offa It would be totally cool if you could help me out again. :D
The API-search is neat, but not very useful, because a new user is in a situation "i don't know what I'm looking for". Instead, the user must have a facility to explore the library. I'd recommend annotating public API with doxy-comments and generate documentation from that.
#include "fplus/fplus.hpp"
#include <unordered_map>
int main(void) {
std::unordered_map<int, int> foo = fplus::map_union(std::unordered_map<int, int>(), std::unordered_map<int, int>());
return 0;
}
Compile error:
error: could not convert โfplus::map_union_with(F, const MapIn&, const MapIn&) [with F = fplus::map_union(const MapType&, const MapType&) [with MapType = std::unordered_map<int, int>]::<lambda(const Value&, const Value&)>; MapIn = std::unordered_map<int, int>]((* & dict1), (* & dict2))โ from โstd::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >โ to โstd::unordered_map<int, int>โ
return map_union_with(get_first, dict1, dict2);
Code compiles.
I'm not sure why this happens, but it seems to have to do with the SameMapTypeNewTypes
template
FunctionalPlus/include/fplus/maps.hpp
Line 75 in 4b544e2
I'm using Arch Linux with GCC 8.1.0 and fplus
0.2.1 (the latest release from conan
).
I'm happy to help debug further! If I figure out what causes this, I'll submit a PR, but I'm not sure what it is yet...
https://travis-ci.org/Dobiasd/FunctionalPlus/jobs/389830560
https://api.travis-ci.org/v3/job/389830560/log.txt
It was working fine 6 days ago when I made the last commit. I only broke when I just triggered a rebuild.
Sorry about that. The tests were all green on my machine.
GCC 6 now warns differently about unused but set variables which breaks the travis builds.
@pmalek, since you initially contributed this piece I was wondering if you have an idea on how to fix it?
Hi,
I have seen that in order to use this library you have to generate some files with CMake.
In my case they were two files: curry_instances.autogenerated_defines
and fwd_instances.autogenerated_defines
.
I would like to know if those generated files may be different depending on the operating system, compiler used or not.
Thank you
Here's an example where keep_if_with_idx()
is Ok, but drop_if_with_idx()
fails to compile:
static bool accept_with_index(size_t index, int value)
{
return ( index % 2 == 0 ) && ( value >= 10 );
}
const std::vector<int> v = { 1, 20, 30, 4, 50, 60, 7 };
auto result = fplus::keep_if_with_idx(accept_with_index, v);
// Result: { 30, 50 }
const std::vector<int> v = { 1, 20, 30, 4, 50, 60, 7 };
auto result = fplus::drop_if_with_idx(accept_with_index, v);
// Expected Result: { 1, 20, 4, 60, 7 } --- Compilation fails
Compilation fails due a failed static assertions ("Wrong arity").
Is there a mistake in the example / usage of drop_if_with_idx()
?
Thanks for the awesome library first of all.
During compilation, I get the following error messages.
Please let me know if you need more information.
Thank you.
/usr/local/include/fplus/composition.hpp:385:29: error: unknown type name 'uint'; did you mean 'int'?
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:668:78: error: implicit instantiation of
undefined template 'std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >'
_LIBCPP_INLINE_VISIBILITY back_insert_iterator& operator=(const typename _Container::value_type& __value_)
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:671:72: error: implicit instantiation of
undefined template 'std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >'
_LIBCPP_INLINE_VISIBILITY back_insert_iterator& operator=(typename _Container::value_type&& __value_)
git clone https://github.com/Dobiasd/FunctionalPlus
cd FunctionalPlus
mkdir build
cd build
cmake ..
sudo make install
#include <fplus/fplus.hpp>
#include <iostream>
int main() {
std::list<std::string> things = {"same old", "same old"};
if (fplus::all_the_same(things))
std::cout << "All things being equal." << std::endl;
}
clang++ -std=c++14 main.cpp
In file included from main.cpp:1:
In file included from /usr/local/include/fplus/fplus.hpp:10:
/usr/local/include/fplus/composition.hpp:385:29: error: unknown type name 'uint'; did you mean 'int'?
return [f, storage](uint x)
^
In file included from main.cpp:1:
In file included from /usr/local/include/fplus/fplus.hpp:9:
In file included from /usr/local/include/fplus/compare.hpp:9:
In file included from /usr/local/include/fplus/function_traits.hpp:25:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:477:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:606:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:668:78: error: implicit instantiation of undefined template 'std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >'
_LIBCPP_INLINE_VISIBILITY back_insert_iterator& operator=(const typename _Container::value_type& __value_)
^
/usr/local/include/fplus/container_common.hpp:111:16: note: in instantiation of template class 'std::__1::back_insert_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >' requested here
return std::back_inserter(ys);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iosfwd:193:33: note: template is declared here
class _LIBCPP_TYPE_VIS_ONLY basic_string;
^
In file included from main.cpp:1:
In file included from /usr/local/include/fplus/fplus.hpp:9:
In file included from /usr/local/include/fplus/compare.hpp:9:
In file included from /usr/local/include/fplus/function_traits.hpp:25:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:477:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:606:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:671:72: error: implicit instantiation of undefined template 'std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >'
_LIBCPP_INLINE_VISIBILITY back_insert_iterator& operator=(typename _Container::value_type&& __value_)
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iosfwd:193:33: note: template is declared here
class _LIBCPP_TYPE_VIS_ONLY basic_string;
^
3 errors generated.
clang++ --version
Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin16.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
The following code does not compile.
#include <fplus/fplus.hpp>
int main()
{
std::array<int, 3> xs = {0,1,2};
fplus::transform(fplus::square<int>, xs);
}
fplus::transform
(and some other functions) could deduce the length of the returned sequence at compile time and thus support std::array
in theory.
I'm thinking about adding this functionality, but I'm still in the process of gathering ideas around this topic. Any thoughts? :)
I'm glad to just find this great library! After some exploration, I found that the function name unique
is a little misleading. Here unique([1,2,2,3,2]) == [1,2,3,2]
, though I would expect unique([1,2,2,3,2]) == [1,2,3]
as the behavior of numpy.unique
in python.
Despite this name is followed from std::unique
, which remove consecutive duplicates in range, I personally think using a slight different name (e.g. unique_consecutime
) here would be beneficial for avoiding misuse. However it's just personal thought. Please feel free to take it or leave it :)
There are a lot of duplicate function_traits_asserts
specialization.
We need to group them.
Also, since C++14 constexpr functions can return void
, which would remove the cast to void on each call to detail::trigger_static_asserts
Renaming detail
to internal
is the first step. There are a few internal methods that are superseded by those in detail
.
There's also some static_assert
that do not use the detail::trigger_static_assert
facility.
i wanted to try frugally-deep through the conan package manager,
however i'm not able to compile, getting a lot of errors like:
~.conan/data/functionalplus/v0.2-p1/dobiasd/stable/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/fplus/internal/meta.hpp:28:41: error: no template named 'remove_reference_t' in namespace 'std'; did you mean 'remove_reference'?
using uncvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
~~~~~^~~~~~~~~~~~~~~~~~
remove_reference
not sure if this is a bug in FunctionalPlus or i'm doing something wrong.
conanfile.txt:
[requires]
frugally-deep/v0.3.3-p0@dobiasd/stable
[generators]
cmake
Hi,
I have just started playing with FunctionalPlus using Microsoft Visual Studio 2015 and have this stupid building error that I've been fighting with all day:
1> fplus_tutorial.cpp 1>.......\include\fplus\container_common.hpp(1166): error C2783: 'ContainerOut fplus::transform(F,ContainerIn &&)': could not deduce template argument for 'ContainerOut'
is that something known? I've been googling a lot and checking everything on my side, but it just look like that code can't be really handled by VS15.
Many thanks in advance for help.
BR,
Konrad
https://travis-ci.org/Dobiasd/FunctionalPlus/jobs/166689095
@offa: Any idea what is wrong?
https://api.travis-ci.org/jobs/285710163/log.txt?deansi=true
Err:3 http://apt.llvm.org/trusty llvm-toolchain-trusty-5.0/main amd64 libllvm5.0 amd64 1:5.0~svn315198-1~exp1
404 Not Found
I hope this is caused by a mistake of the llvm people and we just have to wait. ;)
Hi, I think there is a problem with the implementation of constructor_as_function
.
return T{args...};
IIRC, make_unique
uses parenthesis instead of curly braces, I believe we should do the same.
Here is the current behavior:
struct Test {
Test(int, int){} // 1
Test(std::initializer_list<int>){} // 2
};
fplus::constructor_as_function<Test>(4, 2); // will call constructor 2
In order to support every Callable
object, we should use invoke
instead of:
// will not work with pointers/references to member functions/data members
return f(x, y);
When I compile followinf code from usage-examples:
#include <fplus/fplus.hpp>
#include <iostream>
int main()
{
using namespace fplus;
const auto result_fplus = fwd::apply(
numbers(0, 15000000)
, fwd::transform(times_3)
, fwd::drop_if(is_odd)
, fwd::transform(as_string_length)
, fwd::sum());
}
get error:
error: 'fwd' has not been declared
const auto result_fplus = fwd::apply(
...
is it possible rewrite it without updated of the compiler?
Hello, awesome lib! I was playing a bit with it and I have the following example code that compiles and runs as expected:
#include <iostream>
#include <vector>
#include <string>
#include <fplus/fplus.hpp>
using namespace std::string_literals;
int main()
{
auto const l1 = {"Hello"s, "World"s};
auto const l2 = {","s, "!"s};
auto const v = fplus::zip_with(std::plus<std::string>{}, l1, l2);
std::cout << fplus::show_cont_with(" ", v) << std::endl;
}
However, when I use std::plus<>{}
, I get a compiler error (VS2017 on Win10 x64):
include\fplus/function_traits.hpp(79): error C3556: 'std::plus::operator ()': incorrect argument to 'decltype'
include\fplus/pairs.hpp(42): note: see reference to class template instantiation 'utils::function_traits' being compiled
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.10.25017\include\chrono(227): note: see reference to class template instantiation 'std::chrono::duration<__int64,std::nano>' being compiled
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.10.25017\include\chrono(1002): note: see reference to class template instantiation 'std::chrono::time_pointstd::chrono::steady_clock,std::chrono::steady_clock::duration' being compiled
include\fplus/function_traits.hpp(80): error C2955: 'utils::function_traits': use of class template requires template argument list
include\fplus/function_traits.hpp(79): note: see declaration of 'utils::function_traits'
include\fplus/pairs.hpp(42): error C2039: 'arity': is not a member of 'utils::function_traits'
include\fplus/pairs.hpp(42): note: see declaration of 'utils::function_traits'
include\fplus/pairs.hpp(42): error C2065: 'arity': undeclared identifier
include\fplus/pairs.hpp(45): error C2903: 'arg': symbol is neither a class template nor a function template
include\fplus/pairs.hpp(45): error C2039: 'type': is not a member of 'utils::function_traits'
include\fplus/pairs.hpp(42): note: see declaration of 'utils::function_traits'
include\fplus/pairs.hpp(45): warning C4091: 'typedef ': ignored on left of 'int' when no variable is declared
include\fplus/pairs.hpp(45): error C2144: syntax error: 'unknown-type' should be preceded by ';'
include\fplus/pairs.hpp(46): error C2903: 'arg': symbol is neither a class template nor a function template
include\fplus/pairs.hpp(46): error C2039: 'type': is not a member of 'utils::function_traits'
include\fplus/pairs.hpp(42): note: see declaration of 'utils::function_traits'
include\fplus/pairs.hpp(46): warning C4091: 'typedef ': ignored on left of 'int' when no variable is declared
include\fplus/pairs.hpp(46): error C2144: syntax error: 'unknown-type' should be preceded by ';'
include\fplus/pairs.hpp(49): error C2923: 'std::is_convertible': 'FIn0' is not a valid template type argument for parameter '_To'
include\fplus/pairs.hpp(45): note: see declaration of 'FIn0'
include\fplus/pairs.hpp(49): error C2955: 'std::is_convertible': use of class template requires template argument list
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.10.25017\include\type_traits(416): note: see declaration of 'std::is_convertible'
include\fplus/pairs.hpp(50): error C2057: expected constant expression
include\fplus/pairs.hpp(51): error C2923: 'std::is_convertible': 'FIn1' is not a valid template type argument for parameter '_To'
include\fplus/pairs.hpp(46): note: see declaration of 'FIn1'
include\fplus/pairs.hpp(51): error C2955: 'std::is_convertible': use of class template requires template argument list
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.10.25017\include\type_traits(416): note: see declaration of 'std::is_convertible'
include\fplus/pairs.hpp(52): error C2057: expected constant expression
I never really used transparent function objects, so it might very well be a silly mistake.
Hi @Dobiasd !
I forgot to tell you to change the issue tracker link in Bintray https://bintray.com/dobiasd/public-conan/functionalplus%3Adobiasd as it is pointing to the old conan-community repo.
And just to let you know, I have also deleted my packages of FuntionalPlus from conan-community and accepted your inclusion request to conan-center :)
Contribution guidelines (see #58; starting at #58 (comment)).
The following code compiled some time ago but does not compile any more with the latest version.
int main()
{
fplus::keep_if(
fplus::compose(
fplus::square<int>,
fplus::is_greater_or_equal_than(3)),
std::vector<int>());
}
@theodelrieu, could you please have a look at it?
Very nice library!
I find some similarities with ranges v3 from Eric Niebler.
Strong points toward yours are being more portable and faster to compile.
Anyway i'd like some more features that are missing, such as infinite ranges and generators.
For example i would really like to do:
std::vector<std::string> vec = {"first", "second", "third"};
for(auto & el : fplus::zip(vec, fplus::generate_range(1))
std::cout << el.first << " " << el. second << '\n';
// outputs:
// first 1
// second 2
// third 3
Thank you for creating FunctionalPlus!
I was interested in it, and reading code and made memo/summary for my use, then found small error in API doc for scan_right_1
Probably, result [1,3,6] is remain of copy&paste from scan_left_1, isn't it?
I get == [6,5,3] as expected.
// API search type: scan_right_1 : (((a, a) -> a), [a]) -> [a]
// scan_right_1((+), [1, 2, 3]) == [1, 3, 6]
// Takes the last two items of the list and applies the function,
// then it takes the third item from the end and the result, and so on.
// It returns the list of inntermediate and final results.
template <typename F, typename ContainerIn,
typename Acc = typename ContainerIn::value_type,
typename ContainerOut = typename same_cont_new_t<ContainerIn, Acc>::type>
ContainerOut scan_right_1(F f, const ContainerIn& xs)
{
return reverse(scan_left_1(flip(f), reverse(xs)));
Let's say we have the following three functions
const auto times_3 = [](int i){return 3 * i;};
const auto is_odd = [](int i){return i % 2 == 0;};
const auto as_string_length = [](int i){return std::to_string(i).size();};
and want to transform/filter a sequence of numbers with them and sum up the result.
Up to now, we would have to write the following:
const auto result_old_style =
sum(
transform(as_string_length,
drop_if(is_odd,
transform(times_3,
numbers(0, 15000000)))));
I do not like the fact that we have to read backwards to perceive the flow of data. I think something in the style of unix pipes would be nicer.
With some additional code in the library we could do the following thing:
const auto result_new_style = fwd::apply(
numbers(0, 15000000)
, fwd::transform(times_3)
, fwd::drop_if(is_odd)
, fwd::transform(as_string_length)
, fwd::sum());
Furthermore it would solve the problem of the almost unusable-ugly function composition, going from this:
const auto function_chain_old_style = compose(
bind_1st_of_2(transform<decltype(times_3), std::vector<int>>, times_3),
bind_1st_of_2(drop_if<decltype(is_odd), std::vector<int>>, is_odd),
bind_1st_of_2(transform<decltype(as_string_length), std::vector<int>>, as_string_length),
sum<std::vector<std::size_t>>);
to that:
const auto function_chain_new_style = fwd::compose(
fwd::transform(times_3),
fwd::drop_if(is_odd),
fwd::transform(as_string_length),
fwd::sum());
Transforming over the inner containers in nested ones
typedef std::vector<int> ints;
std::vector<ints> nested_ints;
would thus also become simpler, going from:
const auto nested_transformed_old_style = transform(
bind_1st_of_2(transform<decltype(times_3), ints>, times_3),
nested_ints);
to
const auto nested_transformed_new_style = fwd::apply(
nested_ints
, fwd::transform(fwd::transform(times_3)));
It would increase the compiler requirements from C++11 to C++14. But this functionality could be provided in a separate header, so one could still use the old style when only C++11 is available.
Here is the full code of the example above. (The code up to line 104 would live in a file perhaps called fwd.hpp
.)
What do you think about this idea?
It seems that at least some functions don't work with initializer_list. Is it possible to make make them work? For example, this doesn't compile:
void foo(initializer_list<int> const& x) {
fplus::transform([](int i){ return i + 1;}, x);
}
result_of
is deprecated in C++17, and has a lot of caveats.
We should use invoke_result_t
instead. Note that invoke_result_t
returns a T&&
(as std::declval
).
It is important to be careful when storing it into a template type:
using Result = detail::invoke_result_t<F>;
// std::vector<T&&> will fail to compile
// using ContainerOut = std::vector<Result>;
using ContainerOut = std::vector<std::decay_t<Result>>;
The unit test of transform_parallelly
fails with an exception on GCC 4.8.
It first occured with the switch from gtest to doctest, but this of course could just be a coincidence.
@onqtam: Do you think there could be a causal relationship?
e.g. let's look at keep_if
template <typename Pred, typename Container>
Container keep_if(Pred pred, const Container& xs)
{
internal::check_unary_predicate_for_container<Pred, Container>();
Container result;
auto it = internal::get_back_inserter<Container>(result);
std::copy_if(std::begin(xs), std::end(xs), it, pred);
return result;
}
It looks to me it would be better implemented as
template <typename Pred, typename Container>
Container keep_if(Pred pred, Container xs)
{
// erase-remove elements from xs, per predicate
return xs;
}
This way if the container is passed as rvalue, as would most likely be the case, then we have no copy-overhead: xs
is passed by-move, edited in-place, and returned by-move.
This is not just applicable to keep_if
, but the family of functions where input and output types are the same, and the input is likely to be passed as rvalue, and the type is potentially heavy to copy.
The performance of split_by
can be improved by using a more efficient tokenizing algorithm as written here (not by me) http://tcbrindle.github.io/posts/a-quicker-study-on-tokenising. I've included an inlined version of the algorithm in the test program below. Here are the execution times of splitting a short string 5,000,000 times, compiled with -O3
on clang 3.7
on x86_64 Linux 4.3:
Time taken: 19.8554 (fplus::split_words)
Time taken: 2.02921 (improved version)
#include <algorithm>
#include <chrono>
#include <iostream>
#include <vector>
#include "fplus.h"
// Slightly modified code based on http://tcbrindle.github.io/posts/a-quicker-study-on-tokenising/
template <typename Str, typename Container = std::vector<std::string>>
Container tokenize(const Str &str, const char delim = ' ') {
Container result;
auto inserter = std::inserter(result, std::begin(result));
auto token_start = std::begin(str);
while (token_start != std::end(str) && *token_start) {
const auto token_end = std::find(token_start, std::end(str), delim);
if (token_start != token_end) {
*inserter = { token_start, token_end };
}
if (token_end == std::end(str)) break;
token_start = std::next(token_end);
}
return result;
}
int main() {
using namespace std::chrono;
auto words = std::string{ "a bb ccc dddd eeeee ffffff ggggggg hhhhhhhhh" };
const auto start_time = high_resolution_clock::now();
for (auto _ = 0; _ < 5000000; _++) {
// auto tokens = fplus::split_words(words);
auto tokens = tokenize(words);
}
const auto elapsed = high_resolution_clock::now() - start_time;
std::cout << "Time taken: " << duration_cast<duration<double>>(elapsed).count() << std::endl;
}
This works out of the box - great!
auto cpp11_odd = [](int i) { return i % 2 != 0; };
fplus::keep_if(cpp11_odd,
generate_range<std::vector<int>>(10, 1000));
This fails with C3556 on VS2015:
auto cpp14_odd = [](auto i) { return i % 2 != 0; };
fplus::keep_if(cpp14_odd,
generate_range<std::vector<int>>(10, 1000));
If that would work out of the box... great!
What about adding unit tests to the project?
I have written some example tests, see here: https://github.com/offa/FunctionalPlus/tree/ut_gtest (test
directory)
The tests are off per default at the moment โ so no dependencies are needed. Only Building / running the unit tests requires gtest. To do so:
cd build
cmake -DUNITTEST=ON ..
make unittest
You can check the CI log's for the branch to see how it works.
Btw. I stumbled over #20 by writing an UT for it ๐
Is internal_helper_carthesian_prodocut_n_idx()
(File: generate.h
) intended as public API? The internal_helper
part indicates it's not. If that's the case, maybe it can be moved into an internal
namespace?
As noted in #60, there's a signedness issue on clang:
[โฆ]/include/fplus/search.hpp:145:27: error:
implicit conversion changes signedness: 'std::size_t'
(aka 'unsigned long') to 'typename iterator_traits<__wrap_iter<const char
*> >::difference_type' (aka 'long') [-Werror,-Wsign-conversion]
std::advance(itInEnd, size_of_cont(token));
~~~ ^~~~~~~~~~~~~~~~~~~
https://travis-ci.org/offa/FunctionalPlus/jobs/184998962#L346
Currently our travis ci configuration uses 4 different g++ versions for the tests. I think it would be cool if it could use at least one version of clang additionally.
@offa: Is this something you could have a look at?
Have you tried to compile this code for Arduino like hardware?
I'm failing on my first attempt . Looks like avr stl library is missing something that you need.
I'm a scala developer, and every time I write for(int i=0..... something small is dying in me.
To consolidate the problem I open this new issue.
Situation: We would like to use recent versions of libc++ on Travis (Ubuntu Trusty).
The default version provided with sudo apt install libc++-dev
is svn199600
(_LIBCPP_VERSION
is defined as 1101
).
My attempts to learn about how to do it on SO (1, 2) were not successful yet.
E.g. composition.h contains an include
#include "function_traits.h"
Which means that, when included from anywhere, the client project needs both fplus_installation/includes
and fplus_installation/includes/fplus
on it's include path.
This makes adoption harder, and makes collisions with other libraries more probable.
Public headers should assume as little as possible from client code: they should probably include their neighbour headers as if specified by client code:
#include "fplus/function_traits.h"
There's a lot of defaulted template arguments in the library, they are primarily used to deduce function return types.
The problem is that one can accidentally replace them, and they make the search API less clear.
Most can be removed thanks to C++14's return type deduction.
However, it seems that a few are designed to be replaced? If so, making a list would greatly help to remove the unneeded ones.
Consider adding some Github topics: https://github.com/blog/2309-introducing-topics
Since #112 I encounter a new effect when changing the library and running the unittests.
My usual workflow looks like this:
initially:
rm -r build
mkdir build
cd build
cmake -DUNITTEST=ON ..
make unittest
later:
# change something in one of the header files
make unittest
With the old versions of CMakeLists.txt
and test/CMakeLists.txt
it still works, but with the new ones the second make unittest
does not rebuild the tests. It just ignores the changes in the header files. I have to run sudo make install
to rebuild the tests.
@offa and @theodelrieu Is this intended and my workflow is just strange? Or do you think it would be possible and helpful to change the cmake configuration to support this again?
With closing issue #73 we introduced the possibility of container re-usage for many functions like keep_if
, get_segment
and sort
.
In the general case the output container of fplus::transform
does not have the same type as the input container, but in case the given function is of type a -> a
this can happen. For these cases I would like the function to reuse the input container if it is an r-value.
But currently I am banging my head against the wall trying to implement it, i.e. the unit tests won't compile. Would anybody of you guys like to help me out here?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.