带有继承历史的CRTP插件自动注册(尝试使用SFINAE,但失败)
CRTP Plugin AutoRegistration with inheritance history (attempted but failed with SFINAE)
我已经为使用CRTP的插件系统构建了一个自动注册框架,它工作得很好。当我想与它有一个继承历史时,问题就出现了。我最初的计划是积累父id的向量作为类自动注册自己。我的尝试是在下面的代码中,因为它相当长,我把它放在问题的末尾。
InterfaceFactory和AutoRegister类通过CRTP工作得很好(即使AutoRegister功能被多次继承)。系统能够根据提供的id创建正确的类型,并且编译器在编译时构建所有注册功能。
我认为添加继承历史是在它们注册时添加继承,这看起来很简单。我计划在注册期间使用静态的"InterfaceType s_type"来获取INTERFACE类的信息并添加到当前。问题是原来的INTERFACE调用"BaseInterface"(和类似的)没有s_type。下一个想法是使用SFINAE来忽略层次结构中的那些类(因为它无论如何都没有id)。这导致了AutoRegister中的BaseId结构,它试图关闭s_type。然而,这似乎不起作用,因为从未使用过专门化。我假设Base::BaseId是使用非专门化的创建的,因为没有BaseInterface::s_type,并且当Derived被注册时(因为它是从Base派生的),编译器将拾取Base::BaseId,甚至不尝试专门化。我的问题是,这个假设成立吗?如果有的话,有人能解释一下吗,因为我对细节不太清楚,还有其他方法可以做到这一点吗?
#include <vector>
#include <functional>
template <typename T> struct TypeName
{
static std::string get()
{
std::string fullName=typeid(T).name();
size_t beginPos=0;
size_t classPos=fullName.find("class");
size_t nameSpacePos=fullName.find_last_of("::");
if(classPos != std::string::npos)
beginPos=classPos+6;
if(nameSpacePos != std::string::npos)
{
if(nameSpacePos+1 > beginPos)
beginPos=nameSpacePos+1;
}
return fullName.substr(beginPos);
}
};
class BaseInterface
{
public:
BaseInterface() {}
virtual ~BaseInterface() {}
};
struct InterfaceType
{
InterfaceType():id(-1){}
unsigned int id;
std::vector<unsigned int> idInheritance;
std::string name;
};
template<typename CLASS, typename INTERFACE>
class AutoRegister:public INTERFACE
{
public:
AutoRegister():INTERFACE() { &s_type; }
static BaseInterface *create(unsigned int type) { CLASS *newClass=new CLASS(); return newClass; }
template<typename T, typename=int>
struct BaseId
{
static std::vector<unsigned int> get() { return std::vector<unsigned int>(); }
};
template<typename T>
struct BaseId<T, decltype((void)T::s_type, 0)>
{
static std::vector<unsigned int> get()
{
std::vector<unsigned int> ids;
ids.push_back(T::s_type.id); ids.insert(ids.end(), T::s_type.idInheritance.begin(), T::s_type.idInheritance.end());
return ids;
}
};
static std::vector<unsigned int> getBaseIds() { return BaseId<INTERFACE>::get(); }
private:
static InterfaceType s_type;
};
class InterfaceFactory
{
public:
typedef BaseInterface *(*FactoryFunc)(unsigned int type);
class BaseInterfaceNode
{
public:
BaseInterfaceNode(unsigned int type, std::string typeName, FactoryFunc factoryFunction):
m_type(type), m_typeName(typeName), m_factoryFunction(factoryFunction)
{};
size_t type() { return m_type; }
std::string typeName() { return m_typeName; }
BaseInterface *factoryFunction() { return m_factoryFunction(m_type); }
private:
unsigned int m_type;
std::string m_typeName;
FactoryFunc m_factoryFunction;
};
typedef std::vector<BaseInterfaceNode> BaseInterfaceNodes;
public:
InterfaceFactory() {}
~InterfaceFactory() {}
public:
static BaseInterface *createType(unsigned int type);
static InterfaceType registerType(std::string typeName, std::vector<unsigned int> ids, FactoryFunc factoryFunc);
struct BaseInterfaceNodeHolder
{
BaseInterfaceNodeHolder():s_interfaceTypeIndex(0) {}
BaseInterfaceNodes s_baseInterfaceNodes;
unsigned int s_interfaceTypeIndex;
};
private:
};
template<typename CLASS, typename INTERFACE> InterfaceType AutoRegister<CLASS, INTERFACE>::s_type=
InterfaceFactory::registerType(TypeName<CLASS>::get(), AutoRegister<CLASS, INTERFACE>::getBaseIds(), &AutoRegister<CLASS, INTERFACE>::create);
InterfaceFactory::BaseInterfaceNodeHolder *s_nodes=nullptr;
InterfaceType InterfaceFactory::registerType(std::string typeName, std::vector<unsigned int> ids, FactoryFunc factoryFunc)
{
if(s_nodes == nullptr)
s_nodes=new BaseInterfaceNodeHolder();
InterfaceType sampleType;
sampleType.id=s_nodes->s_interfaceTypeIndex;
sampleType.idInheritance=ids;
sampleType.name=typeName;
s_nodes->s_baseInterfaceNodes.push_back(BaseInterfaceNode(s_nodes->s_interfaceTypeIndex, typeName, factoryFunc));
s_nodes->s_interfaceTypeIndex++;
return sampleType;
}
//////////////////////////////////////////////////////////////////////
class Base:public AutoRegister<Base, BaseInterface>
{
};
class Derived:public AutoRegister<Derived, Base>
{
};
int main(int argc, const char* argv[])
{
Base base;
Derived derived;
return 0;
}
谢谢,
我发现了我自己的错误,似乎SFINAE的风格要求我检查的成员变量是公共的,一旦我改变
private:
static InterfaceType s_type;
public:
static InterfaceType s_type;
解决了这个问题,至少对Clang(可能还有gcc)来说是这样。然而VS2013没有工作。在阅读了一些之后,似乎VS2013在一些高级模板方面还存在一些bug。大多数问题似乎已经解决,但尚未发布。最后,我不得不使用Johannes Schaub - litb提供的版本(如何检测类中是否存在特定的成员变量?)。我包含了一个has_s_type
template <typename T>
struct has_s_type
{
struct Fallback { InterfaceType s_type; };
struct Derived: T, Fallback {};
template<typename C, C> struct ChT;
template<typename C> static char(&f(ChT<InterfaceType Fallback::*, &C::s_type>*))[1];
template<typename C> static char(&f(...))[2];
static bool const value=sizeof(f<Derived>(0)) == 2;
};
和修改AutoRegister为
template<typename CLASS, typename INTERFACE>
class AutoRegister:public INTERFACE
{
public:
AutoRegister():INTERFACE() { &s_type; }
static BaseInterface *create(unsigned int type) { CLASS *newClass=new CLASS(); return newClass; }
template<typename T, bool=false>
struct BaseId
{
static std::vector<unsigned int> get() { return std::vector<unsigned int>(); }
};
template<typename T>
struct BaseId<T, true>
{
static std::vector<unsigned int> get()
{
std::vector<unsigned int> ids;
ids.push_back(INTERFACE::s_type.id); ids.insert(ids.end(), INTERFACE::s_type.idInheritance.begin(), INTERFACE::s_type.idInheritance.end());
return ids;
}
};
static std::vector<unsigned int> getBaseIds() { return BaseId<INTERFACE, has_s_type<INTERFACE>::value>::get(); }
public:
static const InterfaceType s_type;
};
这里是一些活动代码
- 如果没有malloc,链表实现将失败
- 模板参数替换失败,并且未完成隐式转换
- 构造函数SFINAE和继承在clang中失败
- C++ 带有 decltype 的 SFINAE:替换失败成为错误?
- SFINAE - 如果更复杂的功能失败,则回退到默认功能
- MSVC SFINAE:替代不会失败
- 在模板参数中评估 constexpr 时 SFINAE 失败
- SFINAE 序列化共享指针失败
- 使用ENABLE_IF和SFINAE时,功能参数类型扣除(std容器,例如向量)失败
- SFINAE使用演绎,但用替换失败
- Sfinae 类型特征会自动扣除失败
- 模板参数推导失败,SFINAE
- 类模板中的 typedef 的 SFINAE 失败是指另一个类模板中的 typedef
- 变量args SFINAE默认构造函数在clang中工作,但在Visual Studio 2015中失败
- is_container trait在std::set SFINAE问题上失败
- 是否有一种方法可以使用SFINAE来确定对模板化函数的调用是否会由于所提供的类型而失败
- 替换失败不是static_cast的错误(SFINAE)问题
- C++为什么 SFINAE 仅使用类模板参数会失败
- 在存在泛型构造函数的情况下,对三元运算符的SFINAE失败
- 带有继承历史的CRTP插件自动注册(尝试使用SFINAE,但失败)