【C++】郭老二博文之:C++目錄
1、Poco::Util::Application 應用框架
1.1 應用程序基本功能
Poco::Util::Application是POCO實現的的應用程序框架,支持功能如下:
- 命令行參數處理
- 配置文件
- 初始化和關機
- 日志
1.2 命令行程序和守護進程
POCO支持兩種應用:
- 命令行應用程序:從命令行啟動,POCO支持命令行參數處理
- 服務器應用程序:通常作為守護進程運行,比如Linux守護進程、Windows服務
2、Poco::Util::Subsystem 子系統
2.1 說明
Poco::Util::Application繼承自Poco::Util::Subsystem。
POCO中將不同的子系統組成一個應用程序。子系統以模塊化的方式擴展應用。
- 子系統抽象出統一的初始化和關閉等步驟。
- 當一個應用程序被初始化時,它所有注冊的子系統也被初始化。
- 當一個應用程序關閉時,它所有注冊的子系統也會關閉。
2.2 使用
一個子系統都是從Poco::Util::Subsystem繼承而來,下面列出幾個常用的接口
- const char* name():返回子系統的名稱;
- void initialize(Application& app):初始化子系統操作;
- void uninitialize(Application& app):關閉子系統時的操作;
- void reinitialize(Application& app):重新配置子系統(可選;默認實現調用uninitialize(),然后調用initialize());
- void defineOptions(OptionSet& options):子系統自定義命令行參數
3、命令行程序
命令行應用程序是通過創建Poco::Util::Application的子類來實現的。有幾個虛成員函數需要重寫:
- void initialize(Application& self)
- void reinitialize()
- void uninitialize()
- void defineOptions()
- int main(std::vectorstd::string& args)
注意:上面的main是Poco::Util::Application的成員函數,應用程序的主要邏輯寫在這里。
返回值是枚舉:ExitCode
enum ExitCode
{EXIT_OK = 0, 成功終止EXIT_USAGE = 64, 命令行使用錯誤EXIT_DATAERR = 65, 數據格式錯誤EXIT_NOINPUT = 66, 無法打開輸入EXIT_NOUSER = 67, 地址錯誤EXIT_NOHOST = 68, 主機名是未知EXIT_UNAVAILABLE = 69, 服務不可用EXIT_SOFTWARE = 70, 內部軟件錯誤EXIT_OSERR = 71, 系統錯誤 (e.g., can't fork)EXIT_OSFILE = 72, 關鍵操作系統文件丟失EXIT_CANTCREAT = 73, 不能創建(用戶)輸出文件EXIT_IOERR = 74, 輸入/輸出錯誤EXIT_TEMPFAIL = 75, 臨時錯誤,可以嘗試重新運行EXIT_PROTOCOL = 76, 協議中的遠程錯誤EXIT_NOPERM = 77, 沒有權限EXIT_CONFIG = 78 配置錯誤
};
4、Poco::Util::ServerApplication 服務類程序
想要實現一個服務類程序,需要從Poco::Util::ServerApplication繼承;
Poco::Util::ServerApplication本身繼承自Poco::Util::Application;
服務類應用程序可以從命令行運行,比如:Linux守護進程、Windows服務。
通常,服務類應用程序在后臺線程中工作。因此,main()成員函數將啟動線程,然后等待外部請求來終止應用程序(參見waitForTerminationRequest())。
5、配置文件
5.1 說明
默認情況下會創建兩個配置:
- 可寫的MapConfiguration,只讀的PRIO_APPLICATION
- SystemConfiguration, PRIO_SYSTEM
5.2 用法
void MyApplication::initialize(Application& self)
{loadConfiguration(); // 加載默認配置文件Application::initialize(self);
}
6、命令行參數
6.1 說明
應用程序可以定義和處理命令行參數(選項)。命令行參數格式隨系統不同而不同:
- Windows:/option or /option=value
- Linux:-o, -ovalue, --option or --option:value
6.2 使用
應用程序的選項通過重寫虛函數 void defineOptions(OptionSet& options) 來實現
“定義選項類”OptionSet 用來處理選項,比如OptionSet::addOption()函數可以添加選項,示例如下:
void defineOptions(OptionSet& options)
{Application::defineOptions(options);options.addOption(Option("help", "h", "display help information on command line arguments").required(false).repeatable(false).callback(OptionCallback<SampleApp>(this, &SampleApp::handleHelp)));options.addOption(Option("config-file", "f", "load configuration data from a file").required(false).repeatable(true).argument("file").callback(OptionCallback<SampleApp>(this, &SampleApp::handleConfig)));options.addOption(Option("bind", "b", "bind option value to test.property").required(false).argument("value").validator(new IntValidator(0, 100)).binding("test.property"));
}
運行時打印:
-h, --help display help information on command line arguments
-ffile, --config-file=file load configuration data from a file
-bvalue, --bind=value bind option value to test.property
每個選項Option有如下內容:
- 一個全名
- 一個用字符表示的縮寫(短名字)
- 一段描述
- 一個參數名:argument
- 是否必須:required
- 是否可重復:repeatable,它可以在命令行中被多次給出
- 回調函數:callback
6.3 驗證選項參數
通過為選項指定一個Validator對象,可以自動驗證選項參數。
- IntValidator 檢查參數是否為一定范圍內的整數。
- RegExpValidator 驗證參數是否匹配給定的正則表達式。
比如上例中的:.validator(new IntValidator(0, 100))
6.4 顯示幫助信息
Poco::Util::HelpFormatter類可用于顯示命令行選項幫助信息。
當用戶請求幫助信息時,應取消所有進一步的命令行處理(特別是強制執行所需選項)。這可以通過調用stopOptionsProcessing()來完成。
void displayHelp()
{HelpFormatter helpFormatter(options());helpFormatter.setCommand(commandName());helpFormatter.setUsage("OPTIONS");helpFormatter.setHeader("Poco::Util::Application類的示例");helpFormatter.format(std::cout);
}void handleHelp(const std::string& name, const std::string& value)
{_helpRequested = true;displayHelp();stopOptionsProcessing();
}
7、Windows 服務
Windows服務需要注冊。
Poco::Util::ServerApplication可以實現注冊的步驟。
- 注冊:在命令行啟動時指定選項 /registerService
- 取消注冊:指定 /unregisterService 選項來取消
- 設置服務名稱:通過選項 /displayName 來指定
8、Linux 守護進程
在Linux平臺上,繼承Poco::Util::ServerApplication后,可以通過命令行參數“–daemon”來實現,作為守護進程運行。
一個守護進程,當啟動時,會立即從執行實際工作的后臺進程中分離出來。啟動后臺進程后,前臺進程退出。
初始化完成后,在進入main()方法之前,守護進程的當前工作目錄將更改為根目錄(“/”),這是守護進程的常見做法。
應用程序可以通過檢查application.runasdaemon配置屬性來確定它是否作為守護進程運行。
與Windows服務一樣,在配置文件中使用相對路徑時要小心,因為守護進程的當前工作目錄是根目錄。
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Task.h"
#include "Poco/TaskManager.h"
#include "Poco/DateTimeFormatter.h"
#include <iostream>using Poco::Util::Application;
using Poco::Util::ServerApplication;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::OptionCallback;
using Poco::Util::HelpFormatter;
using Poco::Task;
using Poco::TaskManager;
using Poco::DateTimeFormatter;class SampleTask: public Task
{
public:SampleTask(): Task("SampleTask"){}void runTask(){Application& app = Application::instance();while (!sleep(5000)){Application::instance().logger().information("busy doing nothing... " + DateTimeFormatter::format(app.uptime()));}}
};class SampleServer: public ServerApplication
{
public:SampleServer(): _helpRequested(false){}~SampleServer(){}protected:void initialize(Application& self){loadConfiguration(); // load default configuration files, if presentServerApplication::initialize(self);logger().information("starting up");}void uninitialize(){logger().information("shutting down");ServerApplication::uninitialize();}void defineOptions(OptionSet& options){ServerApplication::defineOptions(options);options.addOption(Option("help", "h", "display help information on command line arguments").required(false).repeatable(false).callback(OptionCallback<SampleServer>(this, &SampleServer::handleHelp)));}void handleHelp(const std::string& name, const std::string& value){_helpRequested = true;displayHelp();stopOptionsProcessing();}void displayHelp(){HelpFormatter helpFormatter(options());helpFormatter.setCommand(commandName());helpFormatter.setUsage("OPTIONS");helpFormatter.setHeader("A sample server application that demonstrates some of the features of the Util::ServerApplication class.");helpFormatter.format(std::cout);}int main(const ArgVec& args){if (!_helpRequested){TaskManager tm;tm.start(new SampleTask);waitForTerminationRequest();tm.cancelAll();tm.joinAll();}return Application::EXIT_OK;}
private:bool _helpRequested;
};POCO_SERVER_MAIN(SampleServer)
9、一個簡單的示例
#include "Poco/Util/Application.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Util/AbstractConfiguration.h"
#include "Poco/AutoPtr.h"
#include <iostream>
#include <sstream>using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
using Poco::Util::AbstractConfiguration;
using Poco::Util::OptionCallback;
using Poco::AutoPtr;class SampleApp: public Application/// This sample demonstrates some of the features of the Util::Application class,/// such as configuration file handling and command line arguments processing.////// Try SampleApp --help (on Unix platforms) or SampleApp /help (elsewhere) for/// more information.
{
public:SampleApp(): _helpRequested(false){}protected:void initialize(Application& self){loadConfiguration(); // load default configuration files, if presentApplication::initialize(self);// add your own initialization code here}void uninitialize(){// add your own uninitialization code hereApplication::uninitialize();}void reinitialize(Application& self){Application::reinitialize(self);// add your own reinitialization code here}void defineOptions(OptionSet& options){Application::defineOptions(options);options.addOption(Option("help", "h", "display help information on command line arguments").required(false).repeatable(false).callback(OptionCallback<SampleApp>(this, &SampleApp::handleHelp)));options.addOption(Option("define", "D", "define a configuration property").required(false).repeatable(true).argument("name=value").callback(OptionCallback<SampleApp>(this, &SampleApp::handleDefine)));options.addOption(Option("config-file", "f", "load configuration data from a file").required(false).repeatable(true).argument("file").callback(OptionCallback<SampleApp>(this, &SampleApp::handleConfig)));options.addOption(Option("bind", "b", "bind option value to test.property").required(false).repeatable(false).argument("value").binding("test.property"));}void handleHelp(const std::string& name, const std::string& value){_helpRequested = true;displayHelp();stopOptionsProcessing();}void handleDefine(const std::string& name, const std::string& value){defineProperty(value);}void handleConfig(const std::string& name, const std::string& value){loadConfiguration(value);}void displayHelp(){HelpFormatter helpFormatter(options());helpFormatter.setCommand(commandName());helpFormatter.setUsage("OPTIONS");helpFormatter.setHeader("A sample application that demonstrates some of the features of the Poco::Util::Application class.");helpFormatter.format(std::cout);}void defineProperty(const std::string& def){std::string name;std::string value;std::string::size_type pos = def.find('=');if (pos != std::string::npos){name.assign(def, 0, pos);value.assign(def, pos + 1, def.length() - pos);}else name = def;config().setString(name, value);}int main(const ArgVec& args){if (!_helpRequested){logger().information("Command line:");std::ostringstream ostr;for (ArgVec::const_iterator it = argv().begin(); it != argv().end(); ++it){ostr << *it << ' ';}logger().information(ostr.str());logger().information("Arguments to main():");for (ArgVec::const_iterator it = args.begin(); it != args.end(); ++it){logger().information(*it);}logger().information("Application properties:");printProperties("");}return Application::EXIT_OK;}void printProperties(const std::string& base){AbstractConfiguration::Keys keys;config().keys(base, keys);if (keys.empty()){if (config().hasProperty(base)){std::string msg;msg.append(base);msg.append(" = ");msg.append(config().getString(base));logger().information(msg);}}else{for (AbstractConfiguration::Keys::const_iterator it = keys.begin(); it != keys.end(); ++it){std::string fullKey = base;if (!fullKey.empty()) fullKey += '.';fullKey.append(*it);printProperties(fullKey);}}}private:bool _helpRequested;
};
打印幫助信息
$ ./SampleApp -h
usage: SampleApp OPTIONS
A sample application that demonstrates some of the features of the Poco::Util::Application class.-h, --help display help information on command line arguments
-Dname=value, --define=name=value define a configuration property
-ffile, --config-file=file load configuration data from a file
-bvalue, --bind=value bind option value to test.property
運行后的打印信息
$ ./SampleApp
[Information] Command line:
[Information] ./SampleApp
[Information] Arguments to main():
[Information] Application properties:
[Information] application.argc = 1
[Information] application.argv[0] = ./SampleApp
[Information] application.baseName = SampleApp
[Information] application.cacheDir = /home/laoer/.cache/SampleApp/
[Information] application.configDir = /home/laoer/git/poco/Util/samples/SampleApp/
[Information] application.dataDir = /home/laoer/.local/share/SampleApp/
[Information] application.dir = /home/laoer/git/poco/Util/samples/SampleApp/bin/Linux/x86_64/
[Information] application.name = SampleApp
[Information] application.path = /home/laoer/git/poco/Util/samples/SampleApp/bin/Linux/x86_64/SampleApp
[Information] application.tempDir = /home/laoer/.local/tmp/SampleApp/
[Information] logging.channels.c1.class = ConsoleChannel
[Information] logging.channels.c1.formatter = f1
[Information] logging.formatters.f1.class = PatternFormatter
[Information] logging.formatters.f1.pattern = [%p] %t
[Information] logging.loggers.app.channel = c1
[Information] logging.loggers.app.name = Application
[Information] logging.loggers.root.channel.class = ConsoleChannel
[Information] system.osName = Linux
[Information] system.osVersion = 6.2.0-37-generic
[Information] system.osArchitecture = x86_64
[Information] system.nodeName = laoer
[Information] system.nodeId =
[Information] system.currentDir = /home/laoer/git/poco/Util/samples/SampleApp/bin/Linux/x86_64/
[Information] system.homeDir = /home/laoer/
[Information] system.configHomeDir = /home/laoer/.config/
[Information] system.cacheHomeDir = /home/laoer/.cache/
[Information] system.dataHomeDir = /home/laoer/.local/share/
[Information] system.tempHomeDir = /home/laoer/.local/tmp/
[Information] system.tempDir = /tmp/
[Information] system.configDir = /etc/
[Information] system.dateTime = 2023-12-10T14:50:02Z
[Information] system.pid = 4919