以下基于Poco::Utils::Application框架,实现了一个非常简单的程序。涉及了:
- 主程序框架(Poco::Util::Application)
- 参数解析(Poco::Util::Option)
- 日志文件(Poco::Logger)
- 子系统(Poco::Util::Subsystem)
程序调用示例(参数可设置为Unix风格,或其它风格)
xxx.exe --help
xxx.exe -h
xxx.exe --file="C:\hello world\1.txt"
xxx.exe -f=D:\windows\2.docx
xxx.exe --time=13134
#include <iostream>
#include "Poco/Util/Application.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Util/PropertyFileConfiguration.h"
#include "Poco/File.h"
#include "Poco/SimpleFileChannel.h"
#include "Poco/PatternFormatter.h"
#include "Poco/FormattingChannel.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/Format.h"
class BackgroundSubSystem : public Poco::Util::Subsystem
{
public:
virtual const char* name() const override
{
return "BackgroundSubSystem";
}
void doSomething()
{
}
protected:
virtual void initialize(Poco::Util::Application& app) override
{
// 初始化子系统
}
virtual void uninitialize() override
{
// 反初始化子系统
}
};
class TheApp : public Poco::Util::Application
{
public:
TheApp()
{
// 指定命令行风格为Unix风格,即:--help --key=value --key2="hello world" -t -i=12
// Windows平台默认风格为:/help /key=value
setUnixOptions(true);
addSubsystem(new BackgroundSubSystem); // 子系统需要在构造函数中添加
}
protected:
// 回调顺序:0
// 该接口最先被Poco::Application框架调用
virtual void defineOptions(Poco::Util::OptionSet& options) override
{
__super::defineOptions(options); // 必须调用一次父类接口
options.addOption(
Poco::Util::Option("help", "h", "display help information on command line arguments")
.required(false) // 是否必须
.repeatable(false) // 是否可重复
.callback(Poco::Util::OptionCallback<TheApp>(this, &TheApp::onHelp))); // 设置参数回调函数
options.addOption(
Poco::Util::Option("file", "f", "Run application as a daemon.")
.required(false)
.repeatable(false)
.argument("file")); // 有值的参数,必须加argument,否则无法解析,示例:--file=1.txt
options.addOption(
Poco::Util::Option("time", "t", "Time Information.")
.required(false)
.repeatable(false)
.argument("time").binding("application.commandline.time", this->configPtr())); // 将参数直接解析到配置信息中去
}
// 回调顺序:1
// 此接口负责程序的初始化
virtual void initialize(Application& app) override
{
initializeLogger();
loadConfigurationEx();
__super::initialize(app); // 必须调用一次父类接口
}
// 回调顺序:2
// 此接口负责程序的主要逻辑
virtual int main(const std::vector<std::string>& args) override
{
testConfig();
testLogger();
testSubSystem();
TheApp::instance().name(); // 可以在程序的任何地方,通过单例拿到Application对象
return Poco::Util::Application::EXIT_OK;
}
// 回调顺序:3
// 此接口负责程序的反初始化
virtual void uninitialize() override
{
saveConfigurationEx();
__super::uninitialize(); // 必须调用一次父类接口
}
// 回调顺序:0-0
// 函数defineOptions在执行过程中,会遍历所有匹配的命令行,并调用本接口
virtual void handleOption(const std::string& name, const std::string& value) override
{
__super::handleOption(name, value); // 必须调用一次父类接口
}
private:
// 回调顺序:0-1
// 函数defineOptions在执行过程中,发现参数设置了回调后,会调用对应的回调函数
void onHelp(const std::string& name, const std::string& value)
{
// 打印帮助信息
Poco::Util::HelpFormatter helpFormatter(options());
helpFormatter.setUnixStyle(true);
helpFormatter.setCommand(commandName());
helpFormatter.setUsage("OPTIONS");
helpFormatter.setHeader("A web server that serves the current date and time.");
helpFormatter.format(std::cout);
stopOptionsProcessing(); // 中断参数的解析,后面的参数不再继续解析
exit(1);
}
void loadConfigurationEx()
{
#ifndef _LOAD_POCO_DEFAULT_CONFIGURATION
// 加载自定义配置文件(读写)
// 不使用自定义配置文件时,所有配置信息都存储在内存中,随着程序的退出,配置信息将不复存在。
// 如果希望配置信息记录到文件中,需要将文件配置信息的优先级数值设为最低,且有写权限。
// 每次设置配置时,会按优先级顺序找到第一个具有写权限的。
Poco::Path confPath(config().getString("application.path"));
confPath.setExtension("properties");
Poco::File(confPath).createFile();
config().add(new Poco::Util::PropertyFileConfiguration(confPath.toString()), "properties", PRIO_APPLICATION, true);
#else
// 加载默认路径下的配置文件(只读)
// 后缀名支持4种:properties;ini;json;xml
// 注意:这里的配置是只读的,需要我们预先准备好配置文件,放置在程序所在路径下,文件名与程序同名
// 没有只读配置,无需调用该接口
loadConfiguration();
#endif
// 框架默认自带了两层配置信息:
// 第一层是系统级(只读),优先级PRIO_SYSTEM,提供了系统名称,版本号,CPU框架等信息,具体参见Poco::Util::SystemConfiguration类说明
// 第二层是应用级(读写),优先级PRIO_APPLICATION,提供了程序的目录,名称等信息,也可以存储一些我们自定义的配置信息
// 以上配置均存储于内存中,程序退出即销毁。
//
// 设置配置信息,会按优先级顺序找到第一个有写权限的配置接口,写入后返回。
// 优先级相同的配置,按插入顺序,后插入的排在前面。
}
void saveConfigurationEx()
{
#ifndef _LOAD_POCO_DEFAULT_CONFIGURATION
// 保存到配置文件(程序中更新的配置信息只更新了内存,并没有更新到文件中)
auto autoPtr = config().find("properties");
auto pConfig = dynamic_cast<Poco::Util::PropertyFileConfiguration*>(autoPtr.get());
if (pConfig)
{
Poco::Path confPath(config().getString("application.path"));
confPath.setExtension("properties");
pConfig->save(confPath.toString());
}
}
#endif
void initializeLogger()
{
// 日志默认输出到命令行,如果要输出到文件,需要调整个日志输出通道(Channel)
// 日志内置了一些属性,用于设置文件路径、限制文件大小等,具体参见Poco::SimpleFileChannel类说明
// 简单文件输出通道
Poco::AutoPtr<Poco::SimpleFileChannel> pFileChannel(new Poco::SimpleFileChannel);
pFileChannel->setProperty("path", "sample.log");
pFileChannel->setProperty("rotation", "2 K");
// Poco::Logger::root().setChannel(pFileChannel);
// 格式化输出通道(支持的格式化语句参见Poco::PatternFormatter类说明)
Poco::AutoPtr<Poco::PatternFormatter> pPatternFmt(new Poco::PatternFormatter("%H:%M:%S.%i [%q] [%O@%u] %t"));
Poco::AutoPtr<Poco::FormattingChannel> pFmtChannel(new Poco::FormattingChannel(pPatternFmt, pFileChannel));
// 设置输出通道
Poco::Logger::root().setChannel(pFmtChannel);
}
void testConfig()
{
// 读取参数配置信息(该配置信息由参数绑定而来)
if (config().hasProperty("application.commandline.time"))
{
auto cmdtime = config().getInt64("application.commandline.time");
}
// 读取文件配置信息(该配置信息由配置文件解析得到)
if (config().hasProperty("global.application.name"))
{
auto appname = config().getString("global.application.name");
}
else
{
config().setString("global.application.name", commandName());
}
}
void testLogger()
{
poco_information(logger(),
Poco::format(u8"================ 程序启动 (%s) ================ ",
Poco::DateTimeFormatter::format(startTime(), Poco::DateTimeFormat::SORTABLE_FORMAT)));
poco_warning(logger(), u8"警告信息");
poco_fatal(logger(), u8"致命信息");
}
void testSubSystem()
{
getSubsystem<BackgroundSubSystem>().doSomething();
}
};
POCO_APP_MAIN(TheApp)