没有宏的 C++ 配置方法
Posted
技术标签:
【中文标题】没有宏的 C++ 配置方法【英文标题】:C++ approach to configuration without macro 【发布时间】:2016-10-30 15:43:04 【问题描述】:我正在实现一个库,并使用具有一些不同实现的抽象类/接口。我想允许用户使用配置文件选择特定的实现,例如config.h
。我有两个问题:
我正在寻找 c++11 的解决方案,但也欢迎 c++14 和 c++17。
举个例子:
#include "config.h"
class library1_base
virtual void blah() = 0;
virtual void bleh() = 0;
#ifdef LIB1_USE_IMPL1
class impl1_lib1 : library1_base ... ;
using library1 = impl1_lib;
#else
class impl2_lib1 : library1_base ... ;
using library1 = impl2_lib;
#endif
class library2_base
virtual void ah() = 0;
virtual void oh() = 0;
#ifdef LIB2_USE_IMPL1
class impl1_lib2 : library2_base ... ;
using library2 = impl1_lib2;
#else
class impl2_lib2 : library2_base ... ;
using library2 = impl2_lib2;
#endif
config.h
如下(使用宏)
#define LIB1_USE_IMPL1
#define LIB2_USE_IMPL2
他们如何使用:
library1 x; // now this library1 should be using impl1_lib1
x.blah();
x.bleh();
library2 y; // now this library2 should be using impl2_lib2
y.ah();
y.oh();
【问题讨论】:
downvoter.. 请发表评论,以便我改进我的问题。 您必须显示示例代码以显示您希望的目标。很有可能有人会举报您的帖子。 OK @NickPavini 添加了一个使用宏来做我想做的事的例子 你为什么不想使用宏? 【参考方案1】:首先,问问自己“我将如何选择配置,为什么?”
宏无处不在(您既可以将它们定义为编译器标志,也可以在代码中定义它们),但它们缺乏作用域能力(它们是全局的,例如不能定义为局部命名空间),因此它们容易发生名称冲突.
如果您关心的是后者,您可以使用 typedefs/constants 定义一个特殊的配置类,如下所示:
namespace myapp
struct Config
class library : library_base ...
using other_library = some_other_library;
using version_t = int;
static const version_t VERSION = 1;
但是要选择所需的配置,您必须包含多个 configXXX.h 文件之一,或者制作配置类模板并为每个配置提供多个专业化,如下所示:
enum class ConfigType
JPEG, PNG
;
template <ConfigType type> struct Configs;
template <> struct Configs<ConfigType::JPEG>
using library = libjpeg;
;
template <> struct Configs<ConfigType::PNG>
using library = libpng;
;
...
static const ConfigType CONFIG_TYPE = ConfigType::JPEG;
...
using Config = Configs<CONFIG_TYPE>;
using library = Config::library;
更新 #1: 在后面的示例中,组合似乎是最好的解决方案(我已经重命名了一些东西,所以它们更有意义):
// configs.hpp
enum class AudioType
MP3, OGG
;
template <AudioType type> struct AudioConfigs;
template <> struct AudioConfigs<AudioType::MP3>
using Library = libmp3; // of course, you can define class in-place instead of typedef
;
template <> struct AudioConfigs<AudioType::OGG>
using Library = libvorbis;
;
enum class ImageType
JPEG, PNG
;
template <ImageType type> struct ImageConfigs;
template <> struct ImageConfigs<ImageType::JPEG>
using Library = libjpeg;
;
template <> struct ImageConfigs<ImageType::PNG>
using Library = pnglib;
;
template <AudioType audiotype, ImageType imagetype> struct Configs
using AudioLibrary = typename AudioConfigs<audiotype>::Library;
using ImageLibrary = typename ImageConfigs<imagetype>::Library;
;
// config_setup.hpp - this one you will edit
#include "configs.hpp"
using Config = Configs<AudioType::MP3, ImageType::JPEG>;
// config.hpp
#include "config_setup.hpp"
using AudioLibrary = typename Config::AudioLibrary;
using ImageLibrary = typename Config::ImageLibrary;
// real code
#include "config.hpp"
AudioLibrary audioLibrary;
audioLibrary.doStuff();
替代结构:
template <AudioType audiotype, ImageType imagetype> struct Configs
using Audio = typename AudioConfigs<audiotype>;
using Image = typename ImageConfigs<imagetype>;
;
// Options should be accessed like Config::Audio::Library now
更新 #2: (只是为了说明无限的可能性)借助一些可变参数模板魔术,您可以缩短 AudioConfigs/ImageConfigs/etc。到:
template <AudioConfig config> struct AudioConfigs : public LibraryConfigs<AudioConfig, config, AudioType::MP3, AudioType::OGG, libmp3, libvorbis> ;
...完全避免具有多种专业化的样板代码。然后,您将失去就地定义类的可能性,这就是您在示例中所做的,因此请跨越。
【讨论】:
这种方法无法扩展,因为我必须为所有可能的配置指定专业化,例如如果我有超过 1 个可配置库怎么办? 其实它是你能想象到的最方便/灵活/可扩展的方法,因为,你可以有多个配置类,然后你可以继承、组合、类型定义、特化它们中的任何一个来形成任何可以想象的配置结构。如果您需要更深入的解释,请提供一些示例,说明您将如何使用宏以及您愿意克服哪些镜头。 感谢 Dmitry 的帮助,我添加了两个库的示例,您能否调整一下您的想法。 我明白了.. 没想过使用这样的组合:)。会等待一段时间接受,以防我发现一些不那么冗长的东西。以上是关于没有宏的 C++ 配置方法的主要内容,如果未能解决你的问题,请参考以下文章