没有宏的C ++配置方法

时间:2016-10-30 15:43:04

标签: c++ c++11 inheritance configuration abstract-class

我正在实现一个库,并使用具有一些不同实现的抽象类/接口。我想允许用户使用配置文件选择特定的实现,例如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();

1 个答案:

答案 0 :(得分:1)

首先,问问自己"我将如何选择配置?为什么?"

宏无处不在(你可以将它们定义为编译器标志和代码),但它们缺乏作用域功能(它们是全局的,不能,例如,定义为名称空间本地),因此它们容易发生名称冲突

如果您关心后者,可以使用typedef / 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> { };

...完全避免具有多个特化的样板代码。然后您将无法就地定义类,这就是您在示例中所做的事情,所以越过那个。