带有继承历史的CRTP插件自动注册(尝试使用SFINAE,但失败)

CRTP Plugin AutoRegistration with inheritance history (attempted but failed with SFINAE)

本文关键字:SFINAE 失败 注册 历史 继承 CRTP 插件      更新时间:2023-10-16

我已经为使用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;
};

这里是一些活动代码