We are developing complex application on the top of the ROS framework in C++ and recently ran into discussion how to provide parameters to the parts of code far away from the starting main().
The application is a kind of a server. It has a listener for incoming requests that then finds a suitable factor for the worker capable of handling the request. The factory then creates the worker (not always the same class) and at this point the worker internals (various methods inside) need parameters that may differ for each worker. The parameters are initially within the configuration file on the file system (same for all objects, with different sections), but they are automatically loaded and parsed as the main node starts.
The parameters are easily accessible in the main()
via "node handle" that allows to access them by name, not unlike the system properties are usually accessed. The parameter has one of the few supported primitive types. The type that is differentiated by the name of the method used to access it. Here is the tutorial explaining how the parameters are accessed.
Different developers so far had four approaches:
- Pass the "parameter handle" as a parameter to about any constructor, making sure each object has access to it. However this limits testability - a "live" handle an complex interface to the whole ROS infrastructure that must be running. Hence any object that relies on it just to be created is not testable with simple Unit tests.
- Read
all required parameters within
main()
after startup and store either as global variables in class-specific namespace or alternatively as static fields of these classes. This contradicts generic understanding that "global variables are bad". While the variables are not modified after being set on startup, looks like they cannot be declaredconst
at the language level due the need to set them once inside themain
. - Create the structures containing parameters and pass these around till they reach the constructor where they are used. Different classes need different parameters so we would need to define multiple helper structures, have the code to populate them from node handle and still looks annoying to pass them around.
- Create the "parameter provider" class that can be either mock (returning the agreed values) or production implementation (wrapping node handle). Most of the developers are used to access node handle directly so this is promising the long way to persuade them to use that mediating wrapper instead.
- Some proposed, use dependency injection framework. I understand Spring would do for Java but does such a thing exists or is even possible in C++?
Which of these four approaches (or probably some other) would be the most optimal to provide parameters from the node handle far away from where the node handle is easily accessible?
Foo
needs configuration, there is an accordingFooConfig
struct. Then we use a framework likeboost serialization
to read/write the config. In main, we pass the parsed config structs to the according classes. I like the following advantages: The config of each class is a struct, which makes it easy to use and easy to use a different config in testing. The serialization framework decouples the config struct from the concrete format (xml, binary, etc).