-
Notifications
You must be signed in to change notification settings - Fork 22
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
RSDK-3589 add wrapper for navigation service #323
Conversation
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.
overall this looks really solid, just added some suggestions about SDK coding standards changes which are currently in progress!
src/viam/sdk/common/utils.cpp
Outdated
@@ -184,5 +184,21 @@ bool from_dm_from_extra(const ProtoStruct& extra) { | |||
return false; | |||
} | |||
|
|||
template <typename Src, typename Dst> | |||
void vecToRepeatedPtr(const std::vector<Src>& vec, google::protobuf::RepeatedPtrField<Dst>& dest) { |
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.
Not that we've announced this super loudly but we're currently trying to cut back on API and google::
types in headers, so I think we'll want to delete these helper functions even though they're being used a couple times.
As a suggested inline replacement, I think this could just be
dest.Assign(vec.begin(), vec.end());
You can also do that with the Add
method, but note that if dest
already had stuff then this function would be under-reserving potentially and adding to existing elements which is maybe not what I'd expect from an xToY
function
As a suggested replacement for this one, can you just write inline
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.
good call -- clearing the destination in d334572 to protect against adding to non-empty collection
I think Assign(begin, end)
doesn't work because the types are different -- tried to get it working with constructor/operator implicit conversion approach, couldn't, guessing even harder now that the constructor/operators have been moved out of common headers
src/viam/sdk/common/utils.cpp
Outdated
void repeatedPtrToVec(const google::protobuf::RepeatedPtrField<Src>& src, std::vector<Dst>& vec) { | ||
vec.reserve(src.size()); | ||
for (auto& x : src) { | ||
vec.push_back(x); |
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.
similarly I think this could be vec.assign(src.Begin(), src.End())
, also with similar caveats as above about assigning vs appending
src/viam/sdk/services/navigation.hpp
Outdated
}; | ||
|
||
struct Waypoint { | ||
Waypoint(const viam::service::navigation::v1::Waypoint& proto) |
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.
similar to the other comment about keeping API stuff out of headers, @stuqdog is currently working on a PR to take these kinds of conversion functions out of public headers, so maybe you guys can talk so that we have a similar approach in this PR as well
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.
but one option for example is, since I think these are only being used in navigation_client.cpp
to just inline them there
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.
okay I removed all *.pb.h includes from the wrapper header (navigation.hpp)
how:
- I moved the implicit conversions for
Waypoint
andPath
to functions in the navigation client/server.cpp files - and gave the std::vector <-> RepeatedPointer conversions their own header
now the invocation looks like:
// when destination type has a `from_proto` method, use it:
repeatedPtrToVec(response.obstacles(), *ret);
// otherwise, pass in the conversion function:
repeatedPtrToVec(response.waypoints(), *ret, waypoint_from_proto);
… ptr <-> vec conversions
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.
Just taking a look because I'm interested. The client/server parts all look great, and I'm glad to see that the helpers are making it much easier to add these implementations. I think the proto_utils stuff could be simplified and generalized.
src/viam/sdk/common/proto_utils.hpp
Outdated
/// in cpp implementation files, but not in wrapper headers consumed by third party code. | ||
#pragma once | ||
|
||
#include <viam/api/common/v1/common.pb.h> |
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.
A semi-near-term goal for the C++ SDK is to move all the proto stuff out of public headers. Could you push this down into viam/sdk/common/private/proto_utils.hpp
? That's how we indicate that files shouldn't be consumed except by files that form part of the SDK or published as part of the install step.
src/viam/sdk/common/proto_utils.hpp
Outdated
|
||
namespace viam { | ||
namespace sdk { | ||
|
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.
add namespace impl
here, so viam::sdk::impl
. That's the namespace we use for any private things that shouldn't escape.
src/viam/sdk/common/proto_utils.hpp
Outdated
/// @brief Copies elements from a protobuf repeated pointer array into a std::vector. Src type | ||
/// must be implicitly convertible to Dst (probably via operator on Src). | ||
template <typename Src, typename Dst> | ||
void vecToRepeatedPtr(const std::vector<Src>& vec, google::protobuf::RepeatedPtrField<Dst>& dest) { |
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.
I have a slight preference for out parameters being taken by pointer, as it makes it clearer at the call site that the field is likely being mutated:
vecToRepeatedPtr(my_vec, dest); // err, who is getting modified here?
vs
vecToRepeatedPtr(my_vec, &dest); // ah, classic out parameter.
Others dislike this because they worry that it allows dest to be nullptr. I don't find that compelling because requiring it to be a reference doesn't prevent the opposite mistake:
RepeatedPtrField<X>* pdest = ...
vecToRepeatedPtr(my_vec, *pdest); // oops, what if pdest is nullptr?
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.
So generally speaking I'm very much against output parameters being passed as pointers, it's one of the things I dislike the most about google's C++ coding guidelines. I think with your counterexample above the point is that, in C++ and as high level as this code is, we generally aren't passing around pointer types in the first place, and the point of references is that they must point to a valid object or else it's UB. So as for
oops, what if pdest is nullptr
That's on the caller for dereferencing a null pointer in the first place, and is equally true of if my_vec
was *p_my_vec
for some reason, and this is kind of the point of C++ having reference types.
Callsite legibility I'm somewhat sympathetic to but would be better done with something like std::ref(arg)
or gsl::output_parameter
, because then you don't lose the non-null guarantee.
.....All that said, I think this could be a pointer parameter just for ABI insulation reasons
src/viam/sdk/common/proto_utils.hpp
Outdated
|
||
/// @brief Non-member to_proto() version. (necessary for moving generated types out of wrapper | ||
/// headers). Template param F is a function that converts from Src to Dst. | ||
template <typename Src, typename Dst> |
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.
More idiomatic would just to be take
template <typename Src, typename Callable>
void vecToRepeatedPtr(const std::vector<Src>& vec,
google::protobuf::RepeatedPtrField<Dst>& dest,
Callable&& c) {
Because then you can pass any sort of callable (std::function, lambda, function pointer as you have now).
You can then enforce the shape of the callable with a static assert or similar.
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.
explain this one -- this is better because it would select this specialization and return a static assert error, rather than giving a long 'no matching template' error?
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.
I think, as written, yours will only accept function pointers. I could be wrong, I'd need to test it out on godbolt.
src/viam/sdk/common/proto_utils.hpp
Outdated
Dst to_proto(const Src&)) { | ||
dest.Clear(); | ||
dest.Reserve(vec.size()); | ||
for (auto& x : vec) { |
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.
This would work with other containers than just std::vector right? std::array, std::list, etc.?
src/viam/sdk/common/proto_utils.hpp
Outdated
template <typename Src, typename Dst> | ||
void repeatedPtrToVec(const google::protobuf::RepeatedPtrField<Src>& src, | ||
std::vector<Dst>& vec, | ||
Dst from_proto(const Src&)) { |
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.
You could eliminate the distinction between the converting and non-converting by just always taking a transformation function and defaulting it to std::identity (might need to get it from boost for now)
That actually has me wondering whether all of these are really glosses on std::transform and would be better implemented that way?
src/viam/sdk/services/navigation.hpp
Outdated
|
||
API api() const override; | ||
|
||
virtual Mode get_mode(const std::string name, const ProtoStruct& extra) = 0; |
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.
Since this is the public facing header, this is the one that should have doc comments. Also, please add blank lines between the end of the declaration of one method and the beginning of the next.
virtual LocationResponse get_location(const std::string name, const ProtoStruct& extra) = 0;
virtual std::unique_ptr<std::vector<Waypoint>> get_waypoints(const std::string name,
const ProtoStruct& extra) = 0;
That, moreso, once comments are added:
/// Some comment
virtual LocationResponse get_location(const std::string name, const ProtoStruct& extra) = 0;
/// Another comment
virtual std::unique_ptr<std::vector<Waypoint>> get_waypoints(const std::string name,
const ProtoStruct& extra) = 0;
|
||
using namespace viam::service::navigation::v1; | ||
|
||
Navigation::Waypoint waypoint_from_proto(const viam::service::navigation::v1::Waypoint& proto) { |
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.
So just as a general guideline it's kind of a C++ anti-pattern to have the type name in the name of the function--since C doesn't have function overloading you're kind of forced to do things like f_int
f_double
etc, but in this case function overloading is able to resolve from_proto(some_waypoint)
to call the overload which takes a const Waypoint&
.
That said, this function and path_from_proto
are only used in this .cpp
file, so I would probably just define them inline as a lambda passed to your conversion function, ie,
repeatedPtrToVec(src, dest, [](const auto& proto) { return Navigation::Waypoint{proto.id(), geo_point::from_proto(proto.location())}; });
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.
renamed to bare from/to proto in 4e9f32c
(holding off on bigger changes bc it sounds like sdk team will do a design pass on repeatedPtrToVec stuff after this merges)
} | ||
|
||
// weak backport of std::transform. | ||
template <typename T, typename U> |
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.
I think just template this as template <typename T, typename U, typename Func>
and have the second argument be Func f
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.
that made it sad
could specify template param I guess? but can I leave as-is?
/host/src/viam/sdk/tests/test_navigation.cpp: In lambda function:
/host/src/viam/sdk/tests/test_navigation.cpp:78:14: error: no matching function for call to 'mapOver(std::vectorviam::sdk::Navigation::Waypoint&, std::__cxx11::basic_string (*)(const viam::sdk::Navigation::Waypoint&))'
78 | });
| ^
/host/src/viam/sdk/tests/test_navigation.cpp:46:16: note: candidate: 'template<class T, class U, class Func> std::vector viam::sdktests::navigation::mapOver(const std::vector&, Func)'
46 | std::vector mapOver(const std::vector& source, Func f) {
| ^~~~~~~
/host/src/viam/sdk/tests/test_navigation.cpp:46:16: note: template argument deduction/substitution failed:
/host/src/viam/sdk/tests/test_navigation.cpp:78:14: note: couldn't deduce template parameter 'T'
78 | });
What changed
Why
So people can write nav modules in C++.
Follow-ups
repeatedPtrToVec
and friends