cbandera / rosparam_handler

An easy wrapper for using parameters in ROS
Other
47 stars 26 forks source link

Add an abstract base to generated parameters structures. #37

Open artivis opened 6 years ago

artivis commented 6 years ago

Summary

Comes in replacement to #35 .

Details

Please consider the following example :

struct DummyBase
{
  DummyBase() = default;
  virtual ~DummyBase() = default;

  // A helper function to instantiate the params_ptr_
  template <typename T>
  void init()
  {
    params_ptr_ = boost::make_shared<T>(ros::NodeHandle("~"));
  }

  void fromParamServer()
  {
    params_ptr_->fromParamServer();
  }

  void toParamServer()
  {
    params_ptr_->toParamServer();
  }

  // Pointer to the xParameters object
  rosparam_handler::ParametersPtr params_ptr_;
};

struct Foo : public DummyBase
{
    Foo()
    {
       // Instantiate the params_ptr_ as a FooParameters object
       init<FooParameters>();

       params_ptr_->fromParamServer();
    }

    void doThingsWithParams()
    {
       auto foo_param_ptr = boost::dynamic_pointer_cast<FooParameters>(params_ptr_);

       // Assuming FooParameters has an int parameter called my_int_param
       foo_param_ptr->my_int_param = 42;

       params_ptr_->toParamServer();
    }
};

struct Bar : public DummyBase
{
  Bar()
  {
     // Instantiate the params_ptr_ as a BarParameters object
     init<BarParameters>();

     params_ptr_->fromParamServer();

     dynamic_reconfigure::Server<BarConfig>::CallbackType cb;
     cb = boost::bind(&Bar::configCallback, this, _1, _2);
     dr_srv_.setCallback(cb);
  }

  void configCallback(BarConfig &config, uint32_t level)
  {
     params_ptr_->fromConfig(config);
  }

  dynamic_reconfigure::Server<BarConfig> dr_srv_;
};

This PR does not change the default behavior while allowing one to use a unique rosparam_handler::ParameterPtr in a base class and instantiate it to a different parameter type. All functions remain available through the base pointer.

The main drawback here is that to access the actual members of the instantiated object one has to cast the pointer to the appropriate type (either statically or dynamically).
See helper functions :

using namespace boost;
using namespace rosparam_handler;
...
ParametersPtr my_params = make_shared<BarParameters>(ros::NodeHandle("~"));
...
shared_ptr<FooParameters> foo_cast = dynamic_parameters_cast<FooParameters>(my_params);

assert(foo_cast == nullptr);

shared_ptr<BarParameters> bar_cast = static_parameters_cast<BarParameters>(my_params);

assert(bar_cast != nullptr);

All former tests are passing and a I added one for this new scheme.
Their is also a test-case demonstrating how to test dynamic_reconfigure callback with rosparam_handler.

This PR also turns *Parameters into copyable objects :

FooParameters my_foo(ros::NodeHandle("~"));
my_foo.fromParamServer();

FooParameters my_foo_copy(my_foo); // Copy initialization

FooParameters my_foo_copy2 = my_foo; // Copy assignment

Please consider this PR as Work In Progress until I add some more tutorial to rosparam_handler_tutorial.

artivis commented 6 years ago

Force pushed after a cleaning of this branch history.

artivis commented 6 years ago

Since all tests are declared in the same rostest, they all share the same environment, thus the same param server.
This can be an issue since some tests set params while other retrieve them (or vice-versa) leading to collision. To avoid it each NodeHandle uses a sub-namespace (see 9956bf1699ff146730009cf79540893b7c7c0bb3).
The other alternative is to break down the unique rostest into several.

artivis commented 6 years ago

Rebased on develop and fix tests compilation (#34).
Force pushed a broken history...

artivis commented 6 years ago

ping @130s, @cbandera. Anyone to review this ?? :DD