没有宏的 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++ 配置方法的主要内容,如果未能解决你的问题,请参考以下文章

为啥没有 C++ 的 DELETE 宏的原因

多项目配置 C++

C++ 宏的可选参数

配置 C++ 应用程序构建的简单方法

Zabbix监控redis配置及宏的使用

使用 Cmake 配置 c++ OpenCV 项目