我正在编写一个C ++应用程序,我想让开发人员在编译时选择用于特定问题的算法。这两种算法都是作为实现通用接口的C ++类实现的,并且是相互替代的替代品。它们都有.h和.cpp文件,并且驻留在子目录中(我们称之为impl/
)。
在我的Makefile中,我有类似的内容:
...
IMPL = default
...
binary: ... impl/$(IMPL).o
...
impl/%.o: impl/%.cpp impl-interface.h impl/%.h
...
%o: %.cpp ...
$(CXX) $(CXXFLAGS) -DIMPL=$(IMPL) -c -o $@ $*.cpp
我们的想法是用户应该可以输入make binary IMPL=thatimpl
。
在任何想要使用用户选择的算法的文件中,我都会这样做:
IImpl o = new IMPL();
但是,这要求我包含所选实现的头文件。不幸的是,C ++要求#include
后跟"string"
,<libfile>
。您也可以使用建议的here宏,但它要求宏的参数是文字字符串。如果我使用:
#define QUOTEME(M) #M
#define INCLUDE_FILE(M) QUOTEME(impl/##M##.h)
#include INCLUDE_FILE(IMPL)
编译器将尝试包含文字字符串impl/IMPL.h
,而不是将IMPL
扩展为传递给make
然后传递给编译器的任何内容。
关于如何实现这一目标的任何指示都非常受欢迎!
答案 0 :(得分:6)
由于预处理器的工作方式,您只需添加一个额外的间接层。这应该做:
#define QUOTEME(x) QUOTEME_1(x)
#define QUOTEME_1(x) #x
#define INCLUDE_FILE(x) QUOTEME(impl/x.h)
#include INCLUDE_FILE(IMPL)
答案 1 :(得分:0)
我看到树的方法来解决你的问题:
#ifdef
在我看来更清洁:如下:
#if !defined(IMPL_CHOICE)
# define IMPL_1 // default to 1, or
//#error "You have to define IMPL_CHOICE"
#endif
#if IMPL_CHOICE == 1
# include "impl1.h"
// other stuff
#elif IMPL_CHOICE == 2
# include "impl1.h"
// other stuff
//elif IMPL_CHOICE == 3 // And so on
#else
# error "invalid IMPL_CHOICE"
#endif
假设名称相同但位于不同的目录
假设树结构
src/common/*.{cpp,h}
/implementation_1/header.h
/implementation_2/header.h
/implementation_3/header.h
所以你的普通包含目录只是“src /”
所以你的#include看起来像是
#include "implementation_1/header.h"
现在,如果你还添加(条件)“src / implementation_1 /”,它就会变成
#include "header.h"
或者使用-include
(对于gcc),就像你对pch(预编译的头文件)所做的那样。
答案 2 :(得分:0)
您还可以使用Boost预处理器,尤其是BOOST_PP_STRINGIZE宏:
#include <boost/preprocessor/stringize.hpp>
#define INCLUDE_FILE(X) BOOST_PP_STRINGIZE(impl/X.h)
// expands to: #include "impl/IMPL.h" where IMPL is given by the developer
#include INCLUDE_FILE(IMPL)
直播示例here。