【C++】POCO学习总结(十):Poco::Util::Application(应用程序框架)
【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 present
ServerApplication::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 present
Application::initialize(self);
// add your own initialization code here
}
void uninitialize()
{
// add your own uninitialization code here
Application::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
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!