我在设计我的 C++ 库时遇到了问题。它是一个用于读取流的库,支持我在其他“流”实现中没有找到的功能。为什么我决定开始写它并不重要。关键是我有一个流类,它通过多重继承提供两个重要的行为:可共享性和可搜索性。
可共享流是具有 shareBlock(size_t length) 方法的流,该方法返回与其父流共享资源的新流(例如,使用父流使用的相同内存块)。可搜索的流是那些......好吧,可搜索的。通过方法 seek(),这些类可以搜索到流中的给定点。并非库的所有流都是可共享和/或可搜索的。
一个同时提供资源查找和共享实现的流类继承了称为 Seekable 和 Shareable 的接口类。如果我知道这样一个流的类型,那就太好了,但是,有时,我可能希望一个函数接受一个流作为参数,该流只满足同时可搜索和可共享的质量,而不管它实际上是哪个流类是。我可以这样做创建另一个继承 Seekable 和 Shareable 的类并引用该类型,但是我必须使我的类既可查找又可共享的类继承自该类。如果要添加更多类似的“行为类”,我将需要在代码的各处进行一些修改,很快就会导致无法维护的代码。有没有办法解决这个困境?如果没有,那我 我完全理解为什么人们对多重继承不满意。它几乎完成了这项工作,但是,就在那时,它没有:D
任何帮助表示赞赏。
-- 第二次编辑,首选问题解决方案 --
起初我认为Managu 的解决方案是我的首选。然而,Matthieu M.带来了另一个我比 Managu 更喜欢的:使用boost::enable_if<>
. BOOST_MPL_ASSERT
如果生成的消息不是那么令人毛骨悚然,我想使用 Managu 的解决方案。如果有任何方法可以创建有启发性的编译时错误消息,我肯定会这样做。但是,正如我所说,可用的方法会产生令人毛骨悚然的信息。boost::enable_if<>
因此,我更喜欢在不满足条件时产生的(更)指导性较低但更清晰的信息。
我创建了一些宏来简化编写模板函数的任务,这些模板函数接受继承选择类类型的参数,它们在这里:
// SonettoEnableIfDerivedMacros.h
#ifndef SONETTO_ENABLEIFDERIVEDMACROS_H
#define SONETTO_ENABLEIFDERIVEDMACROS_H
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/array/elem.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/and.hpp>
#include <boost/type_traits/is_base_and_derived.hpp>
#include <boost/utility/enable_if.hpp>
/*
For each (TemplateArgument,DerivedClassType) preprocessor tuple,
expand: `boost::is_base_and_derived<DerivedClassType,TemplateArgument>,'
*/
#define SONETTO_ENABLE_IF_DERIVED_EXPAND_CONDITION(z,n,data) \
boost::is_base_and_derived<BOOST_PP_TUPLE_ELEM(2,1,BOOST_PP_ARRAY_ELEM(n,data)), \
BOOST_PP_TUPLE_ELEM(2,0,BOOST_PP_ARRAY_ELEM(n,data))>,
/*
ReturnType: Return type of the function
DerivationsArray: Boost.Preprocessor array containing tuples in the form
(TemplateArgument,DerivedClassType) (see
SONETTO_ENABLE_IF_DERIVED_EXPAND_CONDITION)
Expands:
typename boost::enable_if<
boost::mpl::and_<
boost::is_base_and_derived<DerivedClassType,TemplateArgument>,
...
boost::mpl::bool_<true> // Used to nullify trailing comma
>, ReturnType>::type
*/
#define SONETTO_ENABLE_IF_DERIVED(ReturnType,DerivationsArray) \
typename boost::enable_if< \
boost::mpl::and_< \
BOOST_PP_REPEAT(BOOST_PP_ARRAY_SIZE(DerivationsArray), \
SONETTO_ENABLE_IF_DERIVED_EXPAND_CONDITION,DerivationsArray) \
boost::mpl::bool_<true> \
>, ReturnType>::type
#endif
// main.cpp: Usage example
#include <iostream>
#include "SonettoEnableIfDerivedMacros.h"
class BehaviourA
{
public:
void behaveLikeA() const { std::cout << "behaveLikeA()\n"; }
};
class BehaviourB
{
public:
void behaveLikeB() const { std::cout << "behaveLikeB()\n"; }
};
class BehaviourC
{
public:
void behaveLikeC() const { std::cout << "behaveLikeC()\n"; }
};
class CompoundBehaviourAB : public BehaviourA, public BehaviourB {};
class CompoundBehaviourAC : public BehaviourA, public BehaviourC {};
class SingleBehaviourA : public BehaviourA {};
template <class MustBeAB>
SONETTO_ENABLE_IF_DERIVED(void,(2,((MustBeAB,BehaviourA),(MustBeAB,BehaviourB))))
myFunction(MustBeAB &ab)
{
ab.behaveLikeA();
ab.behaveLikeB();
}
int main()
{
CompoundBehaviourAB ab;
CompoundBehaviourAC ac;
SingleBehaviourA a;
myFunction(ab); // Ok, prints `behaveLikeA()' and `behaveLikeB()'
myFunction(ac); // Fails with `error: no matching function for
// call to `myFunction(CompoundBehaviourAC&)''
myFunction(a); // Fails with `error: no matching function for
// call to `myFunction(SingleBehaviourA&)''
}
如您所见,错误消息非常干净(至少在 GCC 3.4.5 中)。但它们可能具有误导性。它不会通知您您传递了错误的参数类型。它通知您该功能不存在(事实上,它不是由于 SFINAE;但这可能对用户来说并不完全清楚)。尽管如此,我更喜欢那些干净的信息而不是那些randomStuff ... ************** garbage **************
BOOST_MPL_ASSERT
产品。
如果您在此代码中发现任何错误,请编辑并更正它们,或在这方面发表评论。我在这些宏中发现的一个主要问题是它们仅限于某些 Boost.Preprocessor 限制。例如,在这里,我只能将DerivationsArray
最多 4 个项目的 a 传递给SONETTO_ENABLE_IF_DERIVED()
. 我认为这些限制是可配置的,也许它们甚至会在即将到来的 C++1x 标准中被取消,不是吗?如果我错了,请纠正我。我不记得他们是否建议对预处理器进行更改。
谢谢你。