Use Case
Boost Program Options library usage to access user input to the program
Session 1:
- Updated main.cpp to use program options library
- Captures user inputs
Libraries Used
Session 1
Video explanation of the code
Program options input screen
root@efa32de8e392:/workspace/backend-cpp# LD_LIBRARY_PATH=/usr/src/redis-plus-plus-modules/lib:/workspace/backend-cpp/boost_1_81_0//stage/lib ./dist/backend-cpp --help
[2023-03-11 05:19:35.676734] [0x00007fe9039040c0] [info] Allowed options:
Generic options:
-v [ --version ] print version string
--help produce help message
-c [ --config ] arg (=app.cfg) name of a file of a configuration.
-s [ --sub-program-name ] arg (=ws-vendor)
Module name to run. Options:
ws-vendor|redis-uploader|webserver.
Configuration:
--ws-vendor-hostname arg websocket vendor hostname
--ws-vendor-port arg websocket vendor port
--ws-vendor-greeting-str arg websocket vendor greeting string
--ws-vendor-endpoint arg websocket vendor endpoint
--ws-vendor-no-threads arg websocket vendor no of threads
--redis-topics arg comma separated list of redis topics
--webserver-hostname arg webserver hostname or ip address to run
on
--webserver-port arg webserver port
--webserver-root-dir arg webserver root dir
--webserver-no-threads arg webserver no of threads
--webserver-topics arg comma separated list of topics
--log-level arg log level
Source Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ws-vendor-hostname=xxxx | |
ws-vendor-port=443 | |
ws-vendor-greeting-str={"action": "subscribe", "symbols": "ETH-USD, BTC-USD"} | |
ws-vendor-endpoint=/ws/crypto | |
ws-vendor-no-threads=3 | |
redis-topics=ETH-USD,BTC-USD | |
webserver-hostname=0.0.0.0 | |
webserver-port=8080 | |
webserver-root-dir=. | |
webserver-no-threads=5 | |
webserver-topics=ETH-USD,BTC-USD | |
log-level=debug |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* SRAVZ LLC | |
**/ | |
#include "main.hpp" | |
using namespace std; | |
int main(int ac, char* av[]) | |
{ | |
try { | |
int opt; | |
string config_file; | |
string sub_program_name; | |
string redis_host; | |
string redis_port; | |
string redis_password; | |
set<string> sub_program_options{ "ws-vendor", "redis-uploader", "webserver" }; | |
set<string> log_levels{ "debug", "info", "warning", "error" }; | |
// Declare a group of options that will be | |
// allowed only on command line | |
po::options_description generic("Generic options"); | |
generic.add_options() | |
("version,v", "print version string") | |
("help", "produce help message") | |
("config,c", po::value<string>(&config_file)->default_value("app.cfg"), | |
"name of a file of a configuration.") | |
("sub-program-name,s", po::value<string>(&sub_program_name)->default_value("ws-vendor"), | |
"Module name to run. Options: ws-vendor|redis-uploader|webserver.") | |
; | |
// Declare a group of options that will be | |
// allowed both on command line and in | |
// config file | |
po::options_description config("Configuration"); | |
config.add_options() | |
("ws-vendor-hostname", | |
po::value<string>(), | |
"websocket vendor hostname") | |
("ws-vendor-port", | |
po::value<string>(), | |
"websocket vendor port") | |
("ws-vendor-greeting-str", | |
po::value<string>(), | |
"websocket vendor greeting string") | |
("ws-vendor-endpoint", | |
po::value<string>(), | |
"websocket vendor endpoint") | |
("ws-vendor-no-threads", | |
po::value<int>(), | |
"websocket vendor no of threads") | |
("redis-topics", | |
po::value<string>(), | |
"comma separated list of redis topics") | |
("webserver-hostname", | |
po::value<string>(), | |
"webserver hostname or ip address to run on") | |
("webserver-port", | |
po::value<string>(), | |
"webserver port") | |
("webserver-root-dir", | |
po::value<string>(), | |
"webserver root dir") | |
("webserver-no-threads", | |
po::value<string>(), | |
"webserver no of threads") | |
("webserver-topics", | |
po::value<string>(), | |
"comma separated list of topics") | |
("log-level", | |
po::value<string>(), | |
"log level") | |
; | |
// Hidden options, will be allowed both on command line and | |
// in config file, but will not be shown to the user. | |
po::options_description hidden("Hidden options"); | |
// hidden.add_options() | |
// ("input-file", po::value< vector<string> >(), "input file") | |
// ; | |
po::options_description cmdline_options; | |
cmdline_options.add(generic).add(config).add(hidden); | |
po::options_description config_file_options; | |
config_file_options.add(config).add(hidden); | |
po::options_description visible("Allowed options"); | |
visible.add(generic).add(config); | |
po::positional_options_description p; | |
// p.add("input-file", -1); | |
// Calls to store, parse_command_line and notify functions | |
// cause vm to contain all the options found on the command line. | |
po::variables_map vm; | |
store(po::command_line_parser(ac, av). | |
options(cmdline_options).positional(p).run(), vm); | |
notify(vm); | |
ifstream ifs(config_file.c_str()); | |
if (!ifs) | |
{ | |
BOOST_LOG_TRIVIAL(info) << "can not open config file: " << config_file; | |
return 0; | |
} | |
else | |
{ | |
store(parse_config_file(ifs, config_file_options), vm); | |
notify(vm); | |
} | |
// If variables map contains help. Please help | |
if (vm.count("help")) { | |
BOOST_LOG_TRIVIAL(info) << visible; | |
return 0; | |
} | |
if (vm.count("version")) { | |
BOOST_LOG_TRIVIAL(info) << "backend-cpp, version 1.0"; | |
return 0; | |
} | |
// If variables map contains sub_program_name. Check it is a valid sub_program. | |
if (sub_program_options.find(sub_program_name) == sub_program_options.end()) { | |
BOOST_LOG_TRIVIAL(info) << "sub-program-name: " << sub_program_name << " invalid. should be one of: "; | |
for (string const& sub_program : sub_program_options) | |
{ | |
BOOST_LOG_TRIVIAL(info) << sub_program << ' '; | |
} | |
return 1; | |
} | |
std::string hostname; | |
boost::program_options::options_description desc_env; | |
// TODO: Add these back if we need to pass the parameters at the cli | |
desc_env.add_options()("redis_host", boost::program_options::value<std::string>(&redis_host)); | |
desc_env.add_options()("redis_port", boost::program_options::value<std::string>(&redis_port)); | |
desc_env.add_options()("redis_password", boost::program_options::value<std::string>(&redis_password)); | |
boost::program_options::variables_map vm_env; | |
boost::program_options::store(boost::program_options::parse_environment(desc_env, | |
[](const std::string& i_env_var) | |
{ | |
switch(get_hash(i_env_var)) { | |
case "REDIS_HOST"_ : | |
{ | |
return "redis_host"; | |
break; | |
} | |
case "REDIS_PORT"_ : | |
return "redis_port"; | |
break; | |
case "REDIS_PASSWORD"_ : | |
return "redis_password"; | |
break; | |
default: | |
return ""; | |
} | |
}), | |
vm_env); | |
boost::program_options::notify(vm_env); | |
// BOOST_LOG_TRIVIAL(info) << "Environment varaibles: \n"; | |
// BOOST_LOG_TRIVIAL(info) << "redis_host: " << vm_env["redis_host"].as<string>() << '\n'; | |
// BOOST_LOG_TRIVIAL(info) << "redis_port: " << vm_env["redis_port"].as<string>() << '\n'; | |
// BOOST_LOG_TRIVIAL(info) << "redis_password: " << vm_env["redis_password"].as<string>() << '\n'; | |
// Init program | |
switch(get_hash(vm["log-level"].as<string>())) { | |
case "debug"_ : | |
{ | |
logging::core::get()->set_filter | |
( | |
logging::trivial::severity >= logging::trivial::debug | |
); | |
break; | |
} | |
case "info"_ : | |
logging::core::get()->set_filter | |
( | |
logging::trivial::severity >= logging::trivial::info | |
); | |
break; | |
case "warning"_ : | |
logging::core::get()->set_filter | |
( | |
logging::trivial::severity >= logging::trivial::warning | |
); | |
break; | |
case "error"_ : | |
logging::core::get()->set_filter | |
( | |
logging::trivial::severity >= logging::trivial::error | |
); | |
break; | |
default: | |
BOOST_LOG_TRIVIAL(info) << "Unsupported log level: " << vm["log-level"].as<string>() << endl; | |
return 1; | |
} | |
BOOST_LOG_TRIVIAL(info) << "sub_program_name"; | |
switch(get_hash(sub_program_name)) { | |
case "ws-vendor"_ : | |
{ | |
ws_vendor(vm["ws-vendor-hostname"].as<string>(), | |
vm["ws-vendor-port"].as<string>(), | |
vm["ws-vendor-greeting-str"].as<string>(), | |
vm["ws-vendor-endpoint"].as<string>(), | |
vm["ws-vendor-no-threads"].as<int>()); | |
break; | |
} | |
case "redis-uploader"_ : | |
redis_uploader(vm["redis-topics"].as<string>()); | |
break; | |
case "webserver"_ : | |
webserver(vm["webserver-hostname"].as<string>(), | |
vm["webserver-port"].as<string>(), | |
vm["webserver-root-dir"].as<string>(), | |
vm["webserver-no-threads"].as<string>(), | |
vm["webserver-topics"].as<string>()); | |
break; | |
default: | |
BOOST_LOG_TRIVIAL(info) << "Unsupported option: " << sub_program_name << endl; | |
} | |
} | |
catch(exception& e) | |
{ | |
BOOST_LOG_TRIVIAL(info) << e.what(); | |
return 1; | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "util.hpp" | |
using namespace std; | |
// https://docs.sravz.com/docs/tech/cpp/boost-beast-websockets/ | |
int ws_vendor(std::string host, std::string port, std::string text, std::string endpoint, int threads); | |
void redis_uploader(std::string topics); | |
// https://docs.sravz.com/docs/tech/cpp/boost-beast-websocket-server/ | |
int webserver(std::string address_, std::string port_, std::string doc_root_, std::string threads_, std::string topics_); | |
// A helper function to simplify the main part. | |
template<class T> | |
ostream& operator<<(ostream& os, const vector<T>& v) | |
{ | |
copy(v.begin(), v.end(), ostream_iterator<T>(os, " ")); | |
return os; | |
} | |
// https://gcc.godbolt.org/z/pfJXRm | |
// https://en.cppreference.com/w/cpp/language/consteval | |
// This function might be evaluated at compile-time, if the input | |
// is known at compile-time. Otherwise, it is executed at run-time. | |
constexpr inline | |
unsigned long long int get_hash(char const * str, int h = 0) | |
{ | |
return (!str[h] ? 5381 : (get_hash(str, h+1)*33) ^ str[h] ); | |
} | |
constexpr inline | |
unsigned long long int operator "" _(char const * p, size_t) { return get_hash(p); } | |
inline | |
unsigned long long int get_hash(std::string const & s) { return get_hash (s.c_str()); } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef SRAVZ_BACKENDCPP_UTIL_H | |
#define SRAVZ_BACKENDCPP_UTIL_H | |
// Includes | |
#include "boost/format.hpp" | |
#include "root_certificates.hpp" | |
#include <boost/algorithm/string.hpp> | |
#include <boost/asio.hpp> | |
#include <boost/asio/co_spawn.hpp> | |
#include <boost/asio/detached.hpp> | |
#include <boost/asio/io_context.hpp> | |
#include <boost/asio/io_service.hpp> | |
#include <boost/asio/ip/tcp.hpp> | |
#include <boost/asio/signal_set.hpp> | |
#include <boost/asio/spawn.hpp> | |
#include <boost/asio/strand.hpp> | |
#include <boost/asio/write.hpp> | |
#include <boost/atomic.hpp> | |
#include <boost/beast.hpp> | |
#include <boost/beast/core.hpp> | |
#include <boost/beast/ssl.hpp> | |
#include <boost/beast/websocket.hpp> | |
#include <boost/beast/websocket/ssl.hpp> | |
#include <boost/config.hpp> | |
#include <boost/coroutine2/all.hpp> | |
#include <boost/foreach.hpp> | |
#include <boost/json.hpp> | |
#include <boost/lexical_cast.hpp> | |
#include <boost/lockfree/spsc_queue.hpp> | |
#include <boost/log/core.hpp> | |
#include <boost/log/expressions.hpp> | |
#include <boost/log/trivial.hpp> | |
#include <boost/optional.hpp> | |
#include <boost/program_options.hpp> | |
#include <boost/smart_ptr.hpp> | |
#include <boost/thread.hpp> | |
#include <boost/url.hpp> | |
#include <chrono> | |
#include <cstddef> | |
#include <cstdio> | |
#include <cstdlib> | |
#include <fstream> | |
#include <functional> | |
#include <iostream> | |
#include <iterator> | |
#include <jwt/jwt.hpp> | |
#include <memory> | |
#include <mutex> | |
#include <redis-plus-plus/src/sw/redis++/errors.h> | |
#include <redis-plus-plus/test/src/sw/redis++/utils.h> | |
#include <set> | |
#include <string> | |
#include <thread> | |
#include <unordered_map> | |
#include <unordered_set> | |
#include <utility> | |
#include <vector> | |
// Namespaces | |
namespace attrs = boost::log::attributes; | |
namespace beast = boost::beast; // from <boost/beast.hpp> | |
namespace expr = boost::log::expressions; | |
namespace http = beast::http; // from <boost/beast/http.hpp> | |
namespace json = boost::json; | |
namespace keywords = boost::log::keywords; | |
namespace logging = boost::log; | |
namespace net = boost::asio; // from <boost/asio.hpp> | |
namespace po = boost::program_options; | |
namespace sinks = boost::log::sinks; | |
namespace src = boost::log::sources; | |
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> | |
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp> | |
/* Using namespaces */ | |
// using boost::format; | |
// using boost::io::group; | |
using RedisInstance = sw::redis::Redis; | |
using tcp = boost::asio::ip::tcp; | |
typedef boost::asio::io_context::executor_type executor_type; | |
typedef boost::asio::strand<executor_type> strand; | |
// Classes | |
class ThreadInfo | |
{ | |
public: | |
beast::flat_buffer buffer; | |
std::shared_ptr<RedisInstance> redis; | |
ThreadInfo() {} | |
ThreadInfo(beast::flat_buffer buffer_, std::shared_ptr<RedisInstance> redis_) : buffer(buffer_), redis(redis_) {} | |
}; | |
typedef std::map<boost::thread::id, ThreadInfo> ThreadsInfo; | |
// Functions | |
// Gets env variable | |
bool getenv(const char *name, std::string &env); | |
// Returns redis connection options to be used in redis connection creation | |
sw::redis::ConnectionOptions getRedisConnectionOptions(); | |
#endif // end SRAVZ_BACKENDCPP_UTIL_H |