2013-11-21 59 views
0

我有一个相当简单的设置围绕着两个基本结构为中心的双向链表佣工:凝重类型安全

typedef struct DoubleLinkedListNode_struct { 
    struct DoubleLinkedListNode_struct *m_prev; 
    struct DoubleLinkedListNode_struct *m_next; 
} DoubleLinkedListNode; 

typedef struct DoubleLinkedList_struct { 
    DoubleLinkedListNode m_anchor; 
} DoubleLinkedList; 

这些原始片被用来构建侵入链表在一大堆结构和(现在)类。我试图用一整套警告和要求来重新修改这组结构和支持功能。如果你想让背景读取'------'中断下面的部分。

我第一次尝试使用旧的节点结构提供了一种新类型安全的List模板:

template<typename Type, DoubleLinkedListNode Type::*MP> 
struct NewDoubleLinkedList 
{ 
    DoubleLinkedListNode m_anchor; 
    //static const int Offset = ((unsigned int)(&(((Type *)0)->*MP))); ///<- broken 

    void pushBack(Type *inst) { DoubleLinkedList_pushBack(this,&(inst->*MP)); } 
    Type *begin() { return GetObjectFromMember(Type,*MP,m_anchor.m_next); } 
    Type *end() { return GetObjectFromMember(Type,*MP,&m_anchor); } 
    Type *getNext(Type *from) { return GetObjectFromMember(Type,*MP,from->*MP.m_next); } 
}; 

而这个模板使用这样的:

struct MyClient { 
    DoubleLinkedListNode m_serviceNode; 
}; 

void testNewList() 
{ 
    NewDoubleLinkedList<MyClient,&MyClient::m_serviceNode> newList; 
    MyClient testClient; 

    newList.pushBack(&testClient); 

    MyClient *client = newList.begin(); 
    while(client != newList.end()) { 
     ASSERT(client == &testClient); 
     client = newList.getNext(client); 
    } 

    DoubleLinkedListNode_remove(&testClient.m_serviceNode); 
} 

这一切似乎工作,并投诉正确,除了此代码:

static const int Offset = ((unsigned int)(&(((Type *)0)->*MP))); 

这意味着失败(在编译时)只有在实例 - > * MP不能在编译时解析(即它依赖于实例的虚拟表,由于虚拟继承)。

有一些办法可以解决这个问题的代码,或以防止虚拟继承潜在的混乱一些替代的方法是什么?

如果你认为自己在完全错误的道路上,我已经包含了(过长)背景以及我正在做的和我的要求)。否则,只是到此为止


首先,我要强调,这是现有的C语言编写的代码,它用于所有的地方,所以我需要一种方法,它允许慢摇出,而不必每一段代码重写一次使用列表结构。

典型的使用情况下,通常是这样的:

struct MyService { 
    ... //other backend service data 
    DoubleLinkedList m_clientList; 
} 

struct MyClient { 
    ... //other client service data 
    MyService *m_serviceProvider; 
    DoubleLinkedListNode m_serviceNode; 
    DoubleLinkedListNode m_wrongServiceNode; 
    void (*v_doSomethingLater)(MyClient *); //"virtual" function 
} 

void Client_requestService(MyClient *client) 
{ 
    ... //prep work for service request 
    DoubleLinkedList_pushBack(&client->m_serviceProvider.m_clientList, 
           &client->m_serviceNode); 
} 

void Service_handleClients(MyService *service) 
{ 
    DoubleLinkedListNode *iter = DoubleLinkedList_begin(&service->m_clientList); 
    DoubleLinkedListNode *end = DoubleLinkedList_end(&service->m_clientList); 
    while(iter != end) { 
     MyClient *client = GetObjectFromMember(MyClient, m_serviceNode, iter); 
     iter = DoubleLinkedListNode_getNext(iter); 
     client->v_doSomethingLater(client); 
    } 
} 

的(超级邪恶和绝对无处不在)宏GetObjectFromMember需要(类型名,成员名称,memberPointer),并返回一个类型化的指针这样的:

TypeName *result = GetObjectFromMember(TypeName,memberName,memberPointer); 
    ASSERT(&result->memberName == memberPointer); 

对于真正的受虐狂它看起来像这样:

#define GetObjectFromMember(ObjectType,MemberName,MemberPointer) \ 
       ((ObjectType *)(((char *)MemberPointer) - ((char *)(&(((ObjectType *)0)->MemberName))))) 

我的目标是要找到侵入性最小的方式来写一些模板,可以添加一个类型安全在该代码最容易出现斑点错误:

DoubleLinkedList_pushBack(&client->m_serviceProvider.m_clientList, 
           &client->m_serviceNode); 

如果有人可能会不小心使用了错误的节点,就像这样:

DoubleLinkedList_pushBack(&client->m_serviceProvider.m_clientList, 
           &client->m_wrongServiceNode); 

它编译干净,并在回调阶段,我们做导致了一场灾难:

因为由GetObjectFro得到的指针毫米将是错误的。

的另一个主要问题是,由于我们现在正在做C++,GetObjectFromMember只能如果类型名没有通过虚拟继承到达成员名称。我需要我拿出任何解决在编译时失败,如果GetObjectFromMember是不是安全。

所以,目标是:

邻>,允许继续使用现有的DoubleLinkedListNode类型名的
邻>,允许继续使用现有的DoubleLinkedList类型名的如果可能的话(我怀疑这ISN”牛逼可能)
邻>,允许继续使用现有的宏(DoubleLinkedListNode_pushBack和(许多)其他)。
邻>为它打破了GetObjectFromMember使用
邻>用途,其中这种情况下,虚拟继承编译时错误:

DoubleLinkedList_pushBack(&client->m_serviceProvider.m_clientList, 
          &client->m_serviceNode); 

    - >可以自由地与该更换:

client->m_serviceProvider.m_clientList.pushBack(client); 

o->其中Service_handleClients调用中使用的情况下被重新设计,看起来像:

void Service_handleClients(MyFabulousService *service) 
{ 
    MyClient *client = service->m_clientList.begin(); 
    while(client != service->m_clientList.end()) { 
     MyClient *nextClient = service->m_clientList.getNext(client); 
     client->v_doSomethingLater(client); 
     client = nextClient; 
    } 
} 

邻>任何种类的动态分配。
邻 - >不显著(量级)放大(存储器)或更慢(CPU)比现有的实现。

回答

1

事实1:表达式&Type::member具有类型MemberType ClassType::*,其中ClassType是其中member被声明,不一定Type的类。事实2:只有当基类不是虚拟基类(也不含糊不清的基类)时,从基类到派生类的引用或指针才是合法的(虽然并非总是安全的)static_cast。在我看来,这正是您在每次使用GetObjectFromMember时要检查的内容。

因此,如何:

// Precondition: mptr points at the specified member of a ClassType object. 
// Precondition: member must not be in a virtual base class of ClassType. 
// 
// The second precondition is not an issue if the template arguments are 
// deduced from an &AnyClass::member expression, since ClassType will 
// deduce as the member's actual enclosing class. 
template<typename ClassType, typename MemberType> 
ClassType* ObjectContainingMember(MemberType ClassType::*member, 
            MemberType *mptr) 
{ 
    ClassType* dummy = 0; 
    std::size_t member_offset = 
     reinterpret_cast<char*>(&(dummy->*member)) - 
     reinterpret_cast<char*>(dummy); 
    char* obj_addr = 
     reinterpret_cast<char*>(mptr) - member_offset; 
    return reinterpret_cast<ClassType*>(obj_addr); 
} 

// Precondition: MemberPointer points at the specified member of 
// an ObjectType object. Returns a pointer to that ObjectType. 
#define GetObjectFromMember(ObjectType,MemberName,MemberPointer) \ 
    static_cast<ObjectType*>(ObjectContainingMember(    \ 
     &ObjectType::MemberName, MemberPointer)) 

因为事实上1,模板参数ClassType会推导出在声明该成员的类。

+0

这看起来像我所需要的,但大多数编译器不支持decltype。对于那些编译器来说,它似乎仍然是正确的解决方案。让我调查类似的替代方案,我会尽快回复您。 – Speed8ump

+0

这绝对可行。最后我做的,而不是decltype模板类型推断:模板 直列类型名称RequiredType * memberStaticCastTo(MemberType BASETYPE :: * memberPointer, MemberType * memberInstance) – Speed8ump

+0

是啊,我只是工作如何在C++ 03中完成它,实际上看起来比我发布的更简单。 – aschepler