-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add API support for optional arguments #126
Add API support for optional arguments #126
Conversation
Oh, hmm, we'll need to require a less ancient boost or ship hana ourselves... I don't know how to manipulate parameter packs that way without hana. :( Hana is header-only so shipping it is fairly trivial. |
We're still targeting Ubuntu-16.04 which comes with boost-1.58. It would be good if we could avoid bumping boost for now. |
Btw, |
OK, so what say I just add hana's headers to FC's vendor directory? |
Hm, that's a lot of stuff, but might be worth it. |
Alternatively, I can just add the headers I'm using right now. Is that better? We can just leave it like that until Ubuntu 16.04 goes EOL and we can bump the minimum boost to 1.61+ |
So I thought about it for quite a while and figured out a way to do it without hana, so I just rolled my own solution. =) |
12b11c3
to
cdfcfdc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is one thing I don't understand yet:
Given a call with n fixed and m optional parameters, what triggers the instantiation of the m+1 required templates for n, n+1, ..., n+m actual parameters?
For the client side this is obvious, but for the server side it isn't. In the unit test, both client and server are within the same compilation unit, so the client-side trigger is probably sufficient for both.
Am I missing something?
OK, so as you see, the handling of arguments from the client side is all done in The server-internal side (call from C++ that knows about the FC_API) was the tricky part. Initially, my plan was to do as you said: generate templates for all the possible calls, so that when client code makes a call, there's a matching prototype waiting for it. Seems simple enough in principle, but I couldn't get it working. I couldn't figure out a way to keep track of the left-side (non-optional) parameter types while iterating across to the right-side (optional) ones. It seemed like I needed to take two parameter packs in the same template, like Finally, I realized I didn't need to generate all possible overload templates at all; instead, I can make a single template that gets instantiated by the call site with the arguments the caller provided. From there, I check whether the provided arguments are compatible with the expected ones, and if so, make the call. This is workable because this mechanism allows me to have what I wanted: two parameter packs in the same template. The pack of expected parameters comes from the class instantiation, and the pack of the provided arguments from the method instantiation. This all happens in First we have the template<typename R, typename... Parameters>
struct optionals_callable : public std::function<R(Parameters...)> {
using std::function<R(Parameters...)>::operator(); Next is some infrastructure to cut up the parameter packs. I didn't need this with the original hana implementation, but I had to write it to replace hana. template<typename... CutList>
struct short_pack {};
/// This metafunction removes the first several types from a variadic parameter pack of types.
/// The first parameter is the number of arguments to remove from the beginning of the pack.
/// All subsequent parameters are types in the list to be cut
/// The result pack_cutter<...>::type is a short_pack<RemainingTypes>
template<unsigned RemoveCount, typename... Types>
struct pack_cutter;
/* implementation details elided */ Next is a simple helper function that just invokes a callable with default instantiations of the optional types. The only reason it's here is because I needed to call a separate function to re-extract the cut types pack from
And finally, we have the template that gets instantiated by the caller. It's SFINAE'd out unless the caller provides fewer arguments than the function expects. When instantiated, it makes a lambda to partially apply the
|
Whew! Sorry if that was unnecessarily verbose, but I thought it would be good to detail out what I did and how it works, for posterity. :) Is it clear now? |
Thanks for the explanation. That's the part that I understood, but it's good to see it confirmed. :-) I see now what I was getting at - |
I hope that we didn't introduce new deep recursion issues in this PR. |
Recursion depth in that call is the same as before, i. e. the number of parameters in the api method signature. |
We have a conflict in api.cpp now due to merge of #119 / 03cc93d. Easy to fix. By the way, named arguments can be easily implemented with the For example,
Then the clients can call the API with
|
Nice idea. We should replace the |
Not quite... My change here was to check: if the argument wasn't provided in the range, and all remaining types in the parameter pack are optionals... then instead of partially applying an item from the range, partially apply a null optional. |
FC_API now supports optional arguments! Any trailing arguments of an fc::optional type are now optional, and do not need to be supplied. If omitted, they will be inferred to be null optionals.
So sad... the hana version was way more expressive... But hey, I'm kinda proud I actually got this working! :D
Add missing functionality to websocket_client and websocket_server to make API tests more reliable and to make it possible to use a random port for the tests.
It doesn't work as expected, so get rid of it.
188f16d
to
6b7874e
Compare
FC_API now supports optional arguments! Any trailing arguments of
an fc::optional type are now optional, and do not need to be
supplied. If omitted, they will be inferred to be null optionals.
Note that I have not made the
binary_api_connection
support optional arguments. This is never used, and thus I didn't feel it was worth the effort. We might consider removingbinary_api_connection
entirely.