Subject: Re: [boost] [Fit] formal review starts today
From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2016-03-03 17:16:32
On 03/03/2016 04:43 AM, Vicente J. Botet Escriba wrote:
> Dear Boost community, sorry for the late announce
> The formal review of Paul Fultz II's Fit library starts today, 2nd March
> and ends on 13th March.
> We encourage your participation in this review. At a minimum, kindly state:
> - Whether you believe the library should be accepted into Boost
No, this library should not be accepted into Boost at this time.
> * Conditions for acceptance
> - Your name
> - Your knowledge of the problem domain.
> You are strongly encouraged to also provide additional information:
> - What is your evaluation of the library's:
> * Design
> * Implementation
> * Documentation
> * Tests
> * Usefulness
> - Did you attempt to use the library? If so:
> * Which compiler(s)
> * What was the experience? Any problems?
msvc-12 (failed), msvc-14, clang-3.3 (failed), gcc-4.8.
This is basically as expected.
> - How much effort did you put into your evaluation of the review?
~5 hours. I only finished half a dozen
files, but I've seen enough problems that
I feel justified in stopping now.
- Different functions in the library do not
have a consistent interface for similar
- always/always_ref vs. capture/capture_forward/capture_decay vs
partial w/ std::ref.
- The library reinvents (poorly) its own versions
of several components:
compressed_pair <-> boost::compressed_pair,
placeholders <-> lambda, bind, phoenix
Various configuration macros <-> Boost.Config
- The library attempts to take advantage of EBO,
but makes several mistakes. (See attached tests
which fail to compile)
- Some library functions require function objects
to have a const operator(), without any rhyme
or reason. e.g. apply requires Callable, but
apply_eval requires ConstCallable. It's especially
weird when a function (e.g. partial) requires
both ConstCallable, and MoveConstructible.
Even worse are the functions (e.g. capture)
which do not specify whether they require
const or not. You should not be requiring
const at all, for anything. See [func.bind.bind]
for an interface that handles const correctly.
- The implementation has various useless bits,
which serve no purpose that I can discern.
- Please be pedantic about ADL. If you aren't
it /will/ bite you.
Please add .gitattributes
- project fit
Project names must be globally unique, so it's
better to use /boost/fit (or leave out the
- [ test_all r ]
"r" is completely meaningless here.
- Actually, everything from rule test-all
down can be reduced to:
for local fileb in [ glob *.cpp ]
run $(fileb) ;
You don't need to create a special target
to group the tests, unless you want more
granularity than "run everything" or "run
specific tests by name."
57: #pragma warning(disable: 4579)
Library headers *must* disable warnings inside push/pop.
67: std::false_type is defined somewhere... (It looks like
you get <type_traits> via delegate.hpp)
74: Ditto for std::is_same
93: BOOST_FIT_UNARY_PERFECT_ID macro is undocumented and unused.
107: I have no idea why you made alias_value variadic.
The extra arguments are unused, their presence is
undocumented, and all calls from inside the library
(pack.hpp, combine.hpp, and detail/compressed_pair.hpp)
that use them, could just as well be written without.
117: struct alias_inherit
#if (defined(__GNUC__) && !defined (__clang__))
: std::conditional<(std::is_class<T>::value), T, alias<T>>::type
Really? alias_inherit is documented to use
inheritence, so it should be an error to use
it with a non-class type.
145: // Since we disable the error for 4579 on MSVC, which leaves the static
// member unitialized at runtime, it is, therefore, only safe to
// class on types that are empty with constructors that have no
// side effects.
Shouldn't it also be safe for types with trivial
default constructors, since then, zero-initialization
will do the right thing?
22: "The `always_ref` version will return a reference, and it
requires the value passed in to be an lvalue."
The first time I read this I thought it meant
that the function object created by always_ref
would be a reference, not that it produces
a reference when called. Consider rephrasing
the sentence to avoid the ambiguity.
39: "Requirements: T must be: * CopyConstructible"
This only applies to always, not always_ref, no?
61: #define BOOST_FIT_NO_CONSTEXPR_VOID 1
121: always_base has boost::fit::detail as an
124: constexpr detail::always_base<void> operator()() const
This overload is not documented. Should it be?
29: assert(compress(apply, f)(x, y, z) == f(x)(y)(z));
I don't think that compress should be part of the
semantics of apply. This belongs as an example.
boost/fit/apply.hpp(83): warning C4003: not enough actual parameters for
18: "Each [`eval`](eval.md) call is always ordered
It isn't clear to me why this guarantee is important.
Not to mention that the test case apply_eval.cpp
doesn't check it.
90: return eval_ordered<R>(f, pack_join(...), ...)
Watch out for ADL.
113: x(apply(f, BOOST_FIT_FORWARD(Ts)(xs)...), 0)
You need parens around the comma operator.
Also not tested.
29: /// template<class IntegralConstant>
/// constexpr auto arg(IntegralConstant);
/// template<std::size_t N, class... Ts>
/// constexpr auto arg_c(Ts&&...);
arg and argc should do the same thing. It looks
like arg creates a function object, while arg_c
evaluates it directly.
86: make_args_at(...)(..., make_perfect_ref(...)...)
The #include guard doesn't match the file name
22: "Also, if just a projection is given, then the projection will
be called for each of its arguments."
I don't see any good reason for this to be an
overload of by. It's essentially a for_each.
25: Note: All projections are always evaluated in order from left-to-right.
By only takes one projection.
This is a lot of code to implement what is essentially a one-liner:
135: #define BOOST_FIT_BY_VOID_RETURN BOOST_FIT_ALWAYS_VOID_RETURN
138: #define BOOST_FIT_BY_VOID_RETURN boost::fit::detail::swallow
You should really have a single BOOST_FIT_VOID_RETURN
and use it consistently everywhere.
32: // Capture lvalues by reference and rvalues by value.
The default should be to capture everything by value.
Capturing by reference is potentially dangerous, and
switching based on whether the argument is an lvalue
is even more so.
79: return always_ref(*this)(xs...);
Why not return *this?
The always_ref dance serves no purpose,
other than making the code hard to follow.
We really don't need another lambda library, let alone
a half-baked one.
Please reference the C++ standard when defining INVOKE.
(Unless of course your INVOKE is different, which it
had better not be)
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk