-
Notifications
You must be signed in to change notification settings - Fork 926
Remapping private topics of anonymous nodes works differently in rospy and roscpp #2324
Description
When I launch a node from CLI via rosrun and do not set any name, the nodes with anonymous flag generate a random name.
If I pass a local topic remap (~input:=test), rospy correctly remaps this topic, while roscpp does not.
The problem seems to be in this part of this_node code:
ros_comm/clients/roscpp/src/libros/this_node.cpp
Lines 120 to 170 in 842f0f0
| name_ = name; | |
| bool disable_anon = false; | |
| M_string::const_iterator it = remappings.find("__name"); | |
| if (it != remappings.end()) | |
| { | |
| name_ = it->second; | |
| disable_anon = true; | |
| } | |
| it = remappings.find("__ns"); | |
| if (it != remappings.end()) | |
| { | |
| namespace_ = it->second; | |
| } | |
| namespace_ = names::clean(namespace_); | |
| if (namespace_.empty() || (namespace_[0] != '/')) | |
| { | |
| namespace_ = "/" + namespace_; | |
| } | |
| std::string error; | |
| if (!names::validate(namespace_, error)) | |
| { | |
| std::stringstream ss; | |
| ss << "Namespace [" << namespace_ << "] is invalid: " << error; | |
| throw InvalidNameException(ss.str()); | |
| } | |
| // names must be initialized here, because it requires the namespace to already be known so that it can properly resolve names. | |
| // It must be done before we resolve g_name, because otherwise the name will not get remapped. | |
| names::init(remappings); | |
| if (name_.find("/") != std::string::npos) | |
| { | |
| throw InvalidNodeNameException(name_, "node names cannot contain /"); | |
| } | |
| if (name_.find("~") != std::string::npos) | |
| { | |
| throw InvalidNodeNameException(name_, "node names cannot contain ~"); | |
| } | |
| name_ = names::resolve(namespace_, name_); | |
| if (options & init_options::AnonymousName && !disable_anon) | |
| { | |
| char buf[200]; | |
| std::snprintf(buf, sizeof(buf), "_%llu", (unsigned long long)WallTime::now().toNSec()); | |
| name_ += buf; | |
| } |
It first sets name_ to the init_node() argument, possibly replaces it with __name remap, and then it calls names::init().
However, names::init() calls names::resolve(), which in turn calls this_node::getName():
ros_comm/clients/roscpp/src/libros/names.cpp
Lines 167 to 170 in 842f0f0
| if (copy[0] == '~') | |
| { | |
| copy = append(this_node::getName(), copy.substr(1)); | |
| } |
But the name in this_node is only the non-anonymous one - the anonymizing part is appended only after names::init() finishes.
Minimal code showing the problem is this:
#include <ros/ros.h>
int main(int argc, char** argv)
{
ros::init(argc, argv, "node", ros::init_options::AnonymousName);
ros::NodeHandle nh ("~");
ROS_ERROR("%s", nh.resolveName("test").c_str());
}run as node ~test:=a to see the problem. Running with node ~test:=a __name:=t works as expected, because explicit name setting disables the anonymous node behavior.
Please note that running the node via roslaunch and name="$(anon node)" would not trigger this issue as it does not actually trigger the anonymous init option of the node (it directly generates a random name which is passed to the node in __name:= remap).
For comparison, here is the Python code that works correctly:
import rospy
rospy.init_node("node", anonymous=True)
rospy.logerr(rospy.names.resolve_name("~test"))The solution would be calling names::init() once more after the name mangling is finished. Would that be okay?