我正在实现一个库,并使用具有一些不同实现的抽象类/接口。我想允许用户使用配置文件选择特定的实现,例如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();
答案 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> { };
...完全避免具有多个特化的样板代码。然后您将无法就地定义类,这就是您在示例中所做的事情,所以越过那个。