简体   繁体   中英

C++: How to pass user input through the system without using global variables?

I am having the problem, that my application can has a lot of user input which determines how the application will be run. The application is an in memory database system and the user could for example invoke the program with commands like '--pagesize 16384' (sets the memory page size to use), '--alignment 4096' (sets the memory alignment to use) or '--measure' (sets a flag to measure certain routines).

Currently I save all the user input in global variables which are defined as extern in a header file:

//@file common.hh
extern  size_t      PAGE_SIZE_GLOBAL;
extern  size_t      ALIGNMENT_GLOBAL;
extern  size_t      MEMCHUNK_SIZE_GLOBAL;
extern  size_t      RUNS_GLOBAL;
extern  size_t      VECTORIZE_SIZE_GLOBAL;
extern  bool        MEASURE_GLOBAL;
extern  bool        PRINT_GLOBAL;
extern  const char* PATH_GLOBAL;

and in main source file:

#include "modes.hh"

size_t      PAGE_SIZE_GLOBAL;
size_t      ALIGNMENT_GLOBAL;
size_t      MEMCHUNK_SIZE_GLOBAL;
size_t      RUNS_GLOBAL;
size_t      VECTORIZE_SIZE_GLOBAL;
bool        MEASURE_GLOBAL;
bool        PRINT_GLOBAL;
const char* PATH_GLOBAL;

int main(const int argc, const char* argv[]){

    ...
    //Initialize the globals with user input
    PAGE_SIZE_GLOBAL        = lArgs.pageSize();
    ALIGNMENT_GLOBAL        = lArgs.alignment();
    MEMCHUNK_SIZE_GLOBAL    = lArgs.chunkSize();
    RUNS_GLOBAL             = lArgs.runs();
    VECTORIZE_SIZE_GLOBAL   = lArgs.vectorized();
    MEASURE_GLOBAL          = lArgs.measure();
    PRINT_GLOBAL            = lArgs.print();
    std::string tmp         = lArgs.path() + storageModel + "/";
    PATH_GLOBAL             = tmp.c_str();

    ...
}

I then include the header file common.hh in each file, where a global variable is needed (which can be very deep down in the system).

I already read a dozen times to prevent global variables so this is obviously bad style. In the book 'Code Complete 2' from Steve McConnell the chapter about global variables also stated to prevent global variables and use access routines instead. In the section 'How to Use Access Routines' he writes

"Hide data in a class. Declare that data by using the static keyword (...) to ensure only a single instance of the data exists. Write routines that let you look at the data and change it."

First of all, the global data won't change (maybe this is changed later but at least not in the near future). But I don't get how these access routines are any better? I will also have a class I need to include at every file where the data is needed. The only difference is the global data are static members accessed through getter functions.

(Edited) I also thought about using a global data Singleton class. But an object with ALL the global data sounds overkill since only a few global variables of the object are needed at its different destinations.

My Question: Should I just stick to the global variables? Are there better solutions, what am I missing? What are the best practices?

Edit: If I would identify a few classes where the user input is needed the most, I could change the global data to member variables. What would be the best practice to pass the user input to these classes? Passing the data as parameters through the whole system down to the lowest layers sounds wrong. Is there are design pattern (thinking about something like a factory) which would be suited here?

How to pass user input through the system without using global variables.

It is easy. Surprise, I created a class.

For a while, I called this class a travel case, because I considered it analogous to the needs of a suitcase during a trip. The TC_t is a non-standard container which held useful things for what is going on at your destination, and there is only one created, with references passed to any other objects that could use the information. Not global, in the strictest sense.

This TC_t is created in main() thread, while studying the command line options.


I recently wrote yet-another-game-of-life. User inputs included a) destination of output (ie a tty num), b) initial fill-pattern choices, c) 'overrides' for game board dimensions, d) test modes, including max speed, and vector vs. array options for cell behaviours.

The GOLUtil_t (Game Of Life Utility) (previously TC_t) includes methods that are useful in more than one effort.

For your question, the two typical globals I avoided are the a) gameBoard, and b) ansi terminal access.

std::cout << "accessing '" << aTermPFN << "' with std::ofstream " 
          << std::endl;
std::ofstream*  ansiTerm = new std::ofstream(aTermPFN);

if (!ansiTerm->is_open())
{
   dtbAssert(nullptr != ansiTerm)(aTermPFN);
   std::cerr << "Can not access '" << aTermPFN << "'" << std::endl;
   assert(0);  // abort
 }

// create game-board - with a vector of cell*
CellVec_t  gameBoard;
gameBoard.reserve (aMaxRow * aMaxCol);

GOLUtil_t  gBrd(aMaxRow, aMaxCol, gameBoard, *ansiTerm);

This last line invoked the ctor of GOLUtil_t.

The instance "gBrd" is then passed (by reference) to the ctor of the game, and from there, to any aggregate objects it contained.

std::string retVal;
{
  // initialize display, initialize pattern
  GameOfLife_t  GOL(gBrd, timeOfDay, fillPatternChoiceLetter, useArray);

  std::string retValS = GOL.exec2(testMode);

  retVal = gBrd.clearGameBoard(retValS); // delete all cells
}
// force GameOfLife_t dtor before close ansiTerm

ansiTerm->close();

Summary - No globals.

Every instance of any class that needed this info (where to output? what are dimensions?) has access to the GOLUtil_t for their entire lifetime. And GOLUtil_t has methods to lighten the coding load.

Note: because single output terminal, I used a single thread (main)


Your first refactor effort might be to:

a) remove the global classes,

b) and instead instantiate these in main() (for lifetime control)

c) and then pass-by-reference these formerly global instances to those non-global objects that make use of them. I recommend in the ctor(s).

d) remember to clean up (delete if new'd)


my environment: Ubuntu 15.10, 64 bit, g++ V5

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM