The libshore C++ Library

From SHORE wiki
Jump to: navigation, search

Basic functionality used by SHORE is provided as a C++ programming library residing below the directory src/CoreLib of the SHORE source package. The make install command will by default install the libshore library and headers below /usr/local/lib and /usr/local/include, respectively; see the file INSTALL distributed with the SHORE sources for details.

Doxygen-generated API documentation is available.

The program class

The program class is a command line interface base class providing command line option parsing and documentation as well as application-level exception handling and reporting.

Hello world

// file hello_program.cpp

#include <iostream> 
#include "shore/program/program.hpp"

class hello_program
:public shore::program
{
 protected:

        virtual int main()
        {
                std::cout << "\nHello, world!\n" << std::endl;
        }
};

int main(int ac, char **av)
{
        hello_program p;
        p(ac, av);
        return p.status();
}

Given libshore is installed system-wide, this program may be compiled using the command

$ g++ -lshore -o hello hello_program.cpp

The default behavior of the program base class is to display usage instructions when no command line arguments are provided:

$ ./hello

./hello

Usage: ./hello 

However, the main() method is invoked if the command's input is a pipe:

$ true | ./hello

Hello, world!

This default behavior is sensible for many applications. It may however be altered by overriding the protected base class method basic_sanity_check():

        virtual void basic_sanity_check() {}

As a result, the default is no longer to display the help page:

$ ./hello

Hello, world!

Adding command line options

Command line options and further usage instructions may be defined in the class's constructor.

For clarity, command line option variables are grouped in a struct config in our example.

For validation of provided user input, the protected base class method sanity_check() may be implemented.

Invalid user input is indicated by throwing an exception of class usage_error defined by the program class:

class hello_program
:public shore::program
{
 private:
        
	struct config
	{
		std::string name;
	};
 
	config m_conf;

 protected:

	virtual void sanity_check()
	{
		if(m_conf.name == "Joe")
			throw usage_error("Joe is not around");
	}

	virtual int main()
	{
		std::cout << "\nHello, " << m_conf.name << "!\n" <<std::endl;
	}

 public:

	hello_program()
	{
		set_description("Greeter program");
		add_option("name,n", &m_conf.name, "The person to greet (but not Joe)");
	}
};

(the global main() function is omitted from now on)

When called without command line options, the output is now

$ ./hello

./hello --- Greeter program

Usage: ./hello [OPTIONS]

Allowed options:
  -n, --name=STRING (not set) The person to greet (but not Joe)

Command line options are automatically verified through sanity_check():

$ ./hello -n Jim

Hello, Jim!

$ ./hello -n Joe

./hello --- Greeter program

Usage: ./hello [OPTIONS]

Allowed options:
  -n, --name=STRING (=Joe) The person to greet (but not Joe)

error: Joe is not around

Command line option variables are not restricted to the std::string class, but may be of any class for which an operator>> for std::istream objects is defined. Furthermore, certain classes such as std::vector<T> are handled explicitly by the command line option parser:

#include <iostream>
#include <string>
#include <vector>
#include "shore/program/program.hpp"
#include "shore/base/stringops.hpp" // for shore::join() and shore::to_string()

class hello_program
:public shore::program
{
 private:
        
	struct config
	{
		std::vector<std::string> names;
		int times;
	};
 
	config m_conf;

 protected:

	virtual void sanity_check()
	{
		if(m_conf.times < 1)
			throw usage_error("invalid value " + shore::to_string(m_conf.times) + " for --times");
	}

	virtual int main()
	{
		for(int i = 0; i < m_conf.times; ++i)
			std::cout << "Hello, " << shore::join(m_conf.names, ", ") << '!' <<std::endl;
	}

 public:

	hello_program()
	{
		m_conf.times=1;

		set_description("Greeter program");
		add_option("names,n", &m_conf.names, "The people to greet", shore::OPT_MANDATORY);
		add_option("times,t", &m_conf.times, "Greet multiple times");
	}
};

When correctly invoked, the example's output is

$ ./hello -t 2 -n Jim -n Joe
Hello, Jim, Joe!
Hello, Jim, Joe!

In this example, the sanity_check() method was changed to ensure correct specification of the --times option. Furthermore, the default value for times was set to 1 in the constructor, which is displayed accordingly in the middle column of the new help page:

$ ./hello

./hello --- Greeter program

Usage: ./hello OPTIONS

Allowed options:
  -n, --names=STRING[,...] (mandatory) The people to greet
  -t, --times=INT          (=1)        Greet multiple times

For options that do not have a default value, output of the default may be suppressed by specifying shore::OPT_NOTSET as an additional parameter to the add_option() method.

Furthermore, as shown in the example's constructor, shore::OPT_MANDATORY may be specified to instruct the command line option parser to automatically verify that an option was specified by the user:

$ ./hello -t 2

./hello --- Greeter program

Usage: ./hello OPTIONS

Allowed options:
  -n, --names=STRING[,...] (mandatory) The people to greet
  -t, --times=INT          (=2)        Greet multiple times

error: need `--names' (mandatory)

Further documentation to be written.