3

Consider a large program with many different parts that have a single command line interface, as is the case with most applications. How best do I handle passing various command line parameters, that may be meant for various parts of the application, to them?

These parts may be hierarchical, for example, main() instantiates A (which instantiates B, C) and D (which spawns E), and each of them has specific requirements for command line parameters. There are various possibilities.

  • Make global variables for the parameters, set them and then just read them as required. Possibly in a configuration namespace or class or some sort.
  • Make a HashMap, which maps the command line arguments, which constitutes members of an Enum, to their values. These are strings initially of course, but each part of the program reads and interprets the types as requires. This map is passed hierarchically with each instantiation.
  • Make classes for AConf, DConf, which are then populated by main and passed to A, and D resp., from which they make BConf, CConf, EConf, and pass those configuration objects to B, C, E resp.

and many more.

In a large application with various parts, how is this handled gracefully?

Note: I'm not asking about argument parsing. I'm asking how to send the values to different objects after they've been parsed.

1
  • 3
    This is very broad, and there is no one-size-fits-all solution to this. It makes a difference if you have 5, 50, or 500 different command line parameters, and it makes a difference what the program does, how it is structured in detail. All 3 of your solutions can be sensible, depends on the specific requirements.
    – Doc Brown
    Jul 12, 2018 at 17:34

5 Answers 5

4

For this it is OK to have a static class in a shared library that gets the entire command line at startup. Say you name it CommandLine. You can then create methods like

bool HasSwitch(string)

and

string GetNamedArgument(string)

on it. If you need more sophisticated parsing later you can easily add it.

1
  • This is recommended because the Command-Line Arguments will be a static value for the whole execution! No matter how many Threads and Classes you will spawn, all inside the same Process will share the same args - and the args are immutable!
    – Falco
    Jul 16, 2018 at 14:02
2

In complex applications, I see command-line parameters as the highest priority source of application configuration, hence I include their handling in the same logic as configuration information.

In my sense, configuration information is retrieved from (highest priority first):

  • command-line arguments
  • configuration file provided in command-line arguments
  • environment variables
  • user-defined configuration file (e.g.: ~/.myapprc on Unix)
  • global configuration file (e.g.: /etc/myapprc on Unix)
  • default configuration defined at compile time
2
  • So, in your solution, for example, the Configuration will be a single class with a function read_conf, which will read configuration from various places and set it's instance variables, and then the Configuration object will be passed around the program? Jul 16, 2018 at 10:16
  • Yes, this is a possible implementation: a global, or singleton, Configuration object that gathers input parameters from predefined sources.
    – mouviciel
    Jul 16, 2018 at 10:28
1

In a large application with various parts, how is this handled gracefully?

Any of your suggested solutions could work, but as the size and scope of the project increases, each method has its downfall. Ultimately I think this is best solved by a configuration singleton, which can support anything from a hashmap to a getConfigA(...) or whatever specific solution you want.

Equivalently a static function or set of static functions can serve the same purpose; if you need something that feels/smells like a global, a singleton is probably a better solution.

0

The Parameters-Model-View-Controller Paradigm

In the same situation: many use cases, use cases in a navigation menu, parameters to the use-cases (selection of some category, selection of order item, ...).

We had a classical UseCase<Model, View, Controller> class, but now have a

UseCase<P, M, V, C>
UseCase<Parameters, Model, View, Controller>

So we actually split the classical model into:

  • M (mutable) data model, and
  • P (fixed parametrisation, control variables/constants, header data)

Indeed Parameters has a different life than Model.

This means that every use case XxxUseCase got its own XxxParameters class. One could very well argue for some kind of scripting / declarative approach; as your mention of hash maps. But if there is no need, a reference from one system component to another is best served by a type-safe, specified, parameter class. Especially if the number and variation of parameters grow.

-2

Usually (barring some very specific requirements) none of the suggested three options is good enough, neither is having a static object. All of these have the same problems: objects are required to know about command line arguments and failure to encapsulate knowledge.

The problem starts at this sentence:

main() instantiates A (which instantiates B, C) and D (which spawns E)

This is not exactly how an object-oriented program should work. All of the program is supposed to be plugged together at the very top of the execution hierarchy, i.e. in the main() method. Objects very rarely directly instantiate each other if at all. We use dependency inversion, factories, and other things to make sure we can separate the instantiation and usage of objects.

If you do this, you will actually instantiate things that matter in your main(), which means you can directly configure them with meaningful values (like a timeout period as Duration) instead of just a bag of strings with no meaning at all.

2
  • I understand dependency inversion, but does that mean that all program objects have to be instantiated by main? This doesn't seem feasiable for large program. If you have large parts of a program, for example, data layer, notification layer, auth layer, then each of these layers might have very many small objects, which may not get instantiated by main. What about in that case? Jul 16, 2018 at 9:45
  • Not all objects are instantiated on the top, for objects that are instantiated elsewhere, you can instantiate a factory with the parameters already present, etc. Some parameters can be even passed along. Also, it does not matter how "large" you application is. At all. You have to use decomposition to arrive at manageable chunks/modules/whatever, so at the top a really large application might not differ at all from a small application. Jul 16, 2018 at 10:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.