ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | Q&A answers.ros.org

# ROS generic subscriber as a class method

Here I am trying this example's subscriber example code from facontidavide and it works fine as it is. However when I put the topicCallback() in a Class, it's instances appear to share its global variables - whether they are public or private.

class Foo{
private:
std::string _topic;
public:
Foo(std::map <std::string, std::string>& info)
{
_topic = info["topic"];
};
void topicCallback(const topic_tools::ShapeShifter::ConstPtr& msg,
const std::string &topic_name,
RosIntrospection::Parser& parser)
{
---EXAMPLE CODE---
cout << _topic << endl;
}
}

main(){
---SOME CODE---
for (auto &info : topics)
{
Foo f(info);
boost::function<void(const topic_tools::ShapeShifter::ConstPtr&) > callback;
callback = [&parser, &info, &f](const topic_tools::ShapeShifter::ConstPtr& msg)->void
{
f.topicCallback(msg, info.at("topic"), parser) ;
};
subscribers.push_back( nh.subscribe(info["topic"], 10, callback) );
}
---SOME MORE CODE---
}


When I create several instances of the class and subscribe to the callback just as in the example code, _topic appears to be the same for all instances. Actually it is the topic of the last instance I create.

This probably might be due to my little understanding of C++11. It'd be a great help if someone could point out what I am doing wrong here.

Edit: Yes it is a C++ related question. But I don't think I'll get an answer by asking this on stackoverflow. I tried using boost::bind instead using a lambda. But it gives some unrecognizable errors like error: use of deleted function xxx May be due to the callback expecting a topic_tools::ShapeShifter for all message types?

edit retag close merge delete

I have a feeling this has more to with your use of the lambda there and how you are capturing the context than with anything ROS related.

( 2018-08-30 05:19:21 -0500 )edit

Sort by » oldest newest most voted

You create Foo f(info) as a local, and then it goes out of scope and is destructed. However, you're also capturing a reference to f within your lambda, and when f goes out of scope you now have a dangling reference.

You should hold onto all of your Foo objects; maybe make them own their subscriber and then keep them in an array somewhere, instead of just holding onto the subscriber objects.

class Foo {
public:
Foo(ros::NodeHandle& nh, std::string topic, RosIntrospection::Parser& parser)
: topic_(topic)
, parser_(parser)
, subscriber_(nh.subscribe(topic, 10, &Foo::topicCallback, this))
{
}

void topicCallback(const topic_tools::ShapeShifter::ConstPtr& msg)
{
---EXAMPLE CODE---
cout << _topic << endl;
}
private:
std::string topic_;
RosIntrospection::Parser& parser_;
ros::Subscriber subscriber_;
};

main(){
---SOME CODE---
std::vector<Foo> foos;
for (auto &info : topics)
{
foos.emplace_back( nh, info["topic"], parser );
}
---SOME MORE CODE---
}

more

Thanks. This makes more sense. I tried this but still I get use of deleted function errors. This is the only thing I found from the compiler output that makes sense. note: ‘RosIntrospection::details::Tree...’ is implicitly deleted because the default definition would be ill-formed

( 2018-08-31 04:09:22 -0500 )edit

Errors I get from this is very much similar to the errors I get from using boost::bind. Nothing makes sense. use of deleted functions everywhere

( 2018-08-31 04:11:28 -0500 )edit
1

You write yourself that you have "little understanding of C++11" and this really seems like a C++ issue. If nothing makes sense then it would perhaps be good to first get a bit of a better grip on the C++ concepts that you're trying to use?

( 2018-08-31 05:49:06 -0500 )edit

Found out what caused the error: use of deleted function xxx errors on @ahendrix 's answer. RosIntrospection::Parser instance can't be copied (similar to std::map instances). You can't do parser1 = parser2;. I don't have a deep understanding on how this happens but if someone come across a similar issue, hope this'll be of help.

Below is my working solution. For my application, I don't need to pass the parser from main so I am using a local parser for each class instance.

class Foo {
public:
Foo(std::string topic)
: topic_(topic)
{
ros::NodeHandle n;
sub = n.subscribe(topic_, 10, &Foo::topicCallback, this);
}

void topicCallback(const topic_tools::ShapeShifter::ConstPtr& msg)
{
---EXAMPLE CODE---
}
private:
std::string topic_;
RosIntrospection::Parser& parser;
ros::Subscriber sub;
};

main(){
---SOME CODE---
Foo *f;
std::vector<Foo*> foos;
for (auto &info : topics)
{
f = new Foo(info);
foos.push_back(f);
}
---SOME MORE CODE---
}

more