Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
Boost.Program_options is a C++ library designed to ease the handling of command-line options in programs. It offers a straightforward way to parse and manage arguments provided by users when running the program.
With Boost.Program_options, developers can define the options their program supports, such as flags or parameters, and effortlessly parse them into variables within their code. The library supports various data types for options, including integers, strings, and custom types, providing flexibility in handling different kinds of input. Additionally, it offers error handling capabilities to detect invalid options or missing arguments, ensuring robustness in user input processing. Overall, Boost.Program_options streamlines the development process by simplifying the management of command-line interfaces in C++ applications.
Table of Contents
- What is Boost Program Options ?
- Getting Started
- Option Details
- Parsing Config File
- Conclusion
1. What is Boost Program Options ?
Program options are parameters passed to a program when it is run, typically via the command line. These options allow users to customize the behavior of the program without modifying its source code. Program options can include flags (boolean options) that enable or disable certain features, as well as options with values that specify settings or parameters.
Handling program options manually in C++ can be cumbersome and error-prone. Developers often have to write custom code to parse command-line arguments, validate input, and handle various edge cases. Boost Program Options is a C++ library that simplifies this task by providing a comprehensive framework for defining, parsing, and managing program options effectively.
Here's how Boost Program Options solves the challenges associated with program options:
-
Option Definition:
Boost Program Options allows developers to define program options using a declarative syntax, which resembles defining variables in C++. This makes it easy to specify the long and short forms of options, describe their purpose, and indicate whether they expect values. -
Parsing Command-line Arguments:
The library provides functions to parse command-line arguments automatically based on the defined options. It handles common parsing tasks such as recognizing option flags, extracting option values, and identifying unrecognized arguments. This relieves developers from writing low-level parsing logic and reduces the likelihood of bugs. -
Type Safety:
Boost Program Options ensures type safety by allowing developers to specify the expected types of option values. When parsing command-line arguments, the library automatically converts provided values to the specified types. If a value cannot be converted, it raises an error, helping developers catch type-related issues early in the development process. -
Configuration File Support:
In addition to command-line options, Boost Program Options supports reading configuration settings from files. This allows users to specify default values or override command-line options by placing them in a configuration file. The library provides functions to load configuration settings from various file formats such as INI files, enhancing the flexibility and usability of the application. -
Validation and Constraints:
Boost Program Options allows developers to define constraints on option values to enforce validity and consistency. For example, developers can specify that an option's value must be within a certain range or match a particular pattern. The library automatically validates user-provided values against these constraints and reports errors if they are not met, improving the robustness and reliability of the program. -
Documentation Generation:
The library can automatically generate usage documentation based on the defined options. This documentation includes a summary of available options, their descriptions, and any default values. Generating documentation helps users understand how to use the application's command-line interface effectively, reducing the need for external documentation and improving the overall user experience. -
Integration with Boost Libraries:
Boost Program Options integrates seamlessly with other Boost libraries, allowing developers to combine its functionality with other features such as filesystem operations, regular expressions, and serialization. This enables building robust and feature-rich applications using a consistent and well-tested set of tools, enhancing productivity and maintainability.
By providing a comprehensive and easy-to-use framework for defining, parsing, and managing program options, Boost Program Options simplifies the development of C++ applications and helps developers create more flexible, reliable, and user-friendly software.
2. Getting Started
Before moving onto the code we should understand the workflow of the code. Let's break down the process step by step:
-
Declaring Options:
We start by defining all the options our program will accept using theoptions_description
class. This is where we specify the name of each option, along with information about its expected value (if any) and a description of what the option does. We use theadd_options
method of theoptions_description
class to define each option. This method returns a special object that lets us define options using theoperator()
function. For example, we might define an option that takes no value (a flag) or an option that expects an integer value. -
Storing Option Values:
Next, we create an object of thevariables_map
class. This object is where we'll store the values of the options found on the command line. Thevariables_map
class can store values of various types, so it's flexible enough to handle any kind of option value. -
Parsing Command-line Arguments:
We use thestore
,parse_command_line
, andnotify
functions to populate thevariables_map
object with the values of the options provided on the command line. Thestore
function reads the command-line arguments and stores their values in thevariables_map
. Theparse_command_line
function parses the command-line arguments using the options we've defined. Finally, thenotify
function updates thevariables_map
object to reflect any changes made during parsing. -
Using the Options:
Now that we have the options stored in thevariables_map
, we can access and use them as needed. We can treat thevariables_map
object like a regularstd::map
, accessing option values using their names. However, to ensure type safety, we need to use theas
method to retrieve option values with the correct data type. If we try to retrieve a value with the wrong data type, an exception will be thrown.
Here is the basic code :
#include <boost/program_options.hpp>
#include <iostream>
namespace po = boost::program_options;
int main(int argc, char const *argv[])
{
// Declare the supported options.
po::options_description desc("Allowed options");
// Add options to the options description.
desc.add_options()
("help", "produce help message") // Option: --help (no value), shows help message
("compression", po::value<int>(), "set compression level"); // Option: --compression=<value>, sets compression level
// Store and parse command-line arguments into variables_map.
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm); // Parse command-line arguments based on options description
po::notify(vm); // Update variables_map with parsed values
// Check if help option is provided.
if (vm.count("help"))
{
// Display help message.
std::cout << desc << "\n";
return 1;
}
// Check if compression option is provided.
if (vm.count("compression"))
{
// Display compression level.
std::cout << "Compression level was set to " << vm["compression"].as<int>() << ".\n";
}
else
{
// Display message indicating compression level was not set.
std::cout << "Compression level was not set.\n";
}
return 0;
}
After we compiler the code using command :
g++ -o program_name source_file.cpp -lboost_program_options
Output :
- If we don't pass any argument :
ββ$ ./main
Compression level was not set.
- If we pass --help as argument :
ββ$ ./main --help
Allowed options:
--help produce help message
--compression arg set compression level
- If we pass --compression value as argument :
ββ$ ./main --compression 10
Compression level was set to 10.
3. Option Details
To understand the options in detail , let's take the example of the compiler as explained in official tutorial. As in our normal compilers we will have help , optimization include-file and include-path as the options.
It will look like the following in code :
int opt;
po::options_description desc("Allowed options");
desc.add_options()
// help program option
("help", "produce help message")
// optimization option
("optimization", po::value<int>(&opt)->default_value(10),
"optimization level")
// include path program option
("include-path,I", po::value< vector<string> >(), "include path")
// input-file progra option
("input-file", po::value< vector<string> >(), "input file")
;
The help options is similar to the previous example. But the point of interest here is optimization command. The command contains po::value<int>(&opt)->default_value(10)
which signifies that we will store the value passed with the optimization in opt
variable and if we don't pass the value we will use the default value which is set to 10.
The include-path option is also somewhat different from the normal which we have seen in previous example. The I in the include-path signifies the Interface. Users typically like to use short option names for common options, and the "include-path,I" name specifies that short option name is "I". So, both "--include-path" and "-I" can be used.
In last example we have input-file we have used vector of strings as value. The library provides special support for vectors , it will be possible to specify the option several times, and all specified values will be collected in one vector.
#include <boost/program_options.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace po = boost::program_options;
int main(int argc, char const *argv[])
{
int opt;
po::options_description desc("Allowed options");
desc.add_options()
// help command
("help", "produce help message")
// optimization command
("optimization", po::value<int>(&opt)->default_value(10), "optimization level")
// include path command
("include-path,I", po::value<std::vector<std::string>>(), "include path")
// input file path
("input-file", po::value<std::vector<std::string>>(), "input file");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
std::cout << "Optimization default set to "
<< vm["optimization"].as<int>() << ".\n";
if (vm.count("help"))
{
std::cout << desc << "\n";
return 1;
}
if (vm.count("optimization"))
{
std::cout << "Optimization level was set to "
<< vm["optimization"].as<int>() << ".\n";
}
if (vm.count("include-path"))
{
std::cout << "Include paths are: "
<< vm["include-path"].as<std::vector<std::string>>().size() << "\n";
std::cout<<"Paths : \n";
for (auto i : vm["include-path"].as<std::vector<std::string>>() )
{
std::cout<<i<<std::endl;
}
}
if (vm.count("input-file"))
{
std::cout << "Include Paths are: "
<< vm["input-file"].as<std::vector<std::string>>().size() << "\n";
std::cout<<"Files : \n";
for (auto i : vm["input-file"].as<std::vector<std::string>>() )
{
std::cout<<i<<std::endl;
}
}
return 0;
}
Output:
- The help command :
ββ$ ./main --help
Allowed options:
--help produce help message
--optimization arg (=10) optimization level
-I [ --include-path ] arg include path
--input-file arg input file
- The optimization command :
ββ$ ./main
Optimization default set to 10.
Optimization level was set to 10.
using the optimization flag:
ββ$ ./main --optimization=20
Optimization default set to 20.
Optimization level was set to 20.
- The include-path command :
ββ$ ./main --include-path=main.cpp --include-path=a.cpp
Optimization level was set to 10.
Include paths are: 2
Paths :
main.cpp
a.cpp
Using -I command which is interface for the include-path :
ββ$ ./main -I main.cpp -I a.cpp
Optimization level was set to 10.
Include paths are: 2
Paths :
main.cpp
a.cpp
- Using the include-path command :
ββ$ ./main --input-file=a.cpp --input-file=b.cpp
Optimization default set to 10.
Optimization level was set to 10.
Include Paths are: 2
Files :
a.cpp
b.cpp
Here we have some unsual stuff going on which can be seen as :
./main --input-file=a.cpp
and
./main a.cpp
The command line tokens which have no option name, as above, are called "positional options" by this library. They can be handled too. With a little help from the user, the library can decide that "a.cpp" really means the same as "--input-file=a.cpp". Here's the additional code we need:
po::positional_options_description p;
p.add("input-file", -1);
po::variables_map vm;
po::store(po::command_line_parser(ac, av).
options(desc).positional(p).run(), vm);
po::notify(vm);
The first two lines say that all positional options should be translated into "input-file" options. Also note that we use the command_line_parser class to parse the command line, not the parse_command_line function. The latter is a convenient wrapper for simple cases, but now we need to pass additional information.
Output :
ββ$ ./main --optimization 4 -I foo a.cpp
Optimization default set to 4.
Optimization level was set to 4.
Include paths are: 1
Paths :
foo
Include Paths are: 1
Files :
a.cpp
The final code is as follows :
#include <boost/program_options.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace po = boost::program_options;
int main(int argc, char const *argv[])
{
int opt;
po::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("optimization", po::value<int>(&opt)->default_value(10), "optimization level")
("include-path,I", po::value<std::vector<std::string>>(), "include path")
("input-file", po::value<std::vector<std::string>>(), "input file");
po::positional_options_description p;
p.add("input-file", -1);
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).
options(desc).positional(p).run(), vm);
po::notify(vm);
std::cout << "Optimization default set to "
<< vm["optimization"].as<int>() << ".\n";
if (vm.count("help"))
{
std::cout << desc << "\n";
return 1;
}
if (vm.count("optimization"))
{
std::cout << "Optimization level was set to "
<< vm["optimization"].as<int>() << ".\n";
}
if (vm.count("include-path"))
{
std::cout << "Include paths are: "
<< vm["include-path"].as<std::vector<std::string>>().size() << "\n";
std::cout<<"Paths : \n";
for (auto i : vm["include-path"].as<std::vector<std::string>>() )
{
std::cout<<i<<std::endl;
}
}
if (vm.count("input-file"))
{
std::cout << "Include Paths are: "
<< vm["input-file"].as<std::vector<std::string>>().size() << "\n";
std::cout<<"Files : \n";
for (auto i : vm["input-file"].as<std::vector<std::string>>() )
{
std::cout<<i<<std::endl;
}
}
return 0;
}
4. Parsing Config files
Often we use the config files to store our configuration data. The boost program_options has made it easier to parse these files.
Consider the example :
# params.config
[General]
name=John Doe
age=30
[Database]
host=localhost
port=3306
username=user
password=pass
database=dbname
The C++ code to read the params is as follows :
#include <iostream>
#include <fstream>
#include <boost/program_options.hpp>
namespace po = boost::program_options;
int main()
{
try
{
// Declare variables to store parameters
std::string general_name;
int general_age;
std::string db_host, db_username, db_password, db_database;
int db_port;
// Initialize Boost.Program_options
po::options_description desc("Configuration");
desc.add_options()
("General.name", po::value<std::string>(&general_name), "Name")
("General.age", po::value<int>(&general_age), "Age")
("Database.host", po::value<std::string>(&db_host), "Host")
("Database.port", po::value<int>(&db_port), "Port")
("Database.username", po::value<std::string>(&db_username), "Username")
("Database.password", po::value<std::string>(&db_password), "Password")
("Database.database", po::value<std::string>(&db_database), "Database");
po::variables_map vm;
// Read configuration file
std::string fileName = "params.conf";
std::ifstream config_file(fileName);
if (!config_file)
{
throw std::runtime_error("Unable to open config file.");
}
po::store(po::parse_config_file(config_file, desc), vm);
config_file.close();
po::notify(vm);
// Print parameters
std::cout << "General:" << std::endl;
std::cout << "Name: " << general_name << std::endl;
std::cout << "Age: " << general_age << std::endl;
std::cout << "\nDatabase:" << std::endl;
std::cout << "Host: " << db_host << std::endl;
std::cout << "Port: " << db_port << std::endl;
std::cout << "Username: " << db_username << std::endl;
std::cout << "Password: " << db_password << std::endl;
std::cout << "Database: " << db_database << std::endl;
}
catch (const std::exception &e)
{
std::cerr << "Error: " << e.what() << '\n';
}
return 0;
}
The code starts by declaring variables to store the configuration parameters that will be read from the params.conf file. These include: general_name and general_age for storing general information like a name and age ,db_host, db_username, db_password, db_database, and db_port for storing database connection parameters.The po::options_description desc("Configuration"); line initializes a Boost.Program_options object named desc with a description label "Configuration". This object is used to define which options the program expects in the configuration file.
The desc.add_options() function is called to define the expected options.
Each option is specified with three parameters:
- The name of the option as it appears in the configuration file (e.g., "General.name"). The format is "Section.key" for hierarchical configurations like INI files.
- A pointer to the variable that will store the value read from the configuration file (e.g., (&general_name)).
- A description of the option (e.g., "Name"), which is useful for generating help messages but is not used when parsing configuration files.
The file name is stored in fileName and an std::ifstream object named config_file is created to open and read from this file. The program checks if the file was successfully opened by evaluating !config_file. If the file cannot be opened, it throws a runtime error with the message "Unable to open config file."
The po::store(po::parse_config_file(config_file, desc), vm); line parses the configuration file using the options described in desc and stores the results in a po::variables_map object named vm. This object acts as a container to hold the parsed option values.The po::notify(vm); function is called to update the variables bound to the options with the parsed values. This effectively transfers the values read from the file into the corresponding variables in the program.
Output :
General:
Name: John Doe
Age: 30
Database:
Host: localhost
Port: 3306
Username: user
Password: pass
Database: dbname
5. Conclusion
The Boost.Program_options library offers a powerful yet easy-to-use solution for managing program options and configuration files in C++ applications. By abstracting away the complexities of command-line parsing and configuration file handling, it enables developers to focus more on the core functionality of their applications rather than the intricacies of parsing and validating user inputs. Here are the key takeaways from our discussion:
-
Simplified Option Management: Boost.Program_options allows for the declarative definition of command-line options and configuration file parameters, making it straightforward to specify what kind of input the program expects. This not only simplifies the code but also makes it more readable and maintainable.
-
Type Safety and Flexibility: The library ensures type safety by allowing developers to specify the expected types of option values, reducing the likelihood of runtime errors. It also supports a wide range of data types, including custom types, providing the flexibility needed to handle various kinds of inputs.
-
Automatic Parsing and Validation: Boost.Program_options automatically parses command-line arguments and configuration files based on the defined options, handling common parsing tasks and errors. It also allows for the validation of option values against developer-defined constraints, ensuring that the program operates with valid and consistent settings.
-
Enhanced Usability: By supporting features such as default values, short and long option names, and automatic documentation generation, the library enhances the usability of C++ applications. It enables users to interact with the application through a command-line interface effectively, with clear instructions and feedback.
-
Robust Configuration File Handling: The library's support for reading from configuration files allows for flexible application configuration, enabling users to specify settings in a file rather than through command-line arguments alone. This is particularly useful for applications with numerous options or for settings that are commonly reused.
In conclusion, Boost.Program_options is an invaluable tool for C++ developers looking to implement command-line interfaces and configuration management in their applications. Its comprehensive feature set and ease of use make it an excellent choice for projects ranging from small utilities to complex software systems. By utilizing Boost.Program_options, developers can create applications that are flexible, reliable, and user-friendly, ultimately enhancing the end-user experience.
Key Takeaways (Boost Program Options)
- Boost Program Options provides a powerful, flexible way to handle command-line arguments in C++ applications, making the parsing of user input straightforward and error-resistant.
- The library supports parsing of command-line arguments and configuration files, including INI files, allowing for versatile application configuration strategies.
- It offers type safety for program options, automatically converting input to the specified data type and throwing errors for invalid conversions, which helps prevent type-related bugs.
- Developers can define default values for options, making applications more user-friendly by not requiring all settings to be specified explicitly by the user.
- The ability to automatically generate help messages based on the defined options simplifies the creation of user documentation and improves the overall user experience.
- Validation and constraints can be easily applied to options, ensuring that the program receives valid input and behaves predictably.
- Boost Program Options integrates well with other Boost libraries, enabling the development of robust and feature-rich applications.
- Positional options support allows for more intuitive command-line interfaces, enabling users to specify arguments without explicit option names for common parameters.