On 3/11/2016 12:10 AM, Paul Fultz II wrote:
We can define functions by simply composing other functions together and
we
don't need to write awkward syntax nor template boilerplate for it. For example, we can simply write `make_tuple` like this:
BOOST_FIT_STATIC_FUNCTION(make_tuple) = by(decay, constructstd::tuple());
What is the type of by(decay,constructstd::tuple()) ?
It is something like `by_adaptor
std::tuple>`. I thought I would be able to write locally:
auto make_tuple(by(decay,constructstd::tuple()));
but evidently you are saying that does not work.
That is not what I am saying at all. It can work, the functions can be used locally, like in the first example. However, for `make_tuple` this is not useful at all. If `std::make_tuple` was assigned to a local variable then no one could ever call the function.
I don't understand this. I have a 'make_tuple' callable above and if I want to use it somewhere else I pass it to whatever functionality needs it. You, instead want things to be global so that they can be used by other functionality without passing anything around. Isn't this just an argument about programming style ? I don't mind if you like your programming style but I doubt I am the first or last programmer who prefers variables created and used when needed. In fact I have a very strong suspicion that "my" programming style is that of the majority of C++ programmers and that only a minority of programmers like global variables even if they can be global within a namespace scope. So what I am essentially saying is that while I understand you like your style and your macros for creating "global" callables, inferring that this is central to using your library, when it is not AFAICS, is going to lose you end-users as well as misrepresent your library. I see much more plusses to your library if the result of using your adapters, decorators, functions, and utilities can be instantiated anywhere and used in ways that any variable in C++ can be used. if however there are real technical limitations, as opposed to "I like this style because I it is easier for me to use with Fit", of using Fit functionality to create "local" objects I think you need to spell out what those limitations are.
So you use global function objects when you want to make the function available for consumption by others. And the advantage of defining `make_tuple` this way over writing it as it is written currently in the standard library, is that we can avoid all the template boilerplate involved with this.
Alternatively, you could write a factory function for this instead:
constexpr auto make_tuple() { return by(decay, constructstd::tuple()); }
Of course this requires the user to write `make_tuple()(xs...)`, which I don't think any user would expect this, especially since `std::make_tuple`
work like that. So instead, it can be wrapped in a template function:
template
constexpr auto make_tuple(Ts&&... xs) -> declype(by(decay, constructstd::tuple())(std::forward<Ts>(xs)...)) { return by(decay, constructstd::tuple())(std::forward<Ts>(xs)...); } Which is a lot of boilerplate to compose some simple functions.
If there really is some other reason it is completely lost to me. If you would like to point to me in your doc where you explain the use of "global variables" as being a key feature of your library or as being necessary to use the functionality of your library, I would be glad to read about it and ask further about it here.
It is not a necessary feature, but it is an important feature nonetheless, and I need to spend more time explaining its usefulness.
If, OTOH, it is just your preference to use global objects as opposed to the various forms of local instantiation, I really wish you would just say that rather than acting like your library does not work correctly somehow other than with global variables.
The composability of the adaptors and functions applies to both global and local functions. The adaptors provide a simple way to create functions to be passed locally, such as creating a comparator for std::sort(ie `std::sort(first, last, by(&employee::name, _<_))`). However, they can also be used to define global/free functions in a much simpler way as well(such as writing `std::make_tuple`), which I think is equally important.
Furthermore, in a quick start guide or introduction, I want to be able to demonstrate the capabilities of the library in a way to show its usefulness, and why someone would choose to use this library. Perhaps, you are ok with awkward syntax or template boilerplate in writing your functions, and would prefer to only use the adaptors for local functions. However, there are others who will find writing global function objects with the adaptors very useful, so I want to be able to show those capabilities in the introduction of
doesn't the
library. I do, however, need to discuss some of the misperceived issues with using global function objects in the documentation. As the issues raised in the review were:
1) Namespacing - This is not a problem because all global function objects can be placed in a namespace.
2) Global state - This is not a problem either, because `BOOST_FIT_STATIC_FUNCTION` declares the function object const with constexpr. So there can be no mutable or changing state internally in the object, and it must be constructed without side effects.
I think addressing these concerns in the documentation will help put users at ease when using global function objects, so they have no problem taking advantage of the adaptors to build function objects globally.
Maybe I have missed something but I have the intuition that many others have missed it likewise, from the responses of others about this issue.
I agree with your intuition, and I believe the documentation should spend more time discussing these issues. I hope my email has made it somewhat clearer to you.