Boost logo

Boost :

Subject: Re: [boost] [Boost-users] [Fit] formal review starts today
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2016-03-09 03:33:25

Le 09/03/2016 08:34, paul Fultz a écrit :
>> On Tuesday, March 8, 2016 4:58 PM, Vladimir Batov <Vladimir.Batov_at_[hidden]> wrote:
>>> On 03/09/2016 08:43 AM, paul Fultz wrote:
>>> ...I guess people have different ways of learning a library. I wonder
>>> what is needed to be explained better in a initial overview of the
>>> library.
>> Please do not take it a a criticism of any kind. That's just an
>> impression I've got. I could be way off the mark.. I often am... I read
>> the docs... twice... well, I tried... :-) I was not able to find an
>> answer to a nagging question -- why I might need the library? What does
>> it do that the standard C++ does not? To me the Quick Start felt more
>> like Quick Start to Confusion. :-) Literally I felt like the deeper into
>> the docs I was going the more alarmed I was.
> Maybe I need a little more explanation of components in the Quick Start Guide.
> Perhaps, also, a comparison of writing some of the examples without using the
> library. The recursive print example is simple, but the technique could apply
> anytime you needed generic serialization of data. I have written code like
> that without using this library, so I can see benefit of using it for this
> particular case. So perhaps, writing a comparison without the library might
> make that clearer.
Yes, please, show us some examples.
>> "We can make it (the functor) behave like a regular function"
> I assume by functor, you mean function, as the library doesn't support
> functors and is beyond the scope of this library.
Some parts of the C++ community name a function object a functor. It ha
nothing to be with the applicative, functor, monad in type classes.
>> In all honesty I don't think I ever had such a need. Usually IMO the
>> conversion is the other way around -- a reg. function into a functor. I
>> think it happens pretty much automatically.
> Well, the library provides help for that as well, because currently in C++ you
> can't pass generic functions to other functions, like this:
> std::accumulate(v.begin(), v.end(), 0, std::max); // Compile error
> However, BOOST_FIT_LIFT will let you do that:
> std::accumulate(v.begin(), v.end(), 0, BOOST_FIT_LIFT(std::max));
Maybe it is worth noting that these is a C++17/20 proposal to manage
with this case.

[1] p0119r1 - Overload sets as function arguments
>> "if we construct the class as a global variable"
>> Should I be excited about it? Globals are often a royal pain. Do I want
>> to construct it a a global variable? |
> Whats the pain about these Global variables? They are const, can be
> namespaced, and work just like free functions. However, they do have several
> advantages.
> First, by making them objects we can turn functions into first-class citizens.
Note that we do that now with function objects and lambdas.
This is a major argument that belongs to the motivation. You library
works with and build high order functions (HOF).
> This allows for functions to be easily passed around to other functions. In
> fact, almost all the functions in this library are declared this way. This
> make the functions(and adaptors) easily composable. For example, if you wanted
> to write a function to do for_each over a tuple, you can easily compose the
> `unpack` adaptor with the `by` adaptor:
> BOOST_FIT_STATIC_FUNCTION(for_each_tuple) = compose(unpack, by);
As others have noted you should separate the the high order function
definition from the possibility to define them globally.

auto for_each_tuple = compose(unpack, by);

An alternative to use global function is to define function factories

auto for_each_tuple() { return compose(unpack, by); }

However this needs an extra level of parenthesis

     for_each_tuple()([](auto x) { std::cout << x << std::endl; })(t);

Maybe it is worth talking of what is behind this style (point-free style
or tacit programming)

> So the `by` adaptor will call a function for each parameter, and the `unpack`
> adaptor will unpack the elements of the tuple to each parameter. By composing
> them together we can call a function for each element in a tuple. So, you can
> write a function to print each value in a tuple, like this:
> auto t = std::make_tuple(1, 2);
> for_each_tuple([](auto x) { std::cout << x << std::endl; })(t);
> So by passing functions to other functions, we can easily write some very
> sophisticated functions without having to resort to metaprogramming. Just as a
> comparison, here is how you could write for_each_tuple without this library:
> namespace detail
> {
> template<typename T, typename F, int... Is>
> void for_each(T&& t, F f, seq<Is...>)
> {
> auto l = { (f(std::get<Is>(t)), 0)... };
> }
> }
> template<typename... Ts, typename F>
> void for_each_tuple(std::tuple<Ts...> const& t, F f)
> {
> detail::for_each(t, f, detail::gen_seq<sizeof...(Ts)>());
> }
> It is more code, with more C++ cleverness going on.
This is s a good example, and of course will need to compare
performances at compile time and run-time.
> Secondly, by making them objects, it allows us to decorate or enhance
> functions. C++ doesn't have support for python-like decorators, so if you want
> to enhance a function, it will need to be a function object through and
> through.
>> |
>> A macro? For C++14? Really? And given you mention it about 20 times just
>> in the Quick Start section it seems quite pivotal for the library...
>> Should I be excited about it? Macros are often a royal pain... Wrapping
>> functors and lambdas in a macro?.. seems like I need quite a bit of
I agree with that and I suggested Paul from the beginning to move this
to an Advanced section.
The fact that the library uses it to define the global function is of no
concern for the users.
Only if a user wants to define a global HOF, then those macros can help
>> convincing I might want that.
> C++17 will be adding support for inline variables, so in the future this macro
> will be unnecessary. For now, it will take care of statically initializing the
> function and avoiding possible ODR issues.
Any reference to something that will appear in the future standard and
describe that your macros are a way to emulate a future feature would be
much appreciated. Could you elaborate how inline variables are related?
> Furthermore, dealing with inconsistencies and bugs across multiple platforms
> is a real pain. For example, MSVC has lots of bugs with constexpr that can
> affect statically initializing the function object.
Not everyone needs constexpr. So maybe this use case should be moved to
the advanced usage.
AN alternative that you could or not have is to require better
compilers. as e.g. Boost.Hana does.
> So this macro provides
> workarounds so the it can be initialized statically. I don't see the macro as
> problematic, and without the macro is more problematic.
Only if the user needs to declare them globally.
> However, you can write it without the macro like this:
> template<class T>
> struct static_const_storage
> {
> static constexpr T value = T();
> };
> template<class T>
> constexpr T static_const_storage<T>::value;
> template<class T>
> constexpr const T& static_const_var(const T&)
> {
> return detail::static_const_storage<T>::value;
> }
I believe that there are a lot of users that would prefer to write the
code that follows once a library provide the previous one
> static constexpr auto&& for_each_tuple = static_const_var(compose(unpack, by));
> This of course, will only work on a fairly compliant C++11 compilers. It
> doesn't work on MSVC.
Does it works with a MSVC C++14?
> Also, it won't work when using lambdas. No doubt, C++14
> gets rid of a lot of macro usages, but C++14 is still lacking in many areas,
> so macros are still needed to fill in these gaps.
You need to let the user choose. If the user is using a C++14 compliant
compiler, would it need the macros? If not it is not worth presenting it
as something capital. This belong to the workaround and emulations and I
could appreciate having those macros, but having the equivalent C++ code
is better.
>> For a library to be accepted the user has to understand the
>> purpose/value of it and to get excited about it. I did not get it. In
>> fact, I got the opposite... but I a not the brightest "bulb" in the
>> pack...
> Thanks for the feedback, I probably should discuss more of the advantages of
> using function objects in the documentation to make it clear to more people.
Yes I've said not all the people knows about High order functional
programming. You should explain and make reference to external resources
as needed.


Boost list run by bdawes at, gregod at, cpdaniel at, john at